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   777046 use strict;
  13         92  
  13         299  
4 13     13   56 use warnings;
  13         20  
  13         281  
5 13     13   4704 use parent qw(Class::Accessor::Fast);
  13         3344  
  13         56  
6 13     13   32195 use Carp;
  13         25  
  13         1223  
7              
8             our $VERSION = '0.16';
9              
10             __PACKAGE__->mk_accessors(qw/registered_classes autoloader_rules objects/);
11              
12             BEGIN {
13 13     13   38 our $_HAVE_EAC = 1;
14 13         22 eval { local $SIG{__DIE__}; require Exporter::AutoClean; };
  13         123  
  13         3939  
15 13 100       97370 if ($@) {
16 1         48 $_HAVE_EAC = 0;
17             }
18             }
19              
20             do {
21             my @EXPORTS;
22              
23             sub import {
24 17     17   468 my ($class, $name) = @_;
25 17 100       3883 return unless $name;
26              
27 8         16 my $caller = caller;
28             {
29 13     13   71 no strict 'refs';
  13         26  
  13         2598  
  8         31  
30 8 100       42 if ($name =~ /^-base$/i) {
31 4         18 push @{"${caller}::ISA"}, $class;
  4         41  
32 4         24 my $r = $class->can('register');
33 4         13 my $l = $class->can('autoloader');
34            
35             my %exports = (
36 8     8   303 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   5 $caller->instance->load_all_except(@_);
43             },
44             preload_all => sub {
45 0     0   0 $caller->instance->load_all;
46             },
47 4         43 );
48            
49 4 100       11 if ($Object::Container::_HAVE_EAC) {
50 2         14 Exporter::AutoClean->export( $caller, %exports );
51             }
52             else {
53 2         8 while (my ($name, $fn) = each %exports) {
54 10         11 *{"${caller}::${name}"} = $fn;
  10         52  
55             }
56 2         132 @EXPORTS = keys %exports;
57             }
58             }
59             else {
60 13     13   74 no strict 'refs';
  13         24  
  13         1299  
61 4         1221 *{"${caller}::${name}"} = sub {
62 11     11   1405 my ($target) = @_;
63 11 100       68 return $target ? $class->get($target) : $class;
64 4         12 };
65             }
66             }
67             }
68              
69             sub unimport {
70 1     1   27 my $caller = caller;
71              
72 13     13   73 no strict 'refs';
  13         18  
  13         13951  
73 1         3 for my $name (@EXPORTS) {
74 5         7 delete ${ $caller . '::' }{ $name };
  5         15  
75             }
76              
77 1         23 1; # for EOF
78             }
79             };
80              
81             my %INSTANCES;
82             sub instance {
83 24     24 1 54 my $class = shift;
84 24   66     99 return $INSTANCES{$class} ||= $class->new;
85             }
86              
87             sub has_instance {
88 2     2 0 1060 my $class = shift;
89 2   33     10 $class = ref $class || $class;
90 2         16 return $INSTANCES{$class};
91             };
92              
93             sub new {
94 17     17 1 5705 $_[0]->SUPER::new( +{
95             registered_classes => +{},
96             autoloader_rules => +[],
97             objects => +{},
98             } );
99             }
100              
101             sub register {
102 22     22 1 2254 my ($self, $args, @rest) = @_;
103 22 100       129 $self = $self->instance unless ref $self;
104              
105 22         137 my ($class, $initializer, $is_preload);
106 22 100 66     109 if (defined $args && !ref $args) {
    50          
107 19         29 $class = $args;
108 19 100 66     67 if (@rest == 1 and ref $rest[0] eq 'CODE') {
109 6         9 $initializer = $rest[0];
110             }
111             else {
112             $initializer = sub {
113 11     11   52 $self->ensure_class_loaded($class);
114 11         56 $class->new(@rest);
115 13         48 };
116             }
117             }
118             elsif (ref $args eq 'HASH') {
119 3         5 $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   12 $self->ensure_class_loaded($class);
126 1 50       2 $class->new(@{exists $args->{args} ? $args->{args} : []});
  1         5  
127 1         5 };
128             }
129              
130 3 100       6 $is_preload = 1 if $args->{preload};
131             }
132             else {
133 0         0 croak "Usage: $self->register($class || { class => $class ... })";
134             }
135              
136 22         447 $self->registered_classes->{$class} = $initializer;
137 22 100       152 $self->get($class) if $is_preload;
138            
139 22         55 return $initializer;
140             }
141              
142             sub unregister {
143 1     1 1 3 my ($self, $class) = @_;
144 1 50       5 $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 18 my ($self, $rule, $trigger) = @_;
151 1 50       10 $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 1623 my ($self, $class) = @_;
158 32 100       92 $self = $self->instance unless ref $self;
159              
160 32   100     598 my $obj = $self->objects->{ $class } ||= do {
161 21         429 my $initializer = $self->registered_classes->{ $class };
162 21 100       117 $initializer ? $initializer->($self) : ();
163             };
164              
165 32 100       389 unless ($obj) {
166             # autoloaderer
167 3 100       4 if (my ($trigger) = grep { $class =~ /$_->[0]/ } @{ $self->autoloader_rules }) {
  1         11  
  3         45  
168 1         4 $trigger->[1]->($self, $class);
169             }
170              
171 3   66     57 $obj = $self->objects->{ $class } ||= do {
172 3         55 my $initializer = $self->registered_classes->{ $class };
173 3 100       19 $initializer ? $initializer->($self) : ();
174             };
175             }
176            
177 32 100       195 $obj or croak qq["$class" is not registered in @{[ ref $self ]}];
  2         320  
178             }
179              
180             sub remove {
181 2     2 1 14 my ($self, $class) = @_;
182 2 50       6 $self = $self->instance unless ref $self;
183 2         30 delete $self->objects->{ $class };
184             }
185              
186             sub load_all {
187 1     1 1 713 my ($self) = @_;
188 1         2 $self->load_all_except;
189             }
190              
191             sub load_all_except {
192 3     3 1 620 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         56  
196 6 100       28 next if grep { $class eq $_ } @except;
  4         16  
197 4         10 $self->get($class);
198             }
199             }
200              
201             # taken from Mouse
202             sub _is_class_loaded {
203 12     12   21 my $class = shift;
204              
205 12 50 33     80 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         32 my $pack = \%::;
211 12         45 foreach my $part (split('::', $class)) {
212 14         26 $part .= '::';
213 14 100       48 return 0 if !exists $pack->{$part};
214              
215 11         18 my $entry = \$pack->{$part};
216 11 50       32 return 0 if ref($entry) ne 'GLOB';
217 11         14 $pack = *{$entry}{HASH};
  11         31  
218             }
219              
220 9 50       13 return 0 if !%{$pack};
  9         28  
221              
222             # check for $VERSION or @ISA
223             return 1 if exists $pack->{VERSION}
224 9 50 66     38 && defined *{$pack->{VERSION}}{SCALAR} && defined ${ $pack->{VERSION} };
  3   66     17  
  3         29  
225             return 1 if exists $pack->{ISA}
226 6 50 33     19 && defined *{$pack->{ISA}}{ARRAY} && @{ $pack->{ISA} } != 0;
  6   33     37  
  6         39  
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   26 my $class = shift;
241              
242 12 100       26 return '' if _is_class_loaded($class);
243 3         13 my $klass = $class;
244 3         7 $klass =~ s{::}{/}g;
245 3         7 $klass .= '.pm';
246              
247 3         3 return do {
248 3         4 local $@;
249 3         5 eval { require $klass };
  3         1181  
250 3         24267 $@;
251             };
252             }
253              
254             sub ensure_class_loaded {
255 12     12 1 37 my ($self, $class) = @_;
256 12         36 my $e = _try_load_one_class($class);
257 12 50       39 Carp::confess "Could not load class ($class) because : $e" if $e;
258              
259 12         22 return $class;
260             }
261              
262             1;
263             __END__