File Coverage

blib/lib/Object/Container.pm
Criterion Covered Total %
statement 147 157 93.6
branch 53 68 77.9
condition 22 42 52.3
subroutine 28 31 90.3
pod 9 11 81.8
total 259 309 83.8


line stmt bran cond sub pod time code
1             package Object::Container;
2              
3 13     13   798205 use strict;
  13         105  
  13         303  
4 13     13   60 use warnings;
  13         19  
  13         290  
5 13     13   4832 use parent qw(Class::Accessor::Fast);
  13         3399  
  13         54  
6 13     13   32368 use Carp;
  13         23  
  13         1348  
7              
8             our $VERSION = '0.15';
9              
10             __PACKAGE__->mk_accessors(qw/registered_classes autoloader_rules objects/);
11              
12             BEGIN {
13 13     13   39 our $_HAVE_EAC = 1;
14 13         19 eval { local $SIG{__DIE__}; require Exporter::AutoClean; };
  13         40  
  13         3966  
15 13 100       98741 if ($@) {
16 1         46 $_HAVE_EAC = 0;
17             }
18             }
19              
20             do {
21             my @EXPORTS;
22              
23             sub import {
24 17     17   442 my ($class, $name) = @_;
25 17 100       4114 return unless $name;
26              
27 8         16 my $caller = caller;
28             {
29 13     13   90 no strict 'refs';
  13         24  
  13         2636  
  8         10  
30 8 100       33 if ($name =~ /^-base$/i) {
31 4         17 push @{"${caller}::ISA"}, $class;
  4         33  
32 4         23 my $r = $class->can('register');
33 4         11 my $l = $class->can('autoloader');
34            
35             my %exports = (
36 8     8   260 register => sub { $r->($caller, @_) },
37 0     0   0 autoloader => sub { $l->($caller, @_) },
38             preload => sub {
39 0     0   0 $caller->instance->get($_) for @_;
40             },
41             preload_all_except => sub {
42 1     1   4 $caller->instance->load_all_except(@_);
43             },
44             preload_all => sub {
45 0     0   0 $caller->instance->load_all;
46             },
47 4         38 );
48            
49 4 100       12 if ($Object::Container::_HAVE_EAC) {
50 2         10 Exporter::AutoClean->export( $caller, %exports );
51             }
52             else {
53 2         7 while (my ($name, $fn) = each %exports) {
54 10         11 *{"${caller}::${name}"} = $fn;
  10         52  
55             }
56 2         167 @EXPORTS = keys %exports;
57             }
58             }
59             else {
60 13     13   75 no strict 'refs';
  13         21  
  13         1277  
61 4         1241 *{"${caller}::${name}"} = sub {
62 11     11   1339 my ($target) = @_;
63 11 100       71 return $target ? $class->get($target) : $class;
64 4         11 };
65             }
66             }
67             }
68              
69             sub unimport {
70 1     1   22 my $caller = caller;
71              
72 13     13   71 no strict 'refs';
  13         21  
  13         13765  
73 1         3 for my $name (@EXPORTS) {
74 5         5 delete ${ $caller . '::' }{ $name };
  5         17  
75             }
76              
77 1         22 1; # for EOF
78             }
79             };
80              
81             my %INSTANCES;
82             sub instance {
83 24     24 1 73 my $class = shift;
84 24   66     92 return $INSTANCES{$class} ||= $class->new;
85             }
86              
87             sub has_instance {
88 2     2 0 1028 my $class = shift;
89 2   33     9 $class = ref $class || $class;
90 2         15 return $INSTANCES{$class};
91             };
92              
93             sub new {
94 17     17 1 5848 $_[0]->SUPER::new( +{
95             registered_classes => +{},
96             autoloader_rules => +[],
97             objects => +{},
98             } );
99             }
100              
101             sub register {
102 22     22 1 2379 my ($self, $args, @rest) = @_;
103 22 100       121 $self = $self->instance unless ref $self;
104              
105 22         152 my ($class, $initializer, $is_preload);
106 22 100 66     114 if (defined $args && !ref $args) {
    50          
107 19         32 $class = $args;
108 19 100 66     68 if (@rest == 1 and ref $rest[0] eq 'CODE') {
109 6         12 $initializer = $rest[0];
110             }
111             else {
112             $initializer = sub {
113 11     11   62 $self->ensure_class_loaded($class);
114 11         47 $class->new(@rest);
115 13         48 };
116             }
117             }
118             elsif (ref $args eq 'HASH') {
119 3         7 $class = $args->{class};
120 3 100 66     11 if (exists $args->{initializer} && ref $args->{initializer} eq 'CODE') {
121 2         3 $initializer = $args->{initializer};
122             }
123             else {
124             $initializer = sub {
125 1     1   4 $self->ensure_class_loaded($class);
126 1 50       1 $class->new(@{exists $args->{args} ? $args->{args} : []});
  1         7  
127 1         4 };
128             }
129              
130 3 100       7 $is_preload = 1 if $args->{preload};
131             }
132             else {
133 0         0 croak "Usage: $self->register($class || { class => $class ... })";
134             }
135              
136 22         460 $self->registered_classes->{$class} = $initializer;
137 22 100       154 $self->get($class) if $is_preload;
138            
139 22         60 return $initializer;
140             }
141              
142             sub unregister {
143 1     1 1 3 my ($self, $class) = @_;
144 1 50       6 $self = $self->instance unless ref $self;
145              
146 1 50       21 delete $self->registered_classes->{$class} and $self->remove($class);
147             }
148              
149             sub autoloader {
150 1     1 0 21 my ($self, $rule, $trigger) = @_;
151 1 50       11 $self = $self->instance unless ref $self;
152              
153 1         5 push @{ $self->autoloader_rules }, [$rule, $trigger];
  1         20  
154             }
155              
156             sub get {
157 32     32 1 1200 my ($self, $class) = @_;
158 32 100       85 $self = $self->instance unless ref $self;
159              
160 32   100     625 my $obj = $self->objects->{ $class } ||= do {
161 21         418 my $initializer = $self->registered_classes->{ $class };
162 21 100       126 $initializer ? $initializer->($self) : ();
163             };
164              
165 32 100       397 unless ($obj) {
166             # autoloaderer
167 3 100       6 if (my ($trigger) = grep { $class =~ /$_->[0]/ } @{ $self->autoloader_rules }) {
  1         12  
  3         44  
168 1         3 $trigger->[1]->($self, $class);
169             }
170              
171 3   66     56 $obj = $self->objects->{ $class } ||= do {
172 3         56 my $initializer = $self->registered_classes->{ $class };
173 3 100       21 $initializer ? $initializer->($self) : ();
174             };
175             }
176            
177 32 100       231 $obj or croak qq["$class" is not registered in @{[ ref $self ]}];
  2         345  
178             }
179              
180             sub remove {
181 2     2 1 14 my ($self, $class) = @_;
182 2 50       4 $self = $self->instance unless ref $self;
183 2         31 delete $self->objects->{ $class };
184             }
185              
186             sub load_all {
187 1     1 1 700 my ($self) = @_;
188 1         3 $self->load_all_except;
189             }
190              
191             sub load_all_except {
192 3     3 1 595 my ($self, @except) = @_;
193 3 50       10 $self = $self->instance unless ref $self;
194              
195 3         4 for my $class (keys %{ $self->registered_classes }) {
  3         55  
196 6 100       27 next if grep { $class eq $_ } @except;
  4         15  
197 4         11 $self->get($class);
198             }
199             }
200              
201             # taken from Mouse
202             sub _is_class_loaded {
203 12     12   20 my $class = shift;
204              
205 12 50 33     83 return 0 if ref($class) || !defined($class) || !length($class);
      33        
206              
207             # walk the symbol table tree to avoid autovififying
208             # \*{${main::}{"Foo::"}{"Bar::"}} == \*main::Foo::Bar::
209              
210 12         37 my $pack = \%::;
211 12         43 foreach my $part (split('::', $class)) {
212 14         36 $part .= '::';
213 14 100       44 return 0 if !exists $pack->{$part};
214              
215 11         20 my $entry = \$pack->{$part};
216 11 50       30 return 0 if ref($entry) ne 'GLOB';
217 11         17 $pack = *{$entry}{HASH};
  11         27  
218             }
219              
220 9 50       14 return 0 if !%{$pack};
  9         21  
221              
222             # check for $VERSION or @ISA
223             return 1 if exists $pack->{VERSION}
224 9 50 66     42 && defined *{$pack->{VERSION}}{SCALAR} && defined ${ $pack->{VERSION} };
  3   66     16  
  3         27  
225             return 1 if exists $pack->{ISA}
226 6 50 33     24 && defined *{$pack->{ISA}}{ARRAY} && @{ $pack->{ISA} } != 0;
  6   33     33  
  6         47  
227              
228             # check for any method
229 0         0 foreach my $name( keys %{$pack} ) {
  0         0  
230 0         0 my $entry = \$pack->{$name};
231 0 0 0     0 return 1 if ref($entry) ne 'GLOB' || defined *{$entry}{CODE};
  0         0  
232             }
233              
234             # fail
235 0         0 return 0;
236             }
237              
238              
239             sub _try_load_one_class {
240 12     12   23 my $class = shift;
241              
242 12 100       31 return '' if _is_class_loaded($class);
243 3         12 my $klass = $class;
244 3         8 $klass =~ s{::}{/}g;
245 3         5 $klass .= '.pm';
246              
247 3         5 return do {
248 3         3 local $@;
249 3         5 eval { require $klass };
  3         1295  
250 3         24836 $@;
251             };
252             }
253              
254             sub ensure_class_loaded {
255 12     12 1 46 my ($self, $class) = @_;
256 12         29 my $e = _try_load_one_class($class);
257 12 50       42 Carp::confess "Could not load class ($class) because : $e" if $e;
258              
259 12         20 return $class;
260             }
261              
262             1;
263             __END__