File Coverage

blib/lib/Object/Container.pm
Criterion Covered Total %
statement 148 158 93.6
branch 52 66 78.7
condition 22 41 53.6
subroutine 28 31 90.3
pod 9 11 81.8
total 259 307 84.3


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