File Coverage

blib/lib/IOC/Container.pm
Criterion Covered Total %
statement 148 148 100.0
branch 58 60 96.6
condition 21 23 91.3
subroutine 34 34 100.0
pod 22 22 100.0
total 283 287 98.6


line stmt bran cond sub pod time code
1              
2             package IOC::Container;
3              
4 22     22   321579 use strict;
  22         50  
  22         888  
5 22     22   266 use warnings;
  22         40  
  22         1108  
6              
7             our $VERSION = '0.14';
8              
9 22     22   156 use Scalar::Util qw(blessed);
  22         45  
  22         3210  
10              
11 22     22   11772 use IOC::Interfaces;
  22         60  
  22         621  
12 22     22   3996 use IOC::Exceptions;
  22         60  
  22         770  
13              
14 22     22   22074 use IOC::Visitor::ServiceLocator;
  22         154  
  22         706  
15              
16 22     22   156 use base 'IOC::Visitable';
  22         41  
  22         68452  
17              
18             sub new {
19 90     90 1 37747 my ($_class, $name) = @_;
20 90   33     529 my $class = ref($_class) || $_class;
21 90         180 my $container = {};
22 90         229 bless($container, $class);
23 90         362 $container->_init($name);
24 90         364 return $container;
25             }
26              
27             sub _init {
28 90     90   222 my ($self, $name) = @_;
29 90         404 $self->{services} = {};
30 90         247 $self->{service_locks} = {};
31 90         228 $self->{proxies} = {};
32 90         300 $self->{sub_containers} = {};
33 90         630 $self->{parent_container} = undef;
34 90   100     587 $self->{name} = $name || 'default';
35             }
36              
37             sub name {
38 68     68 1 4882 my ($self) = @_;
39 68         348 return $self->{name};
40             }
41              
42             # parent containers
43              
44             sub setParentContainer {
45 32     32 1 321 my ($self, $parent_container) = @_;
46 32 100 100     425 (blessed($parent_container) && $parent_container->isa('IOC::Container'))
47             || throw IOC::InsufficientArguments "You must provide an IOC::Container object as a parent container";
48 28         68 $self->{parent_container} = $parent_container;
49             }
50              
51             sub getParentContainer {
52 9     9 1 22 my ($self) = @_;
53 9         46 return $self->{parent_container};
54             }
55              
56             sub isRootContainer {
57 30     30 1 48 my ($self) = @_;
58 30 100       221 return defined($self->{parent_container}) ? 0 : 1;
59             }
60              
61             sub findRootContainer {
62 13     13 1 27 my ($self) = @_;
63 13 100       52 return $self if $self->isRootContainer();
64 4         7 my $current = $self;
65 4         10 $current = $current->getParentContainer() until $current->isRootContainer();
66 4         11 return $current;
67             }
68              
69             # sub containers
70              
71             sub addSubContainer {
72 33     33 1 239 my ($self, $container) = @_;
73 33 100 100     409 (blessed($container) && $container->isa('IOC::Container'))
74             || throw IOC::InsufficientArguments "You must provide an IOC::Container object as a sub-container";
75 29         93 my $name = $container->name();
76 29 100       43 (!exists ${$self->{sub_containers}}{$name})
  29         122  
77             || throw IOC::ContainerAlreadyExists "Duplicate Sub-Container Name '${name}' in container '" . $self->{name} . "'";
78 28         110 $self->{sub_containers}->{$name} = $container;
79 28         90 $container->setParentContainer($self);
80 28         84 $self;
81             }
82              
83             sub addSubContainers {
84 7     7 1 53 my ($self, @containers) = @_;
85 7 100       31 (@containers) || throw IOC::InsufficientArguments "You must provide at least one IOC::Container to add";
86 6         27 $self->addSubContainer($_) foreach @containers;
87 6         23 $self;
88             }
89              
90             sub hasSubContainer {
91 12     12 1 356 my ($self, $name) = @_;
92 12 100       41 (defined($name)) || throw IOC::InsufficientArguments "You must supply a name of a sub-container";
93 11 100       12 return (exists ${$self->{sub_containers}}{$name}) ? 1 : 0;
  11         63  
94             }
95              
96             sub hasSubContainers {
97 35     35 1 5860 my ($self) = @_;
98 35 100       32 return scalar(keys(%{$self->{sub_containers}})) ? 1 : 0;
  35         191  
99             }
100              
101             sub getSubContainerList {
102 11     11 1 892 my ($self) = @_;
103 11         17 return keys %{$self->{sub_containers}};
  11         72  
104             }
105              
106             sub getSubContainer {
107 49     49 1 2190 my ($self, $name) = @_;
108 49 100       131 (defined($name)) || throw IOC::InsufficientArguments "You must supply a name of a sub-container";
109 48 100       80 (exists ${$self->{sub_containers}}{$name})
  48         323  
110             || throw IOC::ContainerNotFound "There is no subcontainer by the name '${name}' in container '" . $self->{name} . "'";
111 45         182 return $self->{sub_containers}->{$name};
112             }
113              
114             sub getAllSubContainers {
115 9     9 1 12 my ($self) = @_;
116 9         10 return values %{$self->{sub_containers}};
  9         43  
117             }
118              
119             sub accept {
120 54     54 1 13548 my ($self, $visitor) = @_;
121 54 100 100     552 (blessed($visitor) && $visitor->isa('IOC::Visitor'))
122             || throw IOC::InsufficientArguments "You must pass an IOC::Visitor object to the visit method";
123 50         193 return $visitor->visit($self);
124             }
125              
126             # services
127              
128             sub register {
129 101     101 1 5585 my ($self, $service) = @_;
130 101 100 100     5140 (blessed($service) && $service->isa('IOC::Service'))
131             || throw IOC::InsufficientArguments "You must provide a valid IOC::Service object to register";
132 97         429 my $name = $service->name();
133 97 100       162 (!exists ${$self->{services}}{$name})
  97         461  
134             || throw IOC::ServiceAlreadyExists "Duplicate Service Name '${name}'";
135 96         383 $service->setContainer($self);
136 96         273 $self->{services}->{$name} = $service;
137 96         303 $self;
138             }
139              
140             sub unregister {
141 3     3 1 100 my ($self, $name) = @_;
142 3 100       22 (defined($name)) || throw IOC::InsufficientArguments "You must provide a service name to unregister";
143 2 100       3 (exists ${$self->{services}}{$name})
  2         18  
144             || throw IOC::ServiceNotFound "Unknown Service '${name}'";
145 1         3 my $service = $self->{services}->{$name};
146 1         7 $service->removeContainer();
147 1         3 delete $self->{services}->{$name};
148 1         14 return $service;
149             }
150              
151             sub registerWithProxy {
152 1     1 1 2 my ($self, $service, $proxy) = @_;
153 1         5 $self->register($service);
154 1         3 $self->addProxy($service->name(), $proxy);
155 1         5 $self;
156             }
157              
158             sub addProxy {
159 8     8 1 2940 my ($self, $name, $proxy) = @_;
160 8 100       31 (defined($name)) || throw IOC::InsufficientArguments "You must provide a valid service name";
161 7 100 100     74 (blessed($proxy) && $proxy->isa('IOC::Proxy'))
162             || throw IOC::InsufficientArguments "You must provide a valid IOC::Proxy object to register";
163 3 100       4 (exists ${$self->{services}}{$name})
  3         23  
164             || throw IOC::ServiceNotFound "Unknown Service '${name}'";
165 2         5 $self->{proxies}->{$name} = $proxy;
166 2         7 $self;
167             }
168              
169             sub get {
170 131     131 1 12906 my ($self, $name, %params) = @_;
171 131 100       393 (defined($name)) || throw IOC::InsufficientArguments "You must provide a name of the service";
172 130 100       163 (exists ${$self->{services}}{$name})
  130         472  
173             || throw IOC::ServiceNotFound "Unknown Service '${name}'";
174             # a literal object can have no dependencies,
175             # and therefore can have no circular refs, so
176             # we can optimize their usage there as well
177 128 100       1067 return $self->{services}->{$name}->instance()
178             if $self->{services}->{$name}->isa('IOC::Service::Literal');
179 103 100       7485 if ($self->_isServiceLocked($name)) {
180             # NOTE:
181             # if the service is parameterized
182             # then we cannot defer it - SL
183 7 50       46 ($self->{services}->{$name}->isa('IOC::Service::Parameterized'))
184             && throw IOC::IllegalOperation "The service '$name' is locked, cannot defer a parameterized instance";
185             # otherwise ...
186 7         33 return $self->{services}->{$name}->deferred();
187             }
188 96         309 $self->_lockService($name);
189 96         504 my $instance = $self->{services}->{$name}->instance(%params);
190 96         624 $self->_unlockService($name);
191 96 100 100     667 if (blessed($instance) && ref($instance) !~ /\:\:\_\:\:Proxy$/) {
192 69 100       131 return $self->{proxies}->{$name}->wrap($instance) if exists ${$self->{proxies}}{$name};
  69         369  
193             }
194 94         450 return $instance;
195             }
196              
197             sub find {
198 33     33 1 6560 my ($self, $path, $extra_args) = @_;
199 33 100       107 (defined($path))
200             || throw IOC::InsufficientArguments "You must provide a path of find a service";
201 32         183 return $self->accept(IOC::Visitor::ServiceLocator->new($path, $extra_args));
202             }
203              
204             sub hasService {
205 28     28 1 76 my ($self, $name) = @_;
206 28 100       67 (defined($name)) || throw IOC::InsufficientArguments "You must provide a name of the service";
207 27 100       28 return (exists ${$self->{services}}{$name}) ? 1 : 0;
  27         147  
208             }
209              
210             sub getServiceList {
211 6     6 1 1974 my ($self) = @_;
212 6         13 return keys %{$self->{services}};
  6         75  
213             }
214              
215             sub DESTROY {
216 78     78   1853 my ($self) = @_;
217             # this will not DESTROY all the
218             # sub-containers it holds, since
219             # a sub-container might be still
220             # refered to elsewhere.
221 78         168 $self->{sub_containers} = undef;
222             # and the same with the parent
223 78         142 $self->{parent_container} = undef;
224             # this will DESTROY all the
225             # services it holds, since
226             # a service can only have one
227             # container, then this is okay
228             # to do that, otherwise we would
229             # need to deal with that.
230 78         117 foreach my $service (values %{$self->{services}}) {
  78         1087  
231 40 50       208 defined $service && $service->DESTROY;
232             }
233             }
234              
235             # private methods
236              
237             sub _lockService {
238 96     96   153 my ($self, $name) = @_;
239 96         174 $self->{lock_level}++;
240 96         297 $self->{service_locks}->{$name} = $self->{lock_level};
241             # use Data::Dumper;
242             # print "locking '$name' -> our locks are: " . Dumper($self->{service_locks});
243             }
244              
245             sub _unlockService {
246 96     96   157 my ($self, $name) = @_;
247 96         168 $self->{lock_level}--;
248 96         345 delete $self->{service_locks}->{$name};
249             }
250              
251             sub _isServiceLocked {
252 103     103   184 my ($self, $name) = @_;
253 103         129 return (exists ${$self->{service_locks}}{$name});
  103         495  
254             }
255              
256             1;
257              
258             __END__