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   15 use 5.018;
  1         4  
4              
5 1     1   6 use strict;
  1         3  
  1         22  
6 1     1   4 use warnings;
  1         2  
  1         47  
7              
8 1     1   5 use Venus::Class 'base', 'with';
  1         3  
  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 103 my ($self, $data) = @_;
19              
20 39 50 66     177 if (keys %$data == 1 && exists $data->{value}) {
21 0         0 return $data;
22             }
23             return {
24 39         135 value => $data
25             };
26             }
27              
28             # METHODS
29              
30             sub metadata {
31 48     48 1 80 my ($self, $name) = @_;
32              
33 48 100       104 return $name ? $self->tokens->{metadata}->{$name} : $self->tokens->{metadata};
34             }
35              
36             sub reify {
37 40     40 1 72 my ($self, $name, $data) = @_;
38              
39 40 50       86 return if !$name;
40              
41 40   66     129 my $cache = $self->{'$cache'} ||= $self->service_cache;
42              
43 40 100       92 return $cache->{$name} if $cache->{$name};
44              
45 37         73 my $services = $self->services;
46 37 100       97 my $service = $services->{$name} or return;
47              
48 35 100       84 if (my $extends = $service->{extends}) {
49 4         13 $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       119 : $self->service_reify($service->{argument}));
56              
57 35         70 my $lifecycle = $service->{lifecycle};
58 35 100 100     104 $self->{'$cache'}->{$name} = $value if $lifecycle && $lifecycle eq 'singleton';
59              
60 35         115 return $value;
61             }
62              
63             sub resolve {
64 34     34 1 997 my ($self, $name, $data) = @_;
65              
66 34 100       86 return if !$name;
67              
68 33         107 my $value = $self->get;
69              
70 33 100       174 return exists $value->{$name} ? $value->{$name} : $self->reify($name, $data);
71             }
72              
73             sub service_build {
74 35     35 0 66 my ($self, $service, $argument) = @_;
75              
76 35         78 my $space = $self->service_space($service->{package});
77              
78 35         120 $space->load;
79              
80 35         54 my $construct;
81              
82 35 100       173 if (my $builder = $service->{builder}) {
    100          
    100          
    100          
    100          
83 6         12 my $original;
84              
85 6         13 my $injectables = $argument;
86              
87 6         25 for (my $i=0; $i < @$builder; $i++) {
88 10         332 my $buildspec = $builder->[$i];
89 10         19 my $argument = $buildspec->{argument};
90 10         18 my $argument_as = $buildspec->{argument_as};
91 10         19 my $inject = $buildspec->{inject};
92 10         14 my $return = $buildspec->{return};
93 10   66     41 my $result = $construct || $space->package;
94              
95 10         142 my @arguments;
96              
97 10 100       24 if ($inject) {
98 5         18 @arguments = $self->service_props(
99             $self->service_reify($injectables), $inject
100             );
101             }
102             else {
103 5         16 @arguments = $self->service_props(
104             $self->service_reify($argument), $argument_as
105             );
106             }
107              
108 10 50       40 if (my $function = $buildspec->{function}) {
    50          
    0          
109 0         0 $result = $space->call($function, @arguments);
110             }
111             elsif (my $method = $buildspec->{method}) {
112 10         35 $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       244 if ($return eq 'class') {
122 0         0 $construct = $space->package;
123             }
124 10 100       28 if ($return eq 'result') {
125 6         8 $construct = $result;
126             }
127 10 100       57 if ($return eq 'self') {
128 4   33     22 $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         7 $self->service_props($argument, $service->{argument_as}));
139             }
140             elsif (my $routine = $service->{routine}) {
141             $construct = $space->call($routine, $space->package,
142 1         5 $self->service_props($argument, $service->{argument_as}));
143             }
144             elsif (my $constructor = $service->{constructor}) {
145             $construct = $space->package->$constructor(
146 1         3 $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       61 $self->service_props($argument, $service->{argument_as}))
152             : $space->package;
153             }
154              
155 35         159 return $construct;
156             }
157              
158             sub service_cache {
159 30     30 0 54 my ($self) = @_;
160              
161 30         71 $self->{'$cache'} = {};
162              
163 30         50 my $cache = {};
164 30         80 my $services = $self->services;
165              
166 30         113 for my $name (keys %$services) {
167 42 50       79 next if $cache->{$name};
168              
169 42         79 my $service = $services->{$name};
170 42         58 my $lifecycle = $service->{lifecycle};
171              
172 42 100       98 next if !$lifecycle;
173              
174 2 100       8 if ($lifecycle eq 'eager') {
175 1         4 $cache->{$name} = $self->reify($name);
176             }
177             }
178              
179 30         80 return $cache;
180             }
181              
182             sub service_merge {
183 4     4 0 11 my ($self, $left, $right) = @_;
184              
185 4         8 my $new_service = {};
186              
187 4 50       11 if (my $extends = $right->{extends}) {
188 0         0 $right = $self->service_merge($right, $self->services->{$extends});
189             }
190              
191 4         16 $new_service = {%$right, %$left};
192              
193 4         9 delete $new_service->{extends};
194              
195 4 50 66     15 if ((my $arg1 = $left->{argument}) || (my $arg2 = $right->{argument})) {
196 4 100 66     55 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     12 $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     3 $new_service->{argument} ||= $arg1 if $arg1;
210             }
211             }
212              
213 4         8 return $new_service;
214             }
215              
216             sub service_props {
217 36     36 0 107 my ($self, $prop, $prop_as) = @_;
218              
219 36         55 my @props;
220              
221 36 100 100     135 if ($prop && $prop_as) {
222 7 50 33     46 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     65 if (lc($prop_as) eq 'hash' || lc($prop_as) eq 'hashref') {
231 5 50       17 if (ref $prop eq 'ARRAY') {
232 0         0 @props = ({@$prop});
233             }
234             else {
235 5         10 @props = ($prop);
236             }
237             }
238 7 100       18 if (lc($prop_as) eq 'list') {
239 2 50       8 if (ref $prop eq 'ARRAY') {
    100          
240 0         0 @props = (@$prop);
241             }
242             elsif (ref $prop eq 'HASH') {
243 1         4 @props = (%$prop);
244             }
245             else {
246 1         2 @props = ($prop);
247             }
248             }
249             }
250             else {
251 29 100       61 @props = ($prop) if defined $prop;
252             }
253              
254 36         103 return (@props);
255             }
256              
257             sub service_reify {
258 45     45 0 85 my ($self, $props) = @_;
259              
260 45         78 my $metadata = $self->metadata;
261 45         87 my $services = $self->services;
262              
263 45 50       106 if (ref $props eq 'ARRAY') {
264 0         0 $props = [map $self->service_reify($_), @$props];
265             }
266              
267             # $metadata
268 45 100 100     164 if (ref $props eq 'HASH' && (keys %$props) == 1) {
269 18 100 66     78 if ($props && $props->{'$metadata'}) {
270 8         24 $props = $metadata->{$props->{'$metadata'}};
271             }
272             }
273              
274             # $envvar
275 45 100 100     145 if (ref $props eq 'HASH' && (keys %$props) == 1) {
276 12 100       30 if (my $envvar = $props->{'$envvar'}) {
277 3 50       20 if (exists $ENV{$envvar}) {
    50          
278 0         0 $props = $ENV{$envvar};
279             }
280             elsif (exists $ENV{uc($envvar)}) {
281 3         10 $props = $ENV{uc($envvar)};
282             }
283             else {
284 0         0 $props = undef;
285             }
286             }
287             }
288              
289             # $function
290 45 100 100     113 if (ref $props eq 'HASH' && (keys %$props) == 1) {
291 9 100       22 if ($props->{'$function'}) {
292 1         5 my ($name, $next) = split /#/, $props->{'$function'};
293 1 50 33     6 if ($name && $next) {
294 1 50       4 if (my $resolved = $self->reify($name)) {
295 1 50 33     11 if (Scalar::Util::blessed($resolved)
      33        
296             || (!ref($resolved) && ($resolved =~ /^[a-z-A-Z]/))) {
297 1   33     11 my $space = $self->service_space(ref $resolved || $resolved);
298 1 50 33     9 $props = $space->call($next) if $next && $next =~ /^[a-zA-Z]/;
299             }
300             }
301             }
302             }
303             }
304              
305             # $method
306 45 100 100     127 if (ref $props eq 'HASH' && (keys %$props) == 1) {
307 8 100       18 if ($props->{'$method'}) {
308 1         5 my ($name, $next) = split /#/, $props->{'$method'};
309 1 50 33     6 if ($name && $next) {
310 1 50       3 if (my $resolved = $self->reify($name)) {
311 1 50 33     10 if (Scalar::Util::blessed($resolved)
      33        
312             || (!ref($resolved) && ($resolved =~ /^[a-z-A-Z]/))) {
313 1 50 33     204 $props = $resolved->$next if $next && $next =~ /^[a-zA-Z]/;
314             }
315             }
316             }
317             }
318             }
319              
320             # $routine
321 45 100 100     119 if (ref $props eq 'HASH' && (keys %$props) == 1) {
322 7 100       18 if ($props->{'$routine'}) {
323 1         3 my ($name, $next) = split /#/, $props->{'$routine'};
324 1 50 33     6 if ($name && $next) {
325 1 50       4 if (my $resolved = $self->reify($name)) {
326 1 50 33     10 if (Scalar::Util::blessed($resolved)
      33        
327             || (!ref($resolved) && ($resolved =~ /^[a-z-A-Z]/)))
328             {
329 1   33     6 my $space = $self->service_space(ref $resolved || $resolved);
330 1 50 33     10 $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     120 if (ref $props eq 'HASH' && (keys %$props) == 1) {
340 6 100       15 if (my $callback = $props->{'$callback'}) {
341 1     1   5 $props = sub { $self->reify($callback) };
  1         4  
342             }
343             }
344              
345             # $service
346 45 100 100     123 if (ref $props eq 'HASH' && (keys %$props) == 1) {
347 5 50       12 if ($props->{'$service'}) {
348 0         0 $props = $self->reify($props->{'$service'});
349             }
350             }
351              
352 45 50 66     135 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         110 return $props;
357             }
358              
359             sub service_space {
360 37     37 0 68 my ($self, $name) = @_;
361              
362 37         1008 require Venus::Space;
363              
364 37         182 return Venus::Space->new($name);
365             }
366              
367             sub services {
368 115     115 1 166 my ($self, $name) = @_;
369              
370 115 100       238 return $name ? $self->tokens->{services}->{$name} : $self->tokens->{services};
371             }
372              
373             sub tokens {
374 163     163 0 190 my ($self) = @_;
375              
376             return $self->{'$tokens'} ||= {
377             services => $self->get->{'$services'} || {},
378 163   100     372 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(Str $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(Str $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(Str $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(Str $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 $callback
911              
912             This package supports resolving services as callbacks to be passed around
913             and/or resolved by other services. The C<$callback> directive is used to
914             specify the name of a service to be resolved and passed as an argument.
915              
916             B
917              
918             package main;
919              
920             use Venus::Container;
921              
922             my $container = Venus::Container->new({
923             '$services' => {
924             log => {
925             package => "Venus/Path",
926             argument => '.',
927             },
928             lazy_log => {
929             package => "Venus/Code",
930             argument => {
931             '$callback' => 'log',
932             }
933             }
934             }
935             });
936              
937             # bless(..., 'Venus::Container')
938              
939             # my $result = $container->resolve('lazy_log');
940              
941             # bless(..., 'Venus::Code')
942              
943             # my $return = $result->call;
944              
945             # bless(..., 'Venus::Path')
946              
947             =back
948              
949             =over 4
950              
951             =item $envvar
952              
953             This package supports inlining environment variables as arguments to services.
954             The C<$envvar> directive is used to specify the name of an environment variable,
955             and can also be used in metadata for reusability.
956              
957             B
958              
959             package main;
960              
961             use Venus::Container;
962              
963             my $container = Venus::Container->new({
964             '$services' => {
965             home => {
966             package => "Venus/Path",
967             argument => {
968             '$envvar' => 'home',
969             }
970             }
971             }
972             });
973              
974             # bless(..., 'Venus::Container')
975              
976             # my $result = $container->resolve('home');
977              
978             # bless(..., 'Venus::Path')
979              
980             B
981              
982             package main;
983              
984             use Venus::Container;
985              
986             my $container = Venus::Container->new({
987             '$metadata' => {
988             home => {
989             '$envvar' => 'home',
990             }
991             },
992             '$services' => {
993             home => {
994             package => "Venus/Path",
995             argument => {
996             '$metadata' => 'home',
997             }
998             }
999             }
1000             });
1001              
1002             # bless(..., 'Venus::Container')
1003              
1004             # my $result = $container->resolve('home');
1005              
1006             # bless(..., 'Venus::Path')
1007              
1008             =back
1009              
1010             =over 4
1011              
1012             =item $function
1013              
1014             This package supports inlining the result of a service resolution and function
1015             call as arguments to services. The C<#> delimited C<$function> directive is
1016             used to specify the name of an existing service on the right-hand side, and an
1017             arbitrary function to be call on the result on the left-hand side.
1018              
1019             B
1020              
1021             package main;
1022              
1023             use Venus::Container;
1024              
1025             my $container = Venus::Container->new({
1026             '$services' => {
1027             filespec => {
1028             package => 'File/Spec/Functions',
1029             },
1030             tempdir => {
1031             package => "Venus/Path",
1032             argument => {
1033             '$function' => 'filespec#tmpdir',
1034             }
1035             }
1036             }
1037             });
1038              
1039             # bless(..., 'Venus::Container')
1040              
1041             # my $result = $container->resolve('tempdir');
1042              
1043             # bless(..., 'Venus::Path')
1044              
1045             =back
1046              
1047             =over 4
1048              
1049             =item $metadata
1050              
1051             This package supports inlining configuration data as arguments to services. The
1052             C<$metadata> directive is used to specify the name of a stashed configuration
1053             value or data structure.
1054              
1055             B
1056              
1057             package main;
1058              
1059             use Venus::Container;
1060              
1061             my $container = Venus::Container->new({
1062             '$metadata' => {
1063             home => '/home/ubuntu',
1064             },
1065             '$services' => {
1066             home => {
1067             package => "Venus/Path",
1068             argument => {
1069             '$metadata' => 'home',
1070             }
1071             }
1072             }
1073             });
1074              
1075             # bless(..., 'Venus::Container')
1076              
1077             # my $result = $container->resolve('home');
1078              
1079             # bless(..., 'Venus::Path')
1080              
1081             =back
1082              
1083             =over 4
1084              
1085             =item $method
1086              
1087             This package supports inlining the result of a service resolution and method
1088             call as arguments to services. The C<#> delimited C<$method> directive is used
1089             to specify the name of an existing service on the right-hand side, and an
1090             arbitrary method to be call on the result on the left-hand side.
1091              
1092             B
1093              
1094             package main;
1095              
1096             use Venus::Container;
1097              
1098             my $container = Venus::Container->new({
1099             '$services' => {
1100             filespec => {
1101             package => 'File/Spec',
1102             },
1103             tempdir => {
1104             package => "Venus/Path",
1105             argument => {
1106             '$method' => 'filespec#tmpdir',
1107             }
1108             }
1109             }
1110             });
1111              
1112             # bless(..., 'Venus::Container')
1113              
1114             # my $result = $container->resolve('tempdir');
1115              
1116             # bless(..., 'Venus::Path')
1117              
1118             =back
1119              
1120             =over 4
1121              
1122             =item $routine
1123              
1124             This package supports inlining the result of a service resolution and routine
1125             call as arguments to services. The C<#> delimited C<$routine> directive is used
1126             to specify the name of an existing service on the right-hand side, and an
1127             arbitrary routine to be call on the result on the left-hand side.
1128              
1129             B
1130              
1131             package main;
1132              
1133             use Venus::Container;
1134              
1135             my $container = Venus::Container->new({
1136             '$services' => {
1137             filespec => {
1138             package => 'File/Spec',
1139             },
1140             tempdir => {
1141             package => "Venus/Path",
1142             argument => {
1143             '$routine' => 'filespec#tmpdir',
1144             }
1145             }
1146             }
1147             });
1148              
1149             # bless(..., 'Venus::Container')
1150              
1151             # my $result = $container->resolve('tempdir');
1152              
1153             # bless(..., 'Venus::Path')
1154              
1155             =back
1156              
1157             =over 4
1158              
1159             =item $service
1160              
1161             This package supports inlining resolved services as arguments to other
1162             services. The C<$service> directive is used to specify the name of a service to
1163             be resolved and passed as an argument.
1164              
1165             B
1166              
1167             package main;
1168              
1169             use Venus::Container;
1170              
1171             my $container = Venus::Container->new({
1172             '$services' => {
1173             'path' => {
1174             'package' => 'Venus/Path',
1175             },
1176             }
1177             });
1178              
1179             # bless(..., 'Venus::Container')
1180              
1181             # my $result = $container->resolve('path');
1182              
1183             # bless(..., 'Venus::Path')
1184              
1185             =back
1186              
1187             =over 4
1188              
1189             =item #argument
1190              
1191             This package supports providing static and/or dynamic arguments during object
1192             construction from metadata or other services.
1193              
1194             B
1195              
1196             package main;
1197              
1198             use Venus::Container;
1199              
1200             my $container = Venus::Container->new({
1201             '$services' => {
1202             'date' => {
1203             'package' => 'Venus/Date',
1204             'argument' => 570672000,
1205             },
1206             }
1207             });
1208              
1209             # bless(..., 'Venus::Container')
1210              
1211             # my $result = $container->resolve('date');
1212              
1213             # bless(..., 'Venus::Date')
1214              
1215             B
1216              
1217             package main;
1218              
1219             use Venus::Container;
1220              
1221             my $container = Venus::Container->new({
1222             '$services' => {
1223             'date' => {
1224             'package' => 'Venus/Date',
1225             'argument' => {
1226             year => 1988,
1227             month => 2,
1228             day => 1,
1229             },
1230             },
1231             }
1232             });
1233              
1234             # bless(..., 'Venus::Container')
1235              
1236             # my $result = $container->resolve('date');
1237              
1238             # bless(..., 'Venus::Date')
1239              
1240             =back
1241              
1242             =over 4
1243              
1244             =item #argument_as
1245              
1246             This package supports transforming the way static and/or dynamic arguments are
1247             passed to the operation during object construction. Acceptable options are
1248             C or C (which provides an arrayref), C or C
1249             (which provides a hashref), or C (which provides a flattened list of
1250             arguments).
1251              
1252             B
1253              
1254             package main;
1255              
1256             use Venus::Container;
1257              
1258             my $container = Venus::Container->new({
1259             '$services' => {
1260             'date' => {
1261             'package' => 'Venus/Date',
1262             'argument' => {
1263             year => 1988,
1264             month => 2,
1265             day => 1,
1266             },
1267             argument_as => 'list',
1268             },
1269             }
1270             });
1271              
1272             # bless(..., 'Venus::Container')
1273              
1274             # my $result = $container->resolve('date');
1275              
1276             # bless(..., 'Venus::Date')
1277              
1278             B
1279              
1280             package main;
1281              
1282             use Venus::Container;
1283              
1284             my $container = Venus::Container->new({
1285             '$services' => {
1286             'date' => {
1287             'package' => 'Venus/Date',
1288             'argument' => {
1289             year => 1988,
1290             month => 2,
1291             day => 1,
1292             },
1293             argument_as => 'hash',
1294             },
1295             }
1296             });
1297              
1298             # bless(..., 'Venus::Container')
1299              
1300             # my $result = $container->resolve('date');
1301              
1302             # bless(..., 'Venus::Date')
1303              
1304             =back
1305              
1306             =over 4
1307              
1308             =item #builder
1309              
1310             This package supports specifying multiple build steps as C,
1311             C, and C calls and chaining them together. Each build step
1312             supports any directive that can be used outside of a build step. Each build
1313             step can be configured, with the C directive, to use a particular value
1314             to chain the next subroutine call. Acceptable C values are C
1315             (package name string), C (scalar return value from the current build
1316             step), and C (instantiated package). Additionally, you can use the
1317             C directive (with any value accepted by C) to override the
1318             default arguments using the arguments provided to the L or L
1319             method.
1320              
1321             B
1322              
1323             package main;
1324              
1325             use Venus::Container;
1326              
1327             my $container = Venus::Container->new({
1328             '$services' => {
1329             datetime => {
1330             package => "Venus/Date",
1331             builder => [
1332             {
1333             method => 'new',
1334             argument => 570672000,
1335             return => 'self',
1336             },
1337             {
1338             method => 'string',
1339             return => 'result',
1340             }
1341             ],
1342             }
1343             }
1344             });
1345              
1346             # bless(..., 'Venus::Container')
1347              
1348             # my $result = $container->resolve('datetime');
1349              
1350             # "1988-02-01T00:00:00Z"
1351              
1352             B
1353              
1354             package main;
1355              
1356             use Venus::Container;
1357              
1358             my $container = Venus::Container->new({
1359             '$services' => {
1360             datetime => {
1361             package => "Venus/Date",
1362             builder => [
1363             {
1364             method => 'new',
1365             argument => 570672000,
1366             return => 'self',
1367             inject => 'list',
1368             },
1369             {
1370             method => 'string',
1371             return => 'result',
1372             }
1373             ],
1374             }
1375             }
1376             });
1377              
1378             # bless(..., 'Venus::Container')
1379              
1380             # my $result = $container->resolve('datetime', 604945074);
1381              
1382             # "1989-03-03T16:17:54Z"
1383              
1384             =back
1385              
1386             =over 4
1387              
1388             =item #config
1389              
1390             This package supports configuring services and metadata in the service of
1391             building objects and values.
1392              
1393             B
1394              
1395             package main;
1396              
1397             use Venus::Container;
1398              
1399             my $container = Venus::Container->new({
1400             'name' => 'app',
1401             'secret' => '...',
1402             '$metadata' => {
1403             home => {
1404             '$envvar' => 'home',
1405             }
1406             },
1407             '$services' => {
1408             date => {
1409             package => "Venus/Date",
1410             },
1411             path => {
1412             package => "Venus/Path",
1413             argument => {
1414             '$metadata' => 'home',
1415             },
1416             }
1417             }
1418             });
1419              
1420             # bless(..., 'Venus::Container')
1421              
1422             # my $path = $container->resolve('path');
1423              
1424             # bless(..., 'Venus::Path')
1425              
1426             # my $name = $container->resolve('name');
1427              
1428             # "app"
1429              
1430             =back
1431              
1432             =over 4
1433              
1434             =item #constructor
1435              
1436             This package supports specifying constructors other than the traditional C
1437             routine. A constructor is always called with the package name as the invocant.
1438              
1439             B
1440              
1441             package main;
1442              
1443             use Venus::Container;
1444              
1445             my $container = Venus::Container->new({
1446             '$services' => {
1447             path => {
1448             package => "Venus/Path",
1449             constructor => "new",
1450             }
1451             }
1452             });
1453              
1454             # bless(..., 'Venus::Container')
1455              
1456             # my $result = $container->resolve('path');
1457              
1458             # bless(..., 'Venus::Path')
1459              
1460             =back
1461              
1462             =over 4
1463              
1464             =item #extends
1465              
1466             This package supports extending services in the definition of other services,
1467             recursively compiling service configurations and eventually executing the
1468             requested compiled service.
1469              
1470             B
1471              
1472             package main;
1473              
1474             use Venus::Container;
1475              
1476             my $container = Venus::Container->new({
1477             '$services' => {
1478             log => {
1479             package => "Venus/Log",
1480             argument => {
1481             level => "trace",
1482             },
1483             },
1484             development_log => {
1485             package => "Venus/Log",
1486             extends => "log",
1487             builder => [
1488             {
1489             method => "new",
1490             return => "self",
1491             inject => "hash",
1492             }
1493             ],
1494             },
1495             production_log => {
1496             package => "Venus/Log",
1497             extends => "log",
1498             argument => {
1499             level => "error",
1500             },
1501             builder => [
1502             {
1503             method => "new",
1504             return => "self",
1505             inject => "hash",
1506             }
1507             ],
1508             },
1509             }
1510             });
1511              
1512             # bless(..., 'Venus::Container')
1513              
1514             # my $result = $container->resolve('development_log');
1515              
1516             # bless(..., 'Venus::Log')
1517              
1518             # my $level = $result->level;
1519              
1520             # "trace"
1521              
1522             # $result = $container->resolve('production_log');
1523              
1524             # bless(..., 'Venus::Log')
1525              
1526             # $level = $result->level;
1527              
1528             # "error"
1529              
1530             =back
1531              
1532             =over 4
1533              
1534             =item #function
1535              
1536             This package supports specifying construction as a function call, which when
1537             called does not provide an invocant.
1538              
1539             B
1540              
1541             package main;
1542              
1543             use Venus::Container;
1544              
1545             my $container = Venus::Container->new({
1546             '$services' => {
1547             foo_hex => {
1548             package => "Digest/MD5",
1549             function => "md5_hex",
1550             argument => "foo",
1551             }
1552             }
1553             });
1554              
1555             # bless(..., 'Venus::Container')
1556              
1557             # my $result = $container->resolve('foo_hex');
1558              
1559             # "acbd18db4cc2f85cedef654fccc4a4d8"
1560              
1561             =back
1562              
1563             =over 4
1564              
1565             =item #lifecycle
1566              
1567             This package supports different lifecycle options which determine when services
1568             are built and whether they're persisted. Acceptable lifecycle values are
1569             C (which caches the result once encountered) and C (which
1570             caches the service upon the first execution of any service).
1571              
1572             B
1573              
1574             package main;
1575              
1576             use Venus::Container;
1577              
1578             my $container = Venus::Container->new({
1579             '$services' => {
1580             match => {
1581             package => "Venus/Match",
1582             argument => {
1583             'a'..'h'
1584             },
1585             builder => [
1586             {
1587             method => "new",
1588             return => "result",
1589             },
1590             {
1591             method => "data",
1592             return => "result",
1593             inject => "hash",
1594             }
1595             ],
1596             lifecycle => 'eager',
1597             }
1598             }
1599             });
1600              
1601             # bless(..., 'Venus::Container')
1602              
1603             # my $result = $container->resolve('thing');
1604              
1605             # undef
1606              
1607             # my $result = $container->resolve('match');
1608              
1609             # bless(..., 'Venus::Match')
1610              
1611             B
1612              
1613             package main;
1614              
1615             use Venus::Container;
1616              
1617             my $container = Venus::Container->new({
1618             '$services' => {
1619             match => {
1620             package => "Venus/Match",
1621             argument => {
1622             'a'..'h'
1623             },
1624             builder => [
1625             {
1626             method => "new",
1627             return => "result",
1628             },
1629             {
1630             method => "data",
1631             return => "result",
1632             inject => "hash",
1633             }
1634             ],
1635             lifecycle => 'singleton',
1636             }
1637             }
1638             });
1639              
1640             # bless(..., 'Venus::Container')
1641              
1642             # my $result = $container->resolve('match');
1643              
1644             # bless(..., 'Venus::Match')
1645              
1646             =back
1647              
1648             =over 4
1649              
1650             =item #metadata
1651              
1652             This package supports specifying data and structures which can be used in the
1653             construction of multiple services.
1654              
1655             B
1656              
1657             package main;
1658              
1659             use Venus::Container;
1660              
1661             my $container = Venus::Container->new({
1662             '$metadata' => {
1663             'homedir' => '/home',
1664             'tempdir' => '/tmp',
1665             },
1666             '$services' => {
1667             home => {
1668             package => "Venus/Path",
1669             argument => {
1670             '$metadata' => 'homedir',
1671             },
1672             },
1673             temp => {
1674             package => "Venus/Path",
1675             argument => {
1676             '$metadata' => 'tempdir',
1677             },
1678             },
1679             }
1680             });
1681              
1682             # bless(..., 'Venus::Container')
1683              
1684             # my $result = $container->resolve('home');
1685              
1686             # bless(..., 'Venus::Path')
1687              
1688             # my $result = $container->resolve('temp');
1689              
1690             # bless(..., 'Venus::Path')
1691              
1692             =back
1693              
1694             =over 4
1695              
1696             =item #method
1697              
1698             This package supports specifying construction as a method call, which when
1699             called provides the package or object instance as the invocant.
1700              
1701             B
1702              
1703             package main;
1704              
1705             use Venus::Container;
1706              
1707             my $container = Venus::Container->new({
1708             '$services' => {
1709             date => {
1710             package => "Venus/Date",
1711             argument => 570672000,
1712             method => "new",
1713             }
1714             }
1715             });
1716              
1717             # bless(..., 'Venus::Container')
1718              
1719             # my $result = $container->resolve('date');
1720              
1721             # bless(..., 'Venus::Date')
1722              
1723             =back
1724              
1725             =over 4
1726              
1727             =item #routine
1728              
1729             This package supports specifying construction as a function call, which when
1730             called provides the package as the invocant.
1731              
1732             B
1733              
1734             package main;
1735              
1736             use Venus::Container;
1737              
1738             my $container = Venus::Container->new({
1739             '$services' => {
1740             date => {
1741             package => "Venus/Date",
1742             argument => 570672000,
1743             routine => "new",
1744             }
1745             }
1746             });
1747              
1748             # bless(..., 'Venus::Container')
1749              
1750             # my $result = $container->resolve('date');
1751              
1752             # bless(..., 'Venus::Date')
1753              
1754             =back
1755              
1756             =over 4
1757              
1758             =item #service
1759              
1760             This package supports defining services to be constructed on-demand or
1761             automatically on instantiation.
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, Al Newkirk.
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