File Coverage

blib/lib/Venus/Container.pm
Criterion Covered Total %
statement 147 165 89.0
branch 110 146 75.3
condition 74 135 54.8
subroutine 17 17 100.0
pod 4 12 33.3
total 352 475 74.1


line stmt bran cond sub pod time code
1             package Venus::Container;
2              
3 1     1   25 use 5.018;
  1         4  
4              
5 1     1   6 use strict;
  1         3  
  1         21  
6 1     1   11 use warnings;
  1         2  
  1         40  
7              
8 1     1   5 use Venus::Class 'base', 'with';
  1         2  
  1         6  
9              
10             base 'Venus::Kind::Utility';
11              
12             with 'Venus::Role::Buildable';
13             with 'Venus::Role::Valuable';
14              
15             # BUILDERS
16              
17             sub build_args {
18 39     39 0 106 my ($self, $data) = @_;
19              
20 39 50 66     190 if (keys %$data == 1 && exists $data->{value}) {
21 0         0 return $data;
22             }
23             return {
24 39         148 value => $data
25             };
26             }
27              
28             # METHODS
29              
30             sub metadata {
31 48     48 1 104 my ($self, $name) = @_;
32              
33 48 100       120 return $name ? $self->tokens->{metadata}->{$name} : $self->tokens->{metadata};
34             }
35              
36             sub reify {
37 40     40 1 134 my ($self, $name, $data) = @_;
38              
39 40 50       99 return if !$name;
40              
41 40   66     181 my $cache = $self->{'$cache'} ||= $self->service_cache;
42              
43 40 100       110 return $cache->{$name} if $cache->{$name};
44              
45 37         72 my $services = $self->services;
46 37 100       113 my $service = $services->{$name} or return;
47              
48 35 100       100 if (my $extends = $service->{extends}) {
49 4         15 $service = $self->service_merge($service, $services->{$extends});
50             }
51              
52             my $value = $self->service_build($service,
53             $data
54             ? $self->service_reify($data)
55 35 100       141 : $self->service_reify($service->{argument}));
56              
57 35         93 my $lifecycle = $service->{lifecycle};
58 35 100 100     116 $self->{'$cache'}->{$name} = $value if $lifecycle && $lifecycle eq 'singleton';
59              
60 35         164 return $value;
61             }
62              
63             sub resolve {
64 34     34 1 1237 my ($self, $name, $data) = @_;
65              
66 34 100       99 return if !$name;
67              
68 33         107 my $value = $self->get;
69              
70 33 100       150 return exists $value->{$name} ? $value->{$name} : $self->reify($name, $data);
71             }
72              
73             sub service_build {
74 35     35 0 91 my ($self, $service, $argument) = @_;
75              
76 35         101 my $space = $self->service_space($service->{package});
77              
78 35         154 $space->load;
79              
80 35         67 my $construct;
81              
82 35 100       250 if (my $builder = $service->{builder}) {
    100          
    100          
    100          
    100          
83 6         13 my $original;
84              
85 6         17 my $injectables = $argument;
86              
87 6         31 for (my $i=0; $i < @$builder; $i++) {
88 10         342 my $buildspec = $builder->[$i];
89 10         28 my $argument = $buildspec->{argument};
90 10         18 my $argument_as = $buildspec->{argument_as};
91 10         21 my $inject = $buildspec->{inject};
92 10         17 my $return = $buildspec->{return};
93 10   66     44 my $result = $construct || $space->package;
94              
95 10         195 my @arguments;
96              
97 10 100       32 if ($inject) {
98 5         17 @arguments = $self->service_props(
99             $self->service_reify($injectables), $inject
100             );
101             }
102             else {
103 5         18 @arguments = $self->service_props(
104             $self->service_reify($argument), $argument_as
105             );
106             }
107              
108 10 50       39 if (my $function = $buildspec->{function}) {
    50          
    0          
109 0         0 $result = $space->call($function, @arguments);
110             }
111             elsif (my $method = $buildspec->{method}) {
112 10         42 $result = $space->call($method, $result, @arguments);
113             }
114             elsif (my $routine = $buildspec->{routine}) {
115 0         0 $result = $space->call($routine, $space->package, @arguments);
116             }
117             else {
118 0         0 next;
119             }
120              
121 10 50       286 if ($return eq 'class') {
122 0         0 $construct = $space->package;
123             }
124 10 100       41 if ($return eq 'result') {
125 6         11 $construct = $result;
126             }
127 10 100       48 if ($return eq 'self') {
128 4   33     25 $construct = $original //= $result;
129             }
130             }
131             }
132             elsif (my $method = $service->{method}) {
133             $construct = $space->package->$method(
134 1         5 $self->service_props($argument, $service->{argument_as}));
135             }
136             elsif (my $function = $service->{function}) {
137             $construct = $space->call($function,
138 1         18 $self->service_props($argument, $service->{argument_as}));
139             }
140             elsif (my $routine = $service->{routine}) {
141             $construct = $space->call($routine, $space->package,
142 1         6 $self->service_props($argument, $service->{argument_as}));
143             }
144             elsif (my $constructor = $service->{constructor}) {
145             $construct = $space->package->$constructor(
146 1         13 $self->service_props($argument, $service->{argument_as}));
147             }
148             else {
149             $construct = $space->package->can('new')
150             ? $space->call('new', $space->package,
151 25 100       66 $self->service_props($argument, $service->{argument_as}))
152             : $space->package;
153             }
154              
155 35         185 return $construct;
156             }
157              
158             sub service_cache {
159 30     30 0 66 my ($self) = @_;
160              
161 30         66 $self->{'$cache'} = {};
162              
163 30         56 my $cache = {};
164 30         96 my $services = $self->services;
165              
166 30         136 for my $name (keys %$services) {
167 42 50       116 next if $cache->{$name};
168              
169 42         111 my $service = $services->{$name};
170 42         73 my $lifecycle = $service->{lifecycle};
171              
172 42 100       111 next if !$lifecycle;
173              
174 2 100       10 if ($lifecycle eq 'eager') {
175 1         5 $cache->{$name} = $self->reify($name);
176             }
177             }
178              
179 30         101 return $cache;
180             }
181              
182             sub service_merge {
183 4     4 0 14 my ($self, $left, $right) = @_;
184              
185 4         9 my $new_service = {};
186              
187 4 50       15 if (my $extends = $right->{extends}) {
188 0         0 $right = $self->service_merge($right, $self->services->{$extends});
189             }
190              
191 4         21 $new_service = {%$right, %$left};
192              
193 4         14 delete $new_service->{extends};
194              
195 4 50 66     17 if ((my $arg1 = $left->{argument}) || (my $arg2 = $right->{argument})) {
196 4 100 66     52 if ((defined $left->{argument} && !ref($arg1))
    50 66        
    50 66        
      33        
      0        
      33        
      33        
      0        
      33        
197             || (defined $right->{argument} && !ref($arg2))) {
198 3 50 33     18 $new_service->{argument} ||= $arg1 if $arg1;
199             }
200             elsif ((defined $left->{argument} && (ref($arg1) eq 'ARRAY'))
201             && (defined $right->{argument} && (ref($arg2) eq 'ARRAY'))) {
202 0         0 $new_service->{argument} = [@$arg2, @$arg1];
203             }
204             elsif ((defined $left->{argument} && (ref($arg1) eq 'HASH'))
205             && (defined $right->{argument} && (ref($arg2) eq 'HASH'))) {
206 0         0 $new_service->{argument} = {%$arg2, %$arg1};
207             }
208             else {
209 1 50 0     4 $new_service->{argument} ||= $arg1 if $arg1;
210             }
211             }
212              
213 4         11 return $new_service;
214             }
215              
216             sub service_props {
217 36     36 0 151 my ($self, $prop, $prop_as) = @_;
218              
219 36         54 my @props;
220              
221 36 100 100     139 if ($prop && $prop_as) {
222 7 50 33     53 if (lc($prop_as) eq 'array' || lc($prop_as) eq 'arrayref') {
223 0 0       0 if (ref $prop eq 'HASH') {
224 0         0 @props = ([$prop]);
225             }
226             else {
227 0         0 @props = ($prop);
228             }
229             }
230 7 100 66     35 if (lc($prop_as) eq 'hash' || lc($prop_as) eq 'hashref') {
231 5 50       25 if (ref $prop eq 'ARRAY') {
232 0         0 @props = ({@$prop});
233             }
234             else {
235 5         47 @props = ($prop);
236             }
237             }
238 7 100       26 if (lc($prop_as) eq 'list') {
239 2 50       10 if (ref $prop eq 'ARRAY') {
    100          
240 0         0 @props = (@$prop);
241             }
242             elsif (ref $prop eq 'HASH') {
243 1         5 @props = (%$prop);
244             }
245             else {
246 1         3 @props = ($prop);
247             }
248             }
249             }
250             else {
251 29 100       75 @props = ($prop) if defined $prop;
252             }
253              
254 36         142 return (@props);
255             }
256              
257             sub service_reify {
258 45     45 0 92 my ($self, $props) = @_;
259              
260 45         91 my $metadata = $self->metadata;
261 45         106 my $services = $self->services;
262              
263 45 50       138 if (ref $props eq 'ARRAY') {
264 0         0 $props = [map $self->service_reify($_), @$props];
265             }
266              
267             # $metadata
268 45 100 100     184 if (ref $props eq 'HASH' && (keys %$props) == 1) {
269 18 100 66     86 if ($props && $props->{'$metadata'}) {
270 8         29 $props = $metadata->{$props->{'$metadata'}};
271             }
272             }
273              
274             # $envvar
275 45 100 100     176 if (ref $props eq 'HASH' && (keys %$props) == 1) {
276 12 100       41 if (my $envvar = $props->{'$envvar'}) {
277 3 50       175 if (exists $ENV{$envvar}) {
    50          
278 0         0 $props = $ENV{$envvar};
279             }
280             elsif (exists $ENV{uc($envvar)}) {
281 3         18 $props = $ENV{uc($envvar)};
282             }
283             else {
284 0         0 $props = undef;
285             }
286             }
287             }
288              
289             # $function
290 45 100 100     157 if (ref $props eq 'HASH' && (keys %$props) == 1) {
291 9 100       26 if ($props->{'$function'}) {
292 1         7 my ($name, $next) = split /#/, $props->{'$function'};
293 1 50 33     7 if ($name && $next) {
294 1 50       6 if (my $resolved = $self->reify($name)) {
295 1 50 33     15 if (Scalar::Util::blessed($resolved)
      33        
296             || (!ref($resolved) && ($resolved =~ /^[a-z-A-Z]/))) {
297 1   33     10 my $space = $self->service_space(ref $resolved || $resolved);
298 1 50 33     11 $props = $space->call($next) if $next && $next =~ /^[a-zA-Z]/;
299             }
300             }
301             }
302             }
303             }
304              
305             # $method
306 45 100 100     135 if (ref $props eq 'HASH' && (keys %$props) == 1) {
307 8 100       26 if ($props->{'$method'}) {
308 1         6 my ($name, $next) = split /#/, $props->{'$method'};
309 1 50 33     8 if ($name && $next) {
310 1 50       4 if (my $resolved = $self->reify($name)) {
311 1 50 33     18 if (Scalar::Util::blessed($resolved)
      33        
312             || (!ref($resolved) && ($resolved =~ /^[a-z-A-Z]/))) {
313 1 50 33     27 $props = $resolved->$next if $next && $next =~ /^[a-zA-Z]/;
314             }
315             }
316             }
317             }
318             }
319              
320             # $routine
321 45 100 100     206 if (ref $props eq 'HASH' && (keys %$props) == 1) {
322 7 100       21 if ($props->{'$routine'}) {
323 1         7 my ($name, $next) = split /#/, $props->{'$routine'};
324 1 50 33     8 if ($name && $next) {
325 1 50       4 if (my $resolved = $self->reify($name)) {
326 1 50 33     13 if (Scalar::Util::blessed($resolved)
      33        
327             || (!ref($resolved) && ($resolved =~ /^[a-z-A-Z]/)))
328             {
329 1   33     8 my $space = $self->service_space(ref $resolved || $resolved);
330 1 50 33     34 $props = $space->call($next, $space->package)
331             if $next && $next =~ /^[a-zA-Z]/;
332             }
333             }
334             }
335             }
336             }
337              
338             # $callback
339 45 100 100     134 if (ref $props eq 'HASH' && (keys %$props) == 1) {
340 6 100       22 if (my $callback = $props->{'$callback'}) {
341 1     1   6 $props = sub { $self->reify($callback) };
  1         5  
342             }
343             }
344              
345             # $service
346 45 100 100     140 if (ref $props eq 'HASH' && (keys %$props) == 1) {
347 5 50       13 if ($props->{'$service'}) {
348 0         0 $props = $self->reify($props->{'$service'});
349             }
350             }
351              
352 45 50 66     145 if (ref $props eq 'HASH' && grep ref, values %$props) {
353 0         0 @$props{keys %$props} = map $self->service_reify($_), values %$props;
354             }
355              
356 45         147 return $props;
357             }
358              
359             sub service_space {
360 37     37 0 77 my ($self, $name) = @_;
361              
362 37         1178 require Venus::Space;
363              
364 37         200 return Venus::Space->new($name);
365             }
366              
367             sub services {
368 115     115 1 186 my ($self, $name) = @_;
369              
370 115 100       242 return $name ? $self->tokens->{services}->{$name} : $self->tokens->{services};
371             }
372              
373             sub tokens {
374 163     163 0 242 my ($self) = @_;
375              
376             return $self->{'$tokens'} ||= {
377             services => $self->get->{'$services'} || {},
378 163   100     438 metadata => $self->get->{'$metadata'} || {},
      100        
      100        
379             };
380             }
381              
382             1;
383              
384              
385              
386             =head1 NAME
387              
388             Venus::Container - Container Class
389              
390             =cut
391              
392             =head1 ABSTRACT
393              
394             Container Class for Perl 5
395              
396             =cut
397              
398             =head1 SYNOPSIS
399              
400             package main;
401              
402             use Venus::Container;
403              
404             my $container = Venus::Container->new;
405              
406             # my $object = $container->resolve('...');
407              
408             # "..."
409              
410             =cut
411              
412             =head1 DESCRIPTION
413              
414             This package provides methods for building objects with dependency injection.
415              
416             =cut
417              
418             =head1 INHERITS
419              
420             This package inherits behaviors from:
421              
422             L
423              
424             =cut
425              
426             =head1 INTEGRATES
427              
428             This package integrates behaviors from:
429              
430             L
431              
432             L
433              
434             =cut
435              
436             =head1 METHODS
437              
438             This package provides the following methods:
439              
440             =cut
441              
442             =head2 metadata
443              
444             metadata(string $name) (any)
445              
446             The metadata method returns the C<$metadata> section of the configuration data
447             if no name is provided, otherwise returning the specific metadata keyed on the
448             name provided.
449              
450             I>
451              
452             =over 4
453              
454             =item metadata example 1
455              
456             # given: synopsis
457              
458             package main;
459              
460             my $metadata = $container->metadata;
461              
462             # {}
463              
464             =back
465              
466             =over 4
467              
468             =item metadata example 2
469              
470             # given: synopsis
471              
472             package main;
473              
474             $container->value({
475             '$metadata' => {
476             tmplog => "/tmp/log"
477             },
478             '$services' => {
479             log => { package => "Venus/Path", argument => { '$metadata' => "tmplog" } }
480             }
481             });
482              
483             my $metadata = $container->metadata;
484              
485             # {
486             # tmplog => "/tmp/log"
487             # }
488              
489             =back
490              
491             =over 4
492              
493             =item metadata example 3
494              
495             # given: synopsis
496              
497             package main;
498              
499             $container->value({
500             '$metadata' => {
501             tmplog => "/tmp/log"
502             },
503             '$services' => {
504             log => { package => "Venus/Path", argument => { '$metadata' => "tmplog" } }
505             }
506             });
507              
508             my $metadata = $container->metadata("tmplog");
509              
510             # "/tmp/log"
511              
512             =back
513              
514             =cut
515              
516             =head2 reify
517              
518             reify(string $name) (any)
519              
520             The reify method resolves and returns an object or value based on the service
521             name provided.
522              
523             I>
524              
525             =over 4
526              
527             =item reify example 1
528              
529             package main;
530              
531             use Venus::Container;
532              
533             my $container = Venus::Container->new({
534             '$metadata' => {
535             tmplog => "/tmp/log"
536             },
537             '$services' => {
538             log => {
539             package => "Venus/Path",
540             argument => {
541             '$metadata' => "tmplog"
542             }
543             }
544             }
545             });
546              
547             my $reify = $container->reify('tmp');
548              
549             # undef
550              
551             =back
552              
553             =over 4
554              
555             =item reify example 2
556              
557             package main;
558              
559             use Venus::Container;
560              
561             my $container = Venus::Container->new({
562             '$metadata' => {
563             tmplog => "/tmp/log"
564             },
565             '$services' => {
566             log => {
567             package => "Venus/Path",
568             argument => {
569             '$metadata' => "tmplog"
570             }
571             }
572             }
573             });
574              
575             my $reify = $container->reify('log');
576              
577             # bless({value => '/tmp/log'}, 'Venus::Path')
578              
579             =back
580              
581             =over 4
582              
583             =item reify example 3
584              
585             package main;
586              
587             use Venus::Container;
588              
589             my $container = Venus::Container->new({
590             '$metadata' => {
591             tmplog => "/tmp/log"
592             },
593             '$services' => {
594             log => {
595             package => "Venus/Path",
596             argument => {
597             '$metadata' => "tmplog"
598             }
599             }
600             }
601             });
602              
603             my $reify = $container->reify('log', '.');
604              
605             # bless({value => '.'}, 'Venus::Path')
606              
607             =back
608              
609             =over 4
610              
611             =item reify example 4
612              
613             package main;
614              
615             use Venus::Container;
616              
617             my $container = Venus::Container->new({
618             '$metadata' => {
619             tmplog => "/tmp/log"
620             },
621             '$services' => {
622             log => {
623             package => "Venus/Path",
624             argument => {
625             '$metadata' => "tmplog"
626             }
627             }
628             }
629             });
630              
631             my $reify = $container->reify('log', {value => '.'});
632              
633             # bless({value => '.'}, 'Venus::Path')
634              
635             =back
636              
637             =cut
638              
639             =head2 resolve
640              
641             resolve(string $name) (any)
642              
643             The resolve method resolves and returns an object or value based on the
644             configuration key or service name provided.
645              
646             I>
647              
648             =over 4
649              
650             =item resolve example 1
651              
652             package main;
653              
654             use Venus::Container;
655              
656             my $container = Venus::Container->new({
657             name => 'app',
658             log => '/tmp/log/app.log',
659             '$metadata' => {
660             tmplog => "/tmp/log",
661             varlog => "/var/log"
662             },
663             '$services' => {
664             log => {
665             package => "Venus/Path",
666             argument => '.'
667             },
668             tmp_log => {
669             package => "Venus/Path",
670             extends => 'log',
671             argument => {
672             '$metadata' => "tmplog"
673             }
674             },
675             var_log => {
676             package => "Venus/Path",
677             extends => 'log',
678             argument => {
679             '$metadata' => "varlog"
680             }
681             }
682             }
683             });
684              
685             my $result = $container->resolve;
686              
687             # undef
688              
689             =back
690              
691             =over 4
692              
693             =item resolve example 2
694              
695             package main;
696              
697             use Venus::Container;
698              
699             my $container = Venus::Container->new({
700             name => 'app',
701             log => '/tmp/log/app.log',
702             '$metadata' => {
703             tmplog => "/tmp/log",
704             varlog => "/var/log"
705             },
706             '$services' => {
707             log => {
708             package => "Venus/Path",
709             argument => '.'
710             },
711             tmp_log => {
712             package => "Venus/Path",
713             extends => 'log',
714             argument => {
715             '$metadata' => "tmplog"
716             }
717             },
718             var_log => {
719             package => "Venus/Path",
720             extends => 'log',
721             argument => {
722             '$metadata' => "varlog"
723             }
724             }
725             }
726             });
727              
728             my $result = $container->resolve('log');
729              
730             # "/tmp/log/app.log"
731              
732             =back
733              
734             =over 4
735              
736             =item resolve example 3
737              
738             package main;
739              
740             use Venus::Container;
741              
742             my $container = Venus::Container->new({
743             name => 'app',
744             log => '/tmp/log/app.log',
745             '$metadata' => {
746             tmplog => "/tmp/log",
747             varlog => "/var/log"
748             },
749             '$services' => {
750             log => {
751             package => "Venus/Path",
752             argument => '.'
753             },
754             tmp_log => {
755             package => "Venus/Path",
756             extends => 'log',
757             argument => {
758             '$metadata' => "tmplog"
759             }
760             },
761             var_log => {
762             package => "Venus/Path",
763             extends => 'log',
764             argument => {
765             '$metadata' => "varlog"
766             }
767             }
768             }
769             });
770              
771             my $result = $container->resolve('tmp_log');
772              
773             # bless({value => '/tmp/log'}, 'Venus::Path')
774              
775             =back
776              
777             =over 4
778              
779             =item resolve example 4
780              
781             package main;
782              
783             use Venus::Container;
784              
785             my $container = Venus::Container->new({
786             name => 'app',
787             log => '/tmp/log/app.log',
788             '$metadata' => {
789             tmplog => "/tmp/log",
790             varlog => "/var/log"
791             },
792             '$services' => {
793             log => {
794             package => "Venus/Path",
795             argument => '.'
796             },
797             tmp_log => {
798             package => "Venus/Path",
799             extends => 'log',
800             argument => {
801             '$metadata' => "tmplog"
802             }
803             },
804             var_log => {
805             package => "Venus/Path",
806             extends => 'log',
807             argument => {
808             '$metadata' => "varlog"
809             }
810             }
811             }
812             });
813              
814             my $result = $container->resolve('var_log');
815              
816             # bless({value => '/var/log'}, 'Venus::Path')
817              
818             =back
819              
820             =cut
821              
822             =head2 services
823              
824             services(string $name) (any)
825              
826             The services method returns the C<$services> section of the configuration data
827             if no name is provided, otherwise returning the specific service keyed on the
828             name provided.
829              
830             I>
831              
832             =over 4
833              
834             =item services example 1
835              
836             # given: synopsis
837              
838             package main;
839              
840             my $services = $container->services;
841              
842             # {}
843              
844             =back
845              
846             =over 4
847              
848             =item services example 2
849              
850             # given: synopsis
851              
852             package main;
853              
854             $container->value({
855             '$metadata' => {
856             tmplog => "/tmp/log"
857             },
858             '$services' => {
859             log => { package => "Venus/Path", argument => { '$metadata' => "tmplog" } }
860             }
861             });
862              
863             my $services = $container->services;
864              
865             # {
866             # log => {
867             # package => "Venus/Path",
868             # argument => {'$metadata' => "tmplog"}
869             # }
870             # }
871              
872             =back
873              
874             =over 4
875              
876             =item services example 3
877              
878             # given: synopsis
879              
880             package main;
881              
882             $container->value({
883             '$metadata' => {
884             tmplog => "/tmp/log"
885             },
886             '$services' => {
887             log => { package => "Venus/Path", argument => { '$metadata' => "tmplog" } }
888             }
889             });
890              
891             my $services = $container->services('log');
892              
893             # {
894             # package => "Venus/Path",
895             # argument => {'$metadata' => "tmplog"}
896             # }
897              
898             =back
899              
900             =cut
901              
902             =head1 FEATURES
903              
904             This package provides the following features:
905              
906             =cut
907              
908             =over 4
909              
910             =item #argument
911              
912             This package supports providing static and/or dynamic arguments during object
913             construction from metadata or other services.
914              
915             B
916              
917             package main;
918              
919             use Venus::Container;
920              
921             my $container = Venus::Container->new({
922             '$services' => {
923             'date' => {
924             'package' => 'Venus/Date',
925             'argument' => 570672000,
926             },
927             }
928             });
929              
930             # bless(..., 'Venus::Container')
931              
932             # my $result = $container->resolve('date');
933              
934             # bless(..., 'Venus::Date')
935              
936             B
937              
938             package main;
939              
940             use Venus::Container;
941              
942             my $container = Venus::Container->new({
943             '$services' => {
944             'date' => {
945             'package' => 'Venus/Date',
946             'argument' => {
947             year => 1988,
948             month => 2,
949             day => 1,
950             },
951             },
952             }
953             });
954              
955             # bless(..., 'Venus::Container')
956              
957             # my $result = $container->resolve('date');
958              
959             # bless(..., 'Venus::Date')
960              
961             =back
962              
963             =over 4
964              
965             =item #argument_as
966              
967             This package supports transforming the way static and/or dynamic arguments are
968             passed to the operation during object construction. Acceptable options are
969             C or C (which provides an arrayref), C or C
970             (which provides a hashref), or C (which provides a flattened list of
971             arguments).
972              
973             B
974              
975             package main;
976              
977             use Venus::Container;
978              
979             my $container = Venus::Container->new({
980             '$services' => {
981             'date' => {
982             'package' => 'Venus/Date',
983             'argument' => {
984             year => 1988,
985             month => 2,
986             day => 1,
987             },
988             argument_as => 'list',
989             },
990             }
991             });
992              
993             # bless(..., 'Venus::Container')
994              
995             # my $result = $container->resolve('date');
996              
997             # bless(..., 'Venus::Date')
998              
999             B
1000              
1001             package main;
1002              
1003             use Venus::Container;
1004              
1005             my $container = Venus::Container->new({
1006             '$services' => {
1007             'date' => {
1008             'package' => 'Venus/Date',
1009             'argument' => {
1010             year => 1988,
1011             month => 2,
1012             day => 1,
1013             },
1014             argument_as => 'hash',
1015             },
1016             }
1017             });
1018              
1019             # bless(..., 'Venus::Container')
1020              
1021             # my $result = $container->resolve('date');
1022              
1023             # bless(..., 'Venus::Date')
1024              
1025             =back
1026              
1027             =over 4
1028              
1029             =item #builder
1030              
1031             This package supports specifying multiple build steps as C,
1032             C, and C calls and chaining them together. Each build step
1033             supports any directive that can be used outside of a build step. Each build
1034             step can be configured, with the C directive, to use a particular value
1035             to chain the next subroutine call. Acceptable C values are C
1036             (package name string), C (scalar return value from the current build
1037             step), and C (instantiated package). Additionally, you can use the
1038             C directive (with any value accepted by C) to override the
1039             default arguments using the arguments provided to the L or L
1040             method.
1041              
1042             B
1043              
1044             package main;
1045              
1046             use Venus::Container;
1047              
1048             my $container = Venus::Container->new({
1049             '$services' => {
1050             datetime => {
1051             package => "Venus/Date",
1052             builder => [
1053             {
1054             method => 'new',
1055             argument => 570672000,
1056             return => 'self',
1057             },
1058             {
1059             method => 'string',
1060             return => 'result',
1061             }
1062             ],
1063             }
1064             }
1065             });
1066              
1067             # bless(..., 'Venus::Container')
1068              
1069             # my $result = $container->resolve('datetime');
1070              
1071             # "1988-02-01T00:00:00Z"
1072              
1073             B
1074              
1075             package main;
1076              
1077             use Venus::Container;
1078              
1079             my $container = Venus::Container->new({
1080             '$services' => {
1081             datetime => {
1082             package => "Venus/Date",
1083             builder => [
1084             {
1085             method => 'new',
1086             argument => 570672000,
1087             return => 'self',
1088             inject => 'list',
1089             },
1090             {
1091             method => 'string',
1092             return => 'result',
1093             }
1094             ],
1095             }
1096             }
1097             });
1098              
1099             # bless(..., 'Venus::Container')
1100              
1101             # my $result = $container->resolve('datetime', 604945074);
1102              
1103             # "1989-03-03T16:17:54Z"
1104              
1105             =back
1106              
1107             =over 4
1108              
1109             =item #config
1110              
1111             This package supports configuring services and metadata in the service of
1112             building objects and values.
1113              
1114             B
1115              
1116             package main;
1117              
1118             use Venus::Container;
1119              
1120             my $container = Venus::Container->new({
1121             'name' => 'app',
1122             'secret' => '...',
1123             '$metadata' => {
1124             home => {
1125             '$envvar' => 'home',
1126             }
1127             },
1128             '$services' => {
1129             date => {
1130             package => "Venus/Date",
1131             },
1132             path => {
1133             package => "Venus/Path",
1134             argument => {
1135             '$metadata' => 'home',
1136             },
1137             }
1138             }
1139             });
1140              
1141             # bless(..., 'Venus::Container')
1142              
1143             # my $path = $container->resolve('path');
1144              
1145             # bless(..., 'Venus::Path')
1146              
1147             # my $name = $container->resolve('name');
1148              
1149             # "app"
1150              
1151             =back
1152              
1153             =over 4
1154              
1155             =item #constructor
1156              
1157             This package supports specifying constructors other than the traditional C
1158             routine. A constructor is always called with the package name as the invocant.
1159              
1160             B
1161              
1162             package main;
1163              
1164             use Venus::Container;
1165              
1166             my $container = Venus::Container->new({
1167             '$services' => {
1168             path => {
1169             package => "Venus/Path",
1170             constructor => "new",
1171             }
1172             }
1173             });
1174              
1175             # bless(..., 'Venus::Container')
1176              
1177             # my $result = $container->resolve('path');
1178              
1179             # bless(..., 'Venus::Path')
1180              
1181             =back
1182              
1183             =over 4
1184              
1185             =item #extends
1186              
1187             This package supports extending services in the definition of other services,
1188             recursively compiling service configurations and eventually executing the
1189             requested compiled service.
1190              
1191             B
1192              
1193             package main;
1194              
1195             use Venus::Container;
1196              
1197             my $container = Venus::Container->new({
1198             '$services' => {
1199             log => {
1200             package => "Venus/Log",
1201             argument => {
1202             level => "trace",
1203             },
1204             },
1205             development_log => {
1206             package => "Venus/Log",
1207             extends => "log",
1208             builder => [
1209             {
1210             method => "new",
1211             return => "self",
1212             inject => "hash",
1213             }
1214             ],
1215             },
1216             production_log => {
1217             package => "Venus/Log",
1218             extends => "log",
1219             argument => {
1220             level => "error",
1221             },
1222             builder => [
1223             {
1224             method => "new",
1225             return => "self",
1226             inject => "hash",
1227             }
1228             ],
1229             },
1230             }
1231             });
1232              
1233             # bless(..., 'Venus::Container')
1234              
1235             # my $result = $container->resolve('development_log');
1236              
1237             # bless(..., 'Venus::Log')
1238              
1239             # my $level = $result->level;
1240              
1241             # "trace"
1242              
1243             # $result = $container->resolve('production_log');
1244              
1245             # bless(..., 'Venus::Log')
1246              
1247             # $level = $result->level;
1248              
1249             # "error"
1250              
1251             =back
1252              
1253             =over 4
1254              
1255             =item #function
1256              
1257             This package supports specifying construction as a function call, which when
1258             called does not provide an invocant.
1259              
1260             B
1261              
1262             package main;
1263              
1264             use Venus::Container;
1265              
1266             my $container = Venus::Container->new({
1267             '$services' => {
1268             foo_hex => {
1269             package => "Digest/MD5",
1270             function => "md5_hex",
1271             argument => "foo",
1272             }
1273             }
1274             });
1275              
1276             # bless(..., 'Venus::Container')
1277              
1278             # my $result = $container->resolve('foo_hex');
1279              
1280             # "acbd18db4cc2f85cedef654fccc4a4d8"
1281              
1282             =back
1283              
1284             =over 4
1285              
1286             =item #lifecycle
1287              
1288             This package supports different lifecycle options which determine when services
1289             are built and whether they're persisted. Acceptable lifecycle values are
1290             C (which caches the result once encountered) and C (which
1291             caches the service upon the first execution of any service).
1292              
1293             B
1294              
1295             package main;
1296              
1297             use Venus::Container;
1298              
1299             my $container = Venus::Container->new({
1300             '$services' => {
1301             match => {
1302             package => "Venus/Match",
1303             argument => {
1304             'a'..'h'
1305             },
1306             builder => [
1307             {
1308             method => "new",
1309             return => "result",
1310             },
1311             {
1312             method => "data",
1313             return => "result",
1314             inject => "hash",
1315             }
1316             ],
1317             lifecycle => 'eager',
1318             }
1319             }
1320             });
1321              
1322             # bless(..., 'Venus::Container')
1323              
1324             # my $result = $container->resolve('thing');
1325              
1326             # undef
1327              
1328             # my $result = $container->resolve('match');
1329              
1330             # bless(..., 'Venus::Match')
1331              
1332             B
1333              
1334             package main;
1335              
1336             use Venus::Container;
1337              
1338             my $container = Venus::Container->new({
1339             '$services' => {
1340             match => {
1341             package => "Venus/Match",
1342             argument => {
1343             'a'..'h'
1344             },
1345             builder => [
1346             {
1347             method => "new",
1348             return => "result",
1349             },
1350             {
1351             method => "data",
1352             return => "result",
1353             inject => "hash",
1354             }
1355             ],
1356             lifecycle => 'singleton',
1357             }
1358             }
1359             });
1360              
1361             # bless(..., 'Venus::Container')
1362              
1363             # my $result = $container->resolve('match');
1364              
1365             # bless(..., 'Venus::Match')
1366              
1367             =back
1368              
1369             =over 4
1370              
1371             =item #metadata
1372              
1373             This package supports specifying data and structures which can be used in the
1374             construction of multiple services.
1375              
1376             B
1377              
1378             package main;
1379              
1380             use Venus::Container;
1381              
1382             my $container = Venus::Container->new({
1383             '$metadata' => {
1384             'homedir' => '/home',
1385             'tempdir' => '/tmp',
1386             },
1387             '$services' => {
1388             home => {
1389             package => "Venus/Path",
1390             argument => {
1391             '$metadata' => 'homedir',
1392             },
1393             },
1394             temp => {
1395             package => "Venus/Path",
1396             argument => {
1397             '$metadata' => 'tempdir',
1398             },
1399             },
1400             }
1401             });
1402              
1403             # bless(..., 'Venus::Container')
1404              
1405             # my $result = $container->resolve('home');
1406              
1407             # bless(..., 'Venus::Path')
1408              
1409             # my $result = $container->resolve('temp');
1410              
1411             # bless(..., 'Venus::Path')
1412              
1413             =back
1414              
1415             =over 4
1416              
1417             =item #method
1418              
1419             This package supports specifying construction as a method call, which when
1420             called provides the package or object instance as the invocant.
1421              
1422             B
1423              
1424             package main;
1425              
1426             use Venus::Container;
1427              
1428             my $container = Venus::Container->new({
1429             '$services' => {
1430             date => {
1431             package => "Venus/Date",
1432             argument => 570672000,
1433             method => "new",
1434             }
1435             }
1436             });
1437              
1438             # bless(..., 'Venus::Container')
1439              
1440             # my $result = $container->resolve('date');
1441              
1442             # bless(..., 'Venus::Date')
1443              
1444             =back
1445              
1446             =over 4
1447              
1448             =item #routine
1449              
1450             This package supports specifying construction as a function call, which when
1451             called provides the package as the invocant.
1452              
1453             B
1454              
1455             package main;
1456              
1457             use Venus::Container;
1458              
1459             my $container = Venus::Container->new({
1460             '$services' => {
1461             date => {
1462             package => "Venus/Date",
1463             argument => 570672000,
1464             routine => "new",
1465             }
1466             }
1467             });
1468              
1469             # bless(..., 'Venus::Container')
1470              
1471             # my $result = $container->resolve('date');
1472              
1473             # bless(..., 'Venus::Date')
1474              
1475             =back
1476              
1477             =over 4
1478              
1479             =item #service
1480              
1481             This package supports defining services to be constructed on-demand or
1482             automatically on instantiation.
1483              
1484             B
1485              
1486             package main;
1487              
1488             use Venus::Container;
1489              
1490             my $container = Venus::Container->new({
1491             '$services' => {
1492             path => {
1493             package => "Venus/Path",
1494             }
1495             }
1496             });
1497              
1498             # bless(..., 'Venus::Container')
1499              
1500             # my $result = $container->resolve('path');
1501              
1502             # bless(..., 'Venus::Path')
1503              
1504             =back
1505              
1506             =over 4
1507              
1508             =item $callback
1509              
1510             This package supports resolving services as callbacks to be passed around
1511             and/or resolved by other services. The C<$callback> directive is used to
1512             specify the name of a service to be resolved and passed as an argument.
1513              
1514             B
1515              
1516             package main;
1517              
1518             use Venus::Container;
1519              
1520             my $container = Venus::Container->new({
1521             '$services' => {
1522             log => {
1523             package => "Venus/Path",
1524             argument => '.',
1525             },
1526             lazy_log => {
1527             package => "Venus/Code",
1528             argument => {
1529             '$callback' => 'log',
1530             }
1531             }
1532             }
1533             });
1534              
1535             # bless(..., 'Venus::Container')
1536              
1537             # my $result = $container->resolve('lazy_log');
1538              
1539             # bless(..., 'Venus::Code')
1540              
1541             # my $return = $result->call;
1542              
1543             # bless(..., 'Venus::Path')
1544              
1545             =back
1546              
1547             =over 4
1548              
1549             =item $envvar
1550              
1551             This package supports inlining environment variables as arguments to services.
1552             The C<$envvar> directive is used to specify the name of an environment variable,
1553             and can also be used in metadata for reusability.
1554              
1555             B
1556              
1557             package main;
1558              
1559             use Venus::Container;
1560              
1561             my $container = Venus::Container->new({
1562             '$services' => {
1563             home => {
1564             package => "Venus/Path",
1565             argument => {
1566             '$envvar' => 'home',
1567             }
1568             }
1569             }
1570             });
1571              
1572             # bless(..., 'Venus::Container')
1573              
1574             # my $result = $container->resolve('home');
1575              
1576             # bless(..., 'Venus::Path')
1577              
1578             B
1579              
1580             package main;
1581              
1582             use Venus::Container;
1583              
1584             my $container = Venus::Container->new({
1585             '$metadata' => {
1586             home => {
1587             '$envvar' => 'home',
1588             }
1589             },
1590             '$services' => {
1591             home => {
1592             package => "Venus/Path",
1593             argument => {
1594             '$metadata' => 'home',
1595             }
1596             }
1597             }
1598             });
1599              
1600             # bless(..., 'Venus::Container')
1601              
1602             # my $result = $container->resolve('home');
1603              
1604             # bless(..., 'Venus::Path')
1605              
1606             =back
1607              
1608             =over 4
1609              
1610             =item $function
1611              
1612             This package supports inlining the result of a service resolution and function
1613             call as arguments to services. The C<#> delimited C<$function> directive is
1614             used to specify the name of an existing service on the right-hand side, and an
1615             arbitrary function to be call on the result on the left-hand side.
1616              
1617             B
1618              
1619             package main;
1620              
1621             use Venus::Container;
1622              
1623             my $container = Venus::Container->new({
1624             '$services' => {
1625             filespec => {
1626             package => 'File/Spec/Functions',
1627             },
1628             tempdir => {
1629             package => "Venus/Path",
1630             argument => {
1631             '$function' => 'filespec#tmpdir',
1632             }
1633             }
1634             }
1635             });
1636              
1637             # bless(..., 'Venus::Container')
1638              
1639             # my $result = $container->resolve('tempdir');
1640              
1641             # bless(..., 'Venus::Path')
1642              
1643             =back
1644              
1645             =over 4
1646              
1647             =item $metadata
1648              
1649             This package supports inlining configuration data as arguments to services. The
1650             C<$metadata> directive is used to specify the name of a stashed configuration
1651             value or data structure.
1652              
1653             B
1654              
1655             package main;
1656              
1657             use Venus::Container;
1658              
1659             my $container = Venus::Container->new({
1660             '$metadata' => {
1661             home => '/home/ubuntu',
1662             },
1663             '$services' => {
1664             home => {
1665             package => "Venus/Path",
1666             argument => {
1667             '$metadata' => 'home',
1668             }
1669             }
1670             }
1671             });
1672              
1673             # bless(..., 'Venus::Container')
1674              
1675             # my $result = $container->resolve('home');
1676              
1677             # bless(..., 'Venus::Path')
1678              
1679             =back
1680              
1681             =over 4
1682              
1683             =item $method
1684              
1685             This package supports inlining the result of a service resolution and method
1686             call as arguments to services. The C<#> delimited C<$method> directive is used
1687             to specify the name of an existing service on the right-hand side, and an
1688             arbitrary method to be call on the result on the left-hand side.
1689              
1690             B
1691              
1692             package main;
1693              
1694             use Venus::Container;
1695              
1696             my $container = Venus::Container->new({
1697             '$services' => {
1698             filespec => {
1699             package => 'File/Spec',
1700             },
1701             tempdir => {
1702             package => "Venus/Path",
1703             argument => {
1704             '$method' => 'filespec#tmpdir',
1705             }
1706             }
1707             }
1708             });
1709              
1710             # bless(..., 'Venus::Container')
1711              
1712             # my $result = $container->resolve('tempdir');
1713              
1714             # bless(..., 'Venus::Path')
1715              
1716             =back
1717              
1718             =over 4
1719              
1720             =item $routine
1721              
1722             This package supports inlining the result of a service resolution and routine
1723             call as arguments to services. The C<#> delimited C<$routine> directive is used
1724             to specify the name of an existing service on the right-hand side, and an
1725             arbitrary routine to be call on the result on the left-hand side.
1726              
1727             B
1728              
1729             package main;
1730              
1731             use Venus::Container;
1732              
1733             my $container = Venus::Container->new({
1734             '$services' => {
1735             filespec => {
1736             package => 'File/Spec',
1737             },
1738             tempdir => {
1739             package => "Venus/Path",
1740             argument => {
1741             '$routine' => 'filespec#tmpdir',
1742             }
1743             }
1744             }
1745             });
1746              
1747             # bless(..., 'Venus::Container')
1748              
1749             # my $result = $container->resolve('tempdir');
1750              
1751             # bless(..., 'Venus::Path')
1752              
1753             =back
1754              
1755             =over 4
1756              
1757             =item $service
1758              
1759             This package supports inlining resolved services as arguments to other
1760             services. The C<$service> directive is used to specify the name of a service to
1761             be resolved and passed as an argument.
1762              
1763             B
1764              
1765             package main;
1766              
1767             use Venus::Container;
1768              
1769             my $container = Venus::Container->new({
1770             '$services' => {
1771             'path' => {
1772             'package' => 'Venus/Path',
1773             },
1774             }
1775             });
1776              
1777             # bless(..., 'Venus::Container')
1778              
1779             # my $result = $container->resolve('path');
1780              
1781             # bless(..., 'Venus::Path')
1782              
1783             =back
1784              
1785             =head1 AUTHORS
1786              
1787             Awncorp, C
1788              
1789             =cut
1790              
1791             =head1 LICENSE
1792              
1793             Copyright (C) 2000, Awncorp, C.
1794              
1795             This program is free software, you can redistribute it and/or modify it under
1796             the terms of the Apache license version 2.0.
1797              
1798             =cut