File Coverage

blib/lib/Beam/Wire.pm
Criterion Covered Total %
statement 398 430 92.5
branch 155 188 82.4
condition 76 111 68.4
subroutine 49 49 100.0
pod 12 14 85.7
total 690 792 87.1


line stmt bran cond sub pod time code
1             package Beam::Wire;
2             our $VERSION = '1.031';
3             # ABSTRACT: Lightweight Dependency Injection Container
4              
5             #pod =head1 SYNOPSIS
6             #pod
7             #pod # wire.yml
8             #pod captain:
9             #pod class: Person
10             #pod args:
11             #pod name: Malcolm Reynolds
12             #pod rank: Captain
13             #pod first_officer:
14             #pod $class: Person
15             #pod name: Zoë Alleyne Washburne
16             #pod rank: Commander
17             #pod
18             #pod # script.pl
19             #pod use Beam::Wire;
20             #pod my $wire = Beam::Wire->new( file => 'wire.yml' );
21             #pod my $captain = $wire->get( 'captain' );
22             #pod print $captain->name; # "Malcolm Reynolds"
23             #pod
24             #pod =head1 DESCRIPTION
25             #pod
26             #pod Beam::Wire is a configuration module and a dependency injection
27             #pod container. In addition to complex data structures, Beam::Wire configures
28             #pod and creates plain old Perl objects.
29             #pod
30             #pod A dependency injection (DI) container creates an inversion of control:
31             #pod Instead of manually creating all the dependent objects (also called
32             #pod "services") before creating the main object that we actually want, a DI
33             #pod container handles that for us: We describe the relationships between
34             #pod objects, and the objects get built as needed.
35             #pod
36             #pod Dependency injection is sometimes called the opposite of garbage
37             #pod collection. Rather than ensure objects are destroyed in the right order,
38             #pod dependency injection makes sure objects are created in the right order.
39             #pod
40             #pod Using Beam::Wire in your application brings great flexibility,
41             #pod allowing users to easily add their own code to customize how your
42             #pod project behaves.
43             #pod
44             #pod For an L<introduction to the Beam::Wire service configuration format,
45             #pod see Beam::Wire::Help::Config|Beam::Wire::Help::Config>.
46             #pod
47             #pod =cut
48              
49 29     29   4239704 use strict;
  29         69  
  29         3306  
50 29     29   302 use warnings;
  29         2125  
  29         2212  
51              
52 29     29   218 use constant DEBUG => $ENV{BEAM_WIRE_DEBUG};
  29         1891  
  29         7538  
53              
54 29     29   2150 use Scalar::Util qw( blessed );
  29         74  
  29         8636  
55 29     29   17083 use Moo;
  29         287292  
  29         159  
56 29     29   76607 use Config::Any;
  29         409757  
  29         1550  
57 29     29   304 use Module::Runtime qw( use_module );
  29         68  
  29         4418  
58 29     29   30464 use Path::Tiny qw( path );
  29         467070  
  29         2928  
59 29     29   22592 use Types::Standard qw( :all );
  29         4205436  
  29         332  
60 29     29   789063 use if DEBUG, 'Data::Dumper' => qw( Dumper );
  29         63  
  29         1343  
61 29     29   20041 use Beam::Wire::Event::ConfigService;
  29         389  
  29         1571  
62 29     29   18242 use Beam::Wire::Event::BuildService;
  29         131  
  29         200022  
63             with 'Beam::Emitter';
64              
65             #pod =attr file
66             #pod
67             #pod The path of the file where services are configured (typically a YAML
68             #pod file). The file's contents should be a single hashref. The keys are
69             #pod service names, and the values are L<service
70             #pod configurations|Beam::Wire::Help::Config>.
71             #pod
72             #pod =cut
73              
74             has file => (
75             is => 'ro',
76             isa => InstanceOf['Path::Tiny'],
77             coerce => sub {
78             if ( !blessed $_[0] || !$_[0]->isa('Path::Tiny') ) {
79             return path( $_[0] );
80             }
81             return $_[0];
82             },
83             );
84              
85             #pod =attr dir
86             #pod
87             #pod The directory path or paths to use when searching for inner container files.
88             #pod Defaults to using the directory which contains the file specified by the
89             #pod L<file attribute|/file> followed by the C<BEAM_PATH> environment variable
90             #pod (separated by colons C<:>).
91             #pod
92             #pod =cut
93              
94             has dir => (
95             is => 'ro',
96             isa => ArrayRef[InstanceOf['Path::Tiny']],
97             lazy => 1,
98             default => sub {
99             my $dir = [
100             ($_[0]->file ? ($_[0]->file->parent) : ()),
101             ($ENV{BEAM_PATH} ? (map { path($_) } grep !!$_, split /:/, $ENV{BEAM_PATH}) : ()),
102             ];
103             ; print 'Using default paths ', Dumper $dir if DEBUG;
104             return $dir;
105             },
106             coerce => sub {
107             if ( !ref $_[0] ) {
108             return [path( $_[0] )];
109             }
110             if ( ref $_[0] eq 'ARRAY' ) {
111             return [map { blessed( $_ ) && $_->isa('Path::Tiny') ? $_ : path($_) } @{$_[0]}];
112             }
113             return $_[0];
114             },
115             );
116              
117             #pod =attr config
118             #pod
119             #pod The raw configuration data. By default, this data is loaded by
120             #pod L<Config::Any|Config::Any> using the file specified by the L<file attribute|/file>.
121             #pod
122             #pod See L<Beam::Wire::Help::Config for details on what the configuration
123             #pod data structure looks like|Beam::Wire::Help::Config>.
124             #pod
125             #pod If you don't want to load a file, you can specify this attribute in the
126             #pod Beam::Wire constructor.
127             #pod
128             #pod =cut
129              
130             has config => (
131             is => 'ro',
132             isa => HashRef,
133             lazy => 1,
134             builder => 1
135             );
136              
137             sub _build_config {
138 32     32   451 my ( $self ) = @_;
139 32 100       316 return {} if ( !$self->file );
140 27         180 return $self->_load_config( $self->file );
141             }
142              
143             #pod =attr services
144             #pod
145             #pod A hashref of cached services built from the L<configuration|/config>. If
146             #pod you want to inject a pre-built object for other services to depend on,
147             #pod add it here.
148             #pod
149             #pod =cut
150              
151             has services => (
152             is => 'ro',
153             isa => HashRef,
154             lazy => 1,
155             builder => 1,
156             );
157              
158             sub _build_services {
159 113     113   1470 my ( $self ) = @_;
160 113         352 my $services = {};
161 113         2277 return $services;
162             }
163              
164             #pod =attr meta_prefix
165             #pod
166             #pod The character that begins a meta-property inside of a service's C<args>. This
167             #pod includes C<$ref>, C<$class>, C<$method>, and etc...
168             #pod
169             #pod The default value is C<$>. The empty string is allowed.
170             #pod
171             #pod =cut
172              
173             has meta_prefix => (
174             is => 'ro',
175             isa => Str,
176             default => sub { q{$} },
177             );
178              
179             #pod =method get
180             #pod
181             #pod my $service = $wire->get( $name );
182             #pod my $service = $wire->get( $name, %overrides )
183             #pod
184             #pod The get method resolves and returns the service named C<$name>, creating
185             #pod it, if necessary, with L<the create_service method|/create_service>.
186             #pod
187             #pod C<%overrides> is an optional list of name-value pairs. If specified,
188             #pod get() will create an new, anonymous service that extends the named
189             #pod service with the given config overrides. For example:
190             #pod
191             #pod # test.pl
192             #pod use Beam::Wire;
193             #pod my $wire = Beam::Wire->new(
194             #pod config => {
195             #pod foo => {
196             #pod args => {
197             #pod text => 'Hello, World!',
198             #pod },
199             #pod },
200             #pod },
201             #pod );
202             #pod
203             #pod my $foo = $wire->get( 'foo', args => { text => 'Hello, Chicago!' } );
204             #pod print $foo; # prints "Hello, Chicago!"
205             #pod
206             #pod This allows you to create factories out of any service, overriding service
207             #pod configuration at run-time.
208             #pod
209             #pod If C<$name> contains a slash (C</>) character (e.g. C<foo/bar>), the left
210             #pod side (C<foo>) will be used as the name of an inner container, and the
211             #pod right side (C<bar>) is a service inside that container. For example,
212             #pod these two lines are equivalent:
213             #pod
214             #pod $bar = $wire->get( 'foo/bar' );
215             #pod $bar = $wire->get( 'foo' )->get( 'bar' );
216             #pod
217             #pod Inner containers can be nested as deeply as desired (C<foo/bar/baz/fuzz>).
218             #pod
219             #pod =cut
220              
221             sub get {
222 323     323 1 180585 my ( $self, $name, %override ) = @_;
223              
224 323         848 ; print STDERR "Get service: $name\n" if DEBUG;
225              
226 323 100       1459 if ( index( $name, q{/} ) != -1 ) {
227 39         203 my ( $container_name, $service_name ) = split m{/}, $name, 2;
228 39         133 my $container = $self->get( $container_name );
229              
230             # This could be a Beam::Wire container, or it could be a plain hashref.
231             # If it's a hashref, we can automatically create a container and then use it.
232 39 100 33     169 if (ref $container eq 'HASH' && !(blessed $container and $container->isa('Beam::Wire'))) {
      66        
233 4         64 my $inner_container = Beam::Wire->new(
234             config => $container,
235             );
236             # Do not cache the inner container for later use, in case someone
237             # also tries to use the parent as a bare hashref.
238 4         9 $container = $inner_container;
239             }
240 39         173 return $self->_get_from_inner_container($container, $container_name, $service_name, %override);
241             }
242              
243 284 100       958 if ( keys %override ) {
244 4         30 return $self->create_service(
245             "\$anonymous extends $name",
246             %override,
247             extends => $name,
248             );
249             }
250              
251 280         9403 my $service = $self->services->{$name};
252 280 100       5356 if ( !$service ) {
253 174         286 ; printf STDERR 'Service "%s" does not exist. Creating.' . "\n", $name if DEBUG;
254              
255 174         695 my $config_ref = $self->get_config($name);
256 174 100       1860 unless ( $config_ref ) {
257 2         34 Beam::Wire::Exception::NotFound->throw(
258             name => $name,
259             file => $self->file,
260             );
261             }
262              
263 172         270 ; print STDERR "Got service config: " . Dumper( $config_ref ) if DEBUG;
264              
265 172 100 100     1140 if ( ref $config_ref eq 'HASH' && $self->is_meta( $config_ref, 1 ) ) {
266 156         325 my %config = %{ $self->normalize_config( $config_ref ) };
  156         691  
267 156         949 $service = $self->create_service( $name, %config );
268 146 100 100     918 if ( !$config{lifecycle} || lc $config{lifecycle} ne 'factory' ) {
269 141         3771 $self->services->{$name} = $service;
270             }
271             }
272             else {
273 16         44 $self->services->{$name} = $service = $self->find_refs( $name, $config_ref );
274             }
275             }
276              
277 268         1950 ; print STDERR "Returning service: " . Dumper( $service ) if DEBUG;
278              
279 268         1514 return $service;
280             }
281              
282             sub _get_from_inner_container {
283 39     39   133 my ($self, $container, $container_name, $service_name, %override) = @_;
284              
285             # Bubble up the events from the inner container
286             my $unsub_config = $container->on( configure_service => sub {
287 17     17   9499 my ( $event ) = @_;
288 17         158 $self->emit( configure_service =>
289             class => 'Beam::Wire::Event::ConfigService',
290             service_name => join( '/', $container_name, $event->service_name ),
291             config => $event->config,
292             );
293 39         311 } );
294             my $unsub_build = $container->on( build_service => sub {
295 17     17   8937 my ( $event ) = @_;
296 17         150 $self->emit( build_service =>
297             class => 'Beam::Wire::Event::BuildService',
298             service_name => join( '/', $container_name, $event->service_name ),
299             service => $event->service,
300             );
301 39         71606 } );
302              
303 39         3620 my $service = $container->get( $service_name, %override );
304              
305 39         147 $unsub_config->();
306 39         1320 $unsub_build->();
307              
308 39         1156 return $service;
309             }
310              
311             #pod =method set
312             #pod
313             #pod $wire->set( $name => $service );
314             #pod
315             #pod The set method configures and stores the specified C<$service> with the
316             #pod specified C<$name>. Use this to add or replace built services.
317             #pod
318             #pod Like L<the get() method, above|/get>, C<$name> can contain a slash (C</>)
319             #pod character to traverse through nested containers.
320             #pod
321             #pod =cut
322              
323             ## no critic ( ProhibitAmbiguousNames )
324             # This was named set() before I started using Perl::Critic, and will
325             # continue to be named set() now that I no longer use Perl::Critic
326             sub set {
327 2     2 1 26 my ( $self, $name, $service ) = @_;
328 2 100       8 if ( $name =~ q{/} ) {
329 1         4 my ( $container_name, $service_name ) = split m{/}, $name, 2;
330 1         5 return $self->get( $container_name )->set( $service_name, $service );
331             }
332 1         14 $self->services->{$name} = $service;
333 1         6 return;
334             }
335              
336             #pod =method get_config
337             #pod
338             #pod my $conf = $wire->get_config( $name );
339             #pod
340             #pod Get the config with the given C<$name>. Like L<the get() method,
341             #pod above|/get>, C<$name> can contain slash (C</>) characters to traverse
342             #pod through nested containers.
343             #pod
344             #pod =cut
345              
346             sub get_config {
347 236     236 1 42257 my ( $self, $name ) = @_;
348 236 100       948 if ( $name =~ q{/} ) {
349 4         23 my ( $container_name, $service ) = split m{/}, $name, 2;
350 4         9 my %inner_config = %{ $self->get( $container_name )->get_config( $service ) };
  4         16  
351             # Fix relative references to prefix the container name
352 4         60 my ( $fixed_config ) = $self->fix_refs( $container_name, \%inner_config );
353 4         18 return $fixed_config;
354             }
355 232         5342 return $self->config->{$name};
356             }
357              
358             #pod =method normalize_config
359             #pod
360             #pod my $out_conf = $self->normalize_config( $in_conf );
361             #pod
362             #pod Normalize the given C<$in_conf> into to hash that L<the create_service
363             #pod method|/create_service> expects. This method allows a service to be
364             #pod defined with prefixed meta-names (C<$class> instead of C<class>) and
365             #pod the arguments specified without prefixes.
366             #pod
367             #pod For example, these two services are identical.
368             #pod
369             #pod foo:
370             #pod class: Foo
371             #pod args:
372             #pod fizz: buzz
373             #pod
374             #pod foo:
375             #pod $class: Foo
376             #pod fizz: buzz
377             #pod
378             #pod The C<$in_conf> must be a hash, and must already pass L<an is_meta
379             #pod check|/is_meta>.
380             #pod
381             #pod =cut
382              
383             sub normalize_config {
384 201     201 1 541 my ( $self, $conf ) = @_;
385              
386 201         359 ; print STDERR "In conf: " . Dumper( $conf ) if DEBUG;
387              
388 201         737 my %meta = reverse $self->get_meta_names;
389              
390             # Confs without prefixed keys can be used as-is
391 201 100       936 return $conf if !grep { $meta{ $_ } } keys %$conf;
  374         2112  
392              
393 23         45 my %out_conf;
394 23         76 for my $key ( keys %$conf ) {
395 39 100       95 if ( $meta{ $key } ) {
396 30         105 $out_conf{ $meta{ $key } } = $conf->{ $key };
397             }
398             else {
399 9         30 $out_conf{ args }{ $key } = $conf->{ $key };
400             }
401             }
402              
403 23         40 ; print STDERR "Out conf: " . Dumper( \%out_conf ) if DEBUG;
404              
405 23         212 return \%out_conf;
406             }
407              
408             #pod =method create_service
409             #pod
410             #pod my $service = $wire->create_service( $name, %config );
411             #pod
412             #pod Create the service with the given C<$name> and C<%config>. Config can
413             #pod contain the following keys:
414             #pod
415             #pod =over 4
416             #pod
417             #pod =item class
418             #pod
419             #pod The class name of an object to create. Can be combined with C<version>,
420             #pod C<method>, and C<args>. An object of any class can be created with Beam::Wire.
421             #pod
422             #pod =item version
423             #pod
424             #pod The minimum version required for a class based service. Has to be combined with
425             #pod C<class>.
426             #pod
427             #pod =item args
428             #pod
429             #pod The arguments to the constructor method. Used with C<class> and
430             #pod C<method>. Can be a simple value, or a reference to an array or
431             #pod hash which will be dereferenced and passed in to the constructor
432             #pod as a list.
433             #pod
434             #pod If the C<class> consumes the L<Beam::Service role|Beam::Service>,
435             #pod the service's C<name> and C<container> will be added to the C<args>.
436             #pod
437             #pod =item method
438             #pod
439             #pod The method to call to create the object. Only used with C<class>.
440             #pod Defaults to C<"new">.
441             #pod
442             #pod This can also be an array of hashes which describe a list of methods
443             #pod that will be called on the object. The first method should create the
444             #pod object, and each subsequent method can be used to modify the object. The
445             #pod hashes should contain a C<method> key, which is a string containing the
446             #pod method to call, and optionally C<args> and C<return> keys. The C<args>
447             #pod key works like the top-level C<args> key, above. The optional C<return>
448             #pod key can have the special value C<"chain">, which will use the return
449             #pod value from the method as the value for the service (L<The tutorial shows
450             #pod examples of this|Beam::Wire::Help::Config/Multiple Constructor
451             #pod Methods>).
452             #pod
453             #pod If an array is used, the top-level C<args> key is not used.
454             #pod
455             #pod =item value
456             #pod
457             #pod The value of this service. Can be a simple value, or a reference to an
458             #pod array or hash. This value will be simply returned by this method, and is
459             #pod mostly useful when using container files.
460             #pod
461             #pod C<value> can not be used with C<class> or C<extends>.
462             #pod
463             #pod =item ref
464             #pod
465             #pod A reference to another service. This may be paired with C<call> or C<path>.
466             #pod
467             #pod =item config
468             #pod
469             #pod The path to a configuration file, relative to L<the dir attribute|/dir>.
470             #pod The file will be read with L<Config::Any>, and the resulting data
471             #pod structure returned. If the config file does not exist, will return the
472             #pod data in the C<$default> attribute. If the C<$default> attribute does not
473             #pod exist, will return C<undef>.
474             #pod
475             #pod C<config> can not be used with C<class> or C<extends>.
476             #pod
477             #pod =item env
478             #pod
479             #pod Get the value from an environment variable. If the environment variable does not exist, will return the
480             #pod data in the C<$default> attribute. If the C<$default> attribute does not exist, will return C<undef>.
481             #pod
482             #pod C<env> can not be used with C<class> or C<extends>.
483             #pod
484             #pod =item extends
485             #pod
486             #pod The name of a service to extend. The named service's configuration will
487             #pod be merged with this configuration (via L<the merge_config
488             #pod method|/merge_config>).
489             #pod
490             #pod This can be used in place of the C<class> key if the extended configuration
491             #pod contains a class.
492             #pod
493             #pod =item with
494             #pod
495             #pod Compose a role into the object's class before creating the object. This
496             #pod can be a single string, or an array reference of strings which are roles
497             #pod to combine.
498             #pod
499             #pod This uses L<Moo::Role|Moo::Role> and L<the create_class_with_roles
500             #pod method|Role::Tiny/create_class_with_roles>, which should work with any
501             #pod class (as it uses L<the Role::Tiny module|Role::Tiny> under the hood).
502             #pod
503             #pod This can be used with the C<class> key.
504             #pod
505             #pod =item on
506             #pod
507             #pod Attach an event handler to a L<Beam::Emitter subclass|Beam::Emitter>. This
508             #pod is an array of hashes of event names and handlers. A handler is made from
509             #pod a service reference (C<$ref> or an anonymous service), and a subroutine to
510             #pod call on that service (C<$sub>).
511             #pod
512             #pod For example:
513             #pod
514             #pod emitter:
515             #pod class: My::Emitter
516             #pod on:
517             #pod - my_event:
518             #pod $ref: my_handler
519             #pod $sub: on_my_event
520             #pod
521             #pod This can be used with the C<class> key.
522             #pod
523             #pod =back
524             #pod
525             #pod This method uses L<the parse_args method|/parse_args> to parse the C<args> key,
526             #pod L<resolving references|resolve_ref> as needed.
527             #pod
528             #pod =cut
529              
530             sub create_service {
531 175     175 1 16471 my ( $self, $name, %service_info ) = @_;
532              
533 175         339 ; print STDERR "Creating service: " . Dumper( \%service_info ) if DEBUG;
534              
535             # Compose the parent ref into the copy, in case the parent changes
536 175         1603 %service_info = $self->merge_config( %service_info );
537              
538             # value | ref | config and class/extends are mutually exclusive
539             # must check after merge_config in case parent config has class/value
540              
541 173         809 my @classy = grep { exists $service_info{$_} } qw( class extends );
  346         1117  
542 173         467 my @other = grep { exists $service_info{$_} } qw( value ref config env );
  692         1317  
543              
544 173 50       612 if ( @other > 1 ) {
545 0         0 Beam::Wire::Exception::InvalidConfig->throw(
546             name => $name,
547             file => $self->file,
548             error => 'use only one of "value", "ref", "env", or "config"',
549             );
550             }
551              
552 173 100 100     811 if ( @classy && @other ) { # @other == 1
553 6         52 Beam::Wire::Exception::InvalidConfig->throw(
554             name => $name,
555             file => $self->file,
556             error => qq{"$other[0]" cannot be used with "class" or "extends"},
557             );
558             }
559              
560 167 100       583 if ( exists $service_info{value} ) {
561 2         6 return $service_info{value};
562             }
563              
564 165 100       499 if ( exists $service_info{env} ) {
565 2 100 50     37 return exists $ENV{$service_info{env}} ? $ENV{$service_info{env}} : ($service_info{default} // undef);
566             }
567              
568 163 100       485 if ( exists $service_info{ref} ){
569             # at this point the service info is normalized, so none of the
570             # meta keys have a prefix. this will cause resolve_ref some angst,
571             # so de-normalize them
572 1         2 my %meta = $self->get_meta_names;
573 1   33     3 my %de_normalized = map { $meta{$_} // $_ => $service_info{$_} } keys %service_info;
  2         6  
574 1         4 return ( $self->resolve_ref( $name, \%de_normalized ) )[0];
575             }
576              
577 162 100       528 if ( $service_info{config} ) {
578 9         53 my $conf_path = path( $service_info{config} );
579 9 100       637 if ( !$conf_path->is_absolute ) {
580 5         245 $conf_path = $self->_resolve_relative_path($conf_path);
581             }
582 9 100 33     419 if ($service_info{default} && (!$conf_path || !$conf_path->is_file)) {
      66        
583 1         31 return $service_info{default};
584             }
585 8         56 return $self->_load_config( "$conf_path" );
586             }
587              
588 153 100       456 if ( !$service_info{class} ) {
589 2         31 Beam::Wire::Exception::InvalidConfig->throw(
590             name => $name,
591             file => $self->file,
592             error => 'Service configuration incomplete. Missing one of "class", "value", "config", "ref"',
593             );
594             }
595              
596 151         1111 $self->emit( configure_service =>
597             class => 'Beam::Wire::Event::ConfigService',
598             service_name => $name,
599             config => \%service_info,
600             );
601              
602 151 100       165817 $service_info{version} ? use_module( $service_info{class}, $service_info{version} ) : use_module( $service_info{class} );
603              
604 150 100       109324 if ( my $with = $service_info{with} ) {
605 4 100       18 my @roles = ref $with ? @{ $with } : ( $with );
  2         8  
606 4         49 my $class = Moo::Role->create_class_with_roles( $service_info{class}, @roles );
607 4         15771 $service_info{class} = $class;
608             }
609              
610 150   100     939 my $method = $service_info{method} || "new";
611 150         324 my $service;
612 150 100       497 if ( ref $method eq 'ARRAY' ) {
613 2         4 for my $m ( @{$method} ) {
  2         8  
614 4         9 my $method_name = $m->{method};
615 4   100     17 my $return = $m->{return} || q{};
616 4         7 delete $service_info{args};
617 4         19 my @args = $self->parse_args( $name, $service_info{class}, $m->{args} );
618 4 100       13 my $invocant = defined $service ? $service : $service_info{class};
619 4         72 my $output = $invocant->$method_name( @args );
620 4 100 100     230 $service = !defined $service || $return eq 'chain' ? $output
621             : $service;
622             }
623             }
624             else {
625 148         978 my @args = $self->parse_args( $name, @service_info{"class","args","default"} );
626 148 100 66     2570 if ( $service_info{class}->can( 'DOES' ) && $service_info{class}->DOES( 'Beam::Service' ) ) {
627 4         121 push @args, name => $name, container => $self;
628             }
629 148         5001 $service = $service_info{class}->$method( @args );
630             }
631              
632 147 100       61938 if ( $service_info{on} ) {
633 7         28 my %meta = $self->get_meta_names;
634 7         22 my @listeners;
635              
636 7 100       34 if ( ref $service_info{on} eq 'ARRAY' ) {
    50          
637 1         3 @listeners = map { [ %$_ ] } @{ $service_info{on} };
  2         8  
  1         3  
638             }
639             elsif ( ref $service_info{on} eq 'HASH' ) {
640 6         14 for my $event ( keys %{ $service_info{on} } ) {
  6         22  
641 6 100       20 if ( ref $service_info{on}{$event} eq 'ARRAY' ) {
642             push @listeners,
643 2         8 map {; [ $event => $_ ] }
644 1         3 @{ $service_info{on}{$event} };
  1         4  
645             }
646             else {
647 5         19 push @listeners, [ $event => $service_info{on}{$event} ];
648             }
649             }
650             }
651              
652 7         39 for my $listener ( @listeners ) {
653 9         278 my ( $event, $conf ) = @$listener;
654 9 100 66     37 if ( $conf->{ $meta{method} } && !$conf->{ $meta{sub} } ) {
655 1         6 _deprecated( 'warning: (deprecated) "$method" in event handlers is now "$sub" in service "' . $name . '"' );
656             }
657 9   100     47 my $sub_name = delete $conf->{ $meta{sub} } || delete $conf->{ $meta{method} };
658 9         30 my ( $listen_svc ) = $self->find_refs( $name, $conf );
659 9     8   83 $service->on( $event => sub { $listen_svc->$sub_name( @_ ) } );
  8         34808  
660             }
661             }
662              
663 147         70359 $self->emit( build_service =>
664             class => 'Beam::Wire::Event::BuildService',
665             service_name => $name,
666             service => $service,
667             );
668              
669 147         151900 return $service;
670             }
671              
672             #pod =method merge_config
673             #pod
674             #pod my %merged = $wire->merge_config( %config );
675             #pod
676             #pod If C<%config> contains an C<extends> key, merge the extended config together
677             #pod with this one, returning the merged service configuration. This works recursively,
678             #pod so a service can extend a service that extends another service just fine.
679             #pod
680             #pod When merging, hashes are combined, with the child configuration taking
681             #pod precedence. The C<args> key is handled specially to allow a hash of
682             #pod args to be merged. A single element array of args is merged too, if the
683             #pod element is a hash.
684             #pod
685             #pod The configuration returned is a safe copy and can be modified without
686             #pod effecting the original config.
687             #pod
688             #pod =cut
689              
690             sub merge_config {
691 215     215 1 674 my ( $self, %service_info ) = @_;
692 215 100       723 if ( $service_info{ extends } ) {
693 38         127 my $base_config_ref = $self->get_config( $service_info{extends} );
694 38 100       377 unless ( $base_config_ref ) {
695             Beam::Wire::Exception::NotFound->throw(
696             name => $service_info{extends},
697 3         40 file => $self->file,
698             );
699             }
700 35         68 my %base_config = %{ $self->normalize_config( $base_config_ref ) };
  35         99  
701             # Merge the args separately, to be a bit nicer about hashes of arguments
702 35         79 my $args;
703 35 100 100     338 if ( ref $service_info{args} eq 'HASH' && ref $base_config{args} eq 'HASH' ) {
    100 66        
      100        
      66        
      66        
      66        
704 8         21 $args = { %{ delete $base_config{args} }, %{ delete $service_info{args} } };
  8         25  
  8         33  
705 10         142 } elsif ( ref $service_info{args} eq 'ARRAY' && @{ $service_info{args} } == 1 && ref $service_info{args}->[0] eq 'HASH' &&
706 8         67 ref $base_config{args} eq 'ARRAY' && @{ $base_config{args} } == 1 && ref $base_config{args}->[0] eq 'HASH' ) {
707 1         2 $args = [ { %{ delete($base_config{args})->[0] }, %{ delete($service_info{args})->[0] } } ];
  1         5  
  1         5  
708             }
709 35         191 %service_info = ( $self->merge_config( %base_config ), %service_info );
710 35 100       128 if ( $args ) {
711 9         25 $service_info{args} = $args;
712             }
713             }
714 212         922 return %service_info;
715             }
716              
717             #pod =method parse_args
718             #pod
719             #pod my @args = $wire->parse_args( $for_name, $class, $args );
720             #pod
721             #pod Parse the arguments (C<$args>) for the given service (C<$for_name>) with
722             #pod the given class (C<$class>).
723             #pod
724             #pod C<$args> can be an array reference, a hash reference, or a simple
725             #pod scalar. The arguments will be searched for references using L<the
726             #pod find_refs method|/find_refs>, and then a list of arguments will be
727             #pod returned, ready to pass to the object's constructor.
728             #pod
729             #pod Nested containers are handled specially by this method:
730             #pod
731             #pod =over
732             #pod
733             #pod =item * Inner references are not resolved by the parent container.
734             #pod This ensures that references are always relative to the container they're in.
735             #pod
736             #pod =item * If a file is specified but cannot be found, a C<default> can be provided
737             #pod as a fallback.
738             #pod
739             #pod =back
740             #pod
741             #pod =cut
742              
743             # NOTE: Fallback only works on nested Beam::Wire containers right now.
744             # I don't know what one could use to detect one should fall back for
745             # any other kind of service...
746             sub parse_args {
747 152     152 1 534 my ( $self, $for, $class, $args, $fallback ) = @_;
748 152 100       466 return if not $args;
749 122         279 my @args;
750 122 100       679 if ( ref $args eq 'ARRAY' ) {
    100          
751 22         54 @args = $self->find_refs( $for, @{$args} );
  22         104  
752             }
753             elsif ( ref $args eq 'HASH' ) {
754             # Hash args could be a ref
755             # Subcontainers cannot scan for refs in their configs
756 95 100       1076 if ( $class->isa( 'Beam::Wire' ) ) {
757 16         38 my %args = %{$args};
  16         1715  
758 16         62 my $config = delete $args{config};
759             # Subcontainer files should inherit the lookup paths of the
760             # current container, unless overridden.
761 16   33     578 $args{dir} //= $self->dir;
762             # Relative subcontainer files should be looked up from the list of dirs.
763 16 100 100     542 if ( exists $args{file} && !path( $args{file} )->is_absolute ) {
764 9         1191 $args{file} = $self->_resolve_relative_path($args{file}, $args{dir});
765             }
766             # If the file doesn't exist, try to fall back to a default
767 16 100 66     744 if ( exists $args{file} && !($args{file} && path( $args{file} )->is_file) && $fallback ) {
      100        
      100        
768 2         290 delete $args{file};
769 2         13 %args = (%args, %$fallback);
770             }
771 16         870 @args = $self->find_refs( $for, %args );
772 16 100       89 if ( $config ) {
773 2         8 push @args, config => $config;
774             }
775             }
776             else {
777 79         378 my ( $maybe_ref ) = $self->find_refs( $for, $args );
778 79 50       242 if ( blessed $maybe_ref ) {
779 0         0 @args = ( $maybe_ref );
780             }
781             else {
782 79 0       475 @args = ref $maybe_ref eq 'HASH' ? %$maybe_ref
    50          
783             : ref $maybe_ref eq 'ARRAY' ? @$maybe_ref
784             : ( $maybe_ref );
785             }
786             }
787             }
788             else {
789             # Try anyway?
790 5         11 @args = $args;
791             }
792              
793 122         444 return @args;
794             }
795              
796             #pod =method find_refs
797             #pod
798             #pod my @resolved = $wire->find_refs( $for_name, @args );
799             #pod
800             #pod Go through the C<@args> and recursively resolve any references and
801             #pod services found inside, returning the resolved result. References are
802             #pod identified with L<the is_meta method|/is_meta>.
803             #pod
804             #pod If a reference contains a C<$ref> key, it will be resolved by L<the
805             #pod resolve_ref method|/resolve_ref>. Otherwise, the reference will be
806             #pod treated as an anonymous service, and passed directly to L<the
807             #pod create_service method|/create_service>.
808             #pod
809             #pod This is used when L<creating a service|create_service> to ensure all
810             #pod dependencies are created first.
811             #pod
812             #pod =cut
813              
814             sub find_refs {
815 284     284 1 870 my ( $self, $for, @args ) = @_;
816              
817 284         414 ; printf STDERR qq{Searching for refs for "%s": %s}, $for, Dumper( \@args ) if DEBUG;
818              
819 284         434 my @out;
820 284         736 my %meta = $self->get_meta_names;
821 284         1032 for my $arg ( @args ) {
822 456 100       1300 if ( ref $arg eq 'HASH' ) {
    100          
823 146 100       406 if ( $self->is_meta( $arg ) ) {
824 46 100       222 if ( $arg->{ $meta{ ref } } ) {
825 36         234 push @out, $self->resolve_ref( $for, $arg );
826             }
827             else { # Try to treat it as a service to create
828 10         16 ; print STDERR "Creating anonymous service: " . Dumper( $arg ) if DEBUG;
829              
830 10         18 my %service_info = %{ $self->normalize_config( $arg ) };
  10         32  
831 10         108 push @out, $self->create_service( '$anonymous', %service_info );
832             }
833             }
834             else {
835 100         226 push @out, { $self->find_refs( $for, %{$arg} ) };
  100         585  
836             }
837             }
838             elsif ( ref $arg eq 'ARRAY' ) {
839 29         68 push @out, [ map { $self->find_refs( $for, $_ ) } @{$arg} ];
  42         130  
  29         84  
840             }
841             else {
842 281         675 push @out, $arg; # simple scalars
843             }
844             }
845              
846             # In case we only pass in one argument and want one return value
847 284 100       2257 return wantarray ? @out : $out[-1];
848             }
849              
850             #pod =method is_meta
851             #pod
852             #pod my $is_meta = $wire->is_meta( $ref_hash, $root );
853             #pod
854             #pod Returns true if the given hash reference describes some kind of
855             #pod Beam::Wire service. This is used to identify service configuration
856             #pod hashes inside of larger data structures.
857             #pod
858             #pod A service hash reference must contain at least one key, and must either
859             #pod contain a L<prefixed|/meta_prefix> key that could create or reference an
860             #pod object (one of C<class>, C<extends>, C<config>, C<value>, C<env>, or C<ref>) or,
861             #pod if the C<$root> flag exists, be made completely of unprefixed meta keys
862             #pod (as returned by L<the get_meta_names method|/get_meta_names>).
863             #pod
864             #pod The C<$root> flag is used by L<the get method|/get> to allow unprefixed
865             #pod meta keys in the top-level hash values.
866             #pod
867             #pod =cut
868              
869             sub is_meta {
870 334     334 1 2549 my ( $self, $arg, $root ) = @_;
871              
872             # Only a hashref can be meta
873 334 50       962 return unless ref $arg eq 'HASH';
874              
875 334         1187 my @keys = keys %$arg;
876 334 100       770 return unless @keys;
877              
878 331         919 my %meta = $self->get_meta_names;
879              
880             # A regular service does not need the prefix, but must consist
881             # only of meta keys
882 331 100 100     1645 return 1 if $root && scalar @keys eq grep { $meta{ $_ } } @keys;
  325         2248  
883              
884             # A meta service contains at least one of these keys, as these are
885             # the keys that can create a service. All other keys are
886             # modifiers
887             return 1
888 1068         2401 if grep { exists $arg->{ $_ } }
889 178 100       489 map { $meta{ $_ } }
  1068         2226  
890             qw( ref class extends config value env );
891              
892             # Must not be meta
893 112         754 return;
894             }
895              
896             #pod =method get_meta_names
897             #pod
898             #pod my %meta_keys = $wire->get_meta_names;
899             #pod
900             #pod Get all the possible service keys with the L<meta prefix|/meta_prefix> already
901             #pod attached.
902             #pod
903             #pod =cut
904              
905             sub get_meta_names {
906 1004     1004 1 1879 my ( $self ) = @_;
907 1004         2494 my $prefix = $self->meta_prefix;
908 1004         13131 my %meta = (
909             ref => "${prefix}ref",
910             path => "${prefix}path",
911             method => "${prefix}method",
912             args => "${prefix}args",
913             class => "${prefix}class",
914             version => "${prefix}version",
915             extends => "${prefix}extends",
916             sub => "${prefix}sub",
917             call => "${prefix}call",
918             lifecycle => "${prefix}lifecycle",
919             on => "${prefix}on",
920             with => "${prefix}with",
921             value => "${prefix}value",
922             config => "${prefix}config",
923             env => "${prefix}env",
924             default => "${prefix}default",
925             );
926 1004 50       15456 return wantarray ? %meta : \%meta;
927             }
928              
929             #pod =method resolve_ref
930             #pod
931             #pod my @value = $wire->resolve_ref( $for_name, $ref_hash );
932             #pod
933             #pod Resolves the given dependency from the configuration hash (C<$ref_hash>)
934             #pod for the named service (C<$for_name>). Reference hashes contain the
935             #pod following keys:
936             #pod
937             #pod =over 4
938             #pod
939             #pod =item $ref
940             #pod
941             #pod The name of a service in the container. Required.
942             #pod
943             #pod =item $path (deprecated)
944             #pod
945             #pod B<Deprecated>: Bare hashrefs can be treated as child containers with C<$ref>
946             #pod now. If there are other uses of C<$path> that cannot be updated to C<$ref>,
947             #pod please L<open a GitHub issue to discuss it|https://github.com/preaction/Beam-Wire/issues>.
948             #pod
949             #pod A data path to pick some data out of the reference. Useful with C<value>
950             #pod and C<config> services.
951             #pod
952             #pod # container.yml
953             #pod bounties:
954             #pod value:
955             #pod malcolm: 50000
956             #pod zoe: 35000
957             #pod simon: 100000
958             #pod
959             #pod captain:
960             #pod class: Person
961             #pod args:
962             #pod name: Malcolm Reynolds
963             #pod bounty:
964             #pod $ref: bounties
965             #pod $path: /malcolm
966             #pod
967             #pod =item $call
968             #pod
969             #pod Call a method on the referenced object and use the resulting value. This
970             #pod may be a string, which will be the method name to call, or a hash with
971             #pod C<$method> and C<$args>, which are the method name to call and the
972             #pod arguments to that method, respectively.
973             #pod
974             #pod captain:
975             #pod class: Person
976             #pod args:
977             #pod name: Malcolm Reynolds
978             #pod location:
979             #pod $ref: beacon
980             #pod $call: get_location
981             #pod bounty:
982             #pod $ref: news
983             #pod $call:
984             #pod $method: get_bounty
985             #pod $args:
986             #pod name: mreynolds
987             #pod
988             #pod =back
989             #pod
990             #pod =cut
991              
992             sub resolve_ref {
993 37     37 1 141 my ( $self, $for, $arg ) = @_;
994              
995 37         111 my %meta = $self->get_meta_names;
996              
997 37         102 my @ref;
998 37         119 my $name = $arg->{ $meta{ref} };
999 37         179 my $service = $self->get( $name );
1000             # resolve service ref w/path
1001 37 50       331 if ( my $path = $arg->{ $meta{path} } ) {
    100          
    100          
1002             # This was not nearly as useful as I had hoped... It would be better,
1003             # much better, to have $ref handle these kind of things instead.
1004 0         0 _deprecated('warning: (deprecated) Using $path is deprecated to remove the dependency on Data::DPath in service "' . $for . '"' );
1005             # locate foreign service data
1006 0         0 use_module( 'Data::DPath' )->import('dpath');
1007 0         0 @ref = dpath( $path )->match($service);
1008             }
1009             elsif ( my $call = $arg->{ $meta{call} } ) {
1010 4         10 my ( $method, @args );
1011              
1012 4 100       13 if ( ref $call eq 'HASH' ) {
1013 2         7 $method = $call->{ $meta{method} };
1014 2         6 my $args = $call->{ $meta{args} };
1015             @args = !$args ? ()
1016 2 100       11 : ref $args eq 'ARRAY' ? @{ $args }
  1 50       5  
1017             : $args;
1018             }
1019             else {
1020 2         4 $method = $call;
1021             }
1022              
1023 4         20 @ref = $service->$method( @args );
1024             }
1025             elsif ( my $method = $arg->{ $meta{method} } ) {
1026 3         19 _deprecated( 'warning: (deprecated) Using "$method" to get a value in a dependency is now "$call" in service "' . $for . '"' );
1027 3         27 my $args = $arg->{ $meta{args} };
1028             my @args = !$args ? ()
1029 3 100       19 : ref $args eq 'ARRAY' ? @{ $args }
  1 100       5  
1030             : $args;
1031 3         27 @ref = $service->$method( @args );
1032             }
1033             else {
1034 30         101 @ref = $service;
1035             }
1036              
1037 37         433 return @ref;
1038             }
1039              
1040             #pod =method fix_refs
1041             #pod
1042             #pod my @fixed = $wire->fix_refs( $for_container_name, @args );
1043             #pod
1044             #pod Similar to L<the find_refs method|/find_refs>. This method searches
1045             #pod through the C<@args> and recursively fixes any reference paths to be
1046             #pod absolute. References are identified with L<the is_meta
1047             #pod method|/is_meta>.
1048             #pod
1049             #pod This is used by L<the get_config method|/get_config> to ensure that the
1050             #pod configuration can be passed directly in to L<the create_service
1051             #pod method|create_service>.
1052             #pod
1053             #pod =cut
1054              
1055             sub fix_refs {
1056 19     19 1 42 my ( $self, $container_name, @args ) = @_;
1057 19         26 my @out;
1058 19         36 my %meta = $self->get_meta_names;
1059 19         48 for my $arg ( @args ) {
1060 22 100       64 if ( ref $arg eq 'HASH' ) {
    100          
1061 9 100       28 if ( $self->is_meta( $arg, 1 ) ) {
1062             #; print STDERR 'Fixing refs for arg: ' . Dumper( $arg );
1063 6         22 my %new = %$arg;
1064 6         20 for my $key ( keys %new ) {
1065 10 100       63 if ( $key =~ /(?:ref|extends)$/ ) {
1066 3         17 $new{ $key } = join( q{/}, $container_name, $new{$key} );
1067             }
1068             else {
1069 7         26 ( $new{ $key } ) = $self->fix_refs( $container_name, $new{ $key } );
1070             }
1071             }
1072             #; print STDERR 'Fixed refs for arg: ' . Dumper( \%new );
1073 6         17 push @out, \%new;
1074             }
1075             else {
1076 3         10 push @out, { $self->fix_refs( $container_name, %{$arg} ) };
  3         76  
1077             }
1078             }
1079             elsif ( ref $arg eq 'ARRAY' ) {
1080 3         5 push @out, [ map { $self->fix_refs( $container_name, $_ ) } @{$arg} ];
  5         7  
  3         6  
1081             }
1082             else {
1083 10         21 push @out, $arg; # simple scalars
1084             }
1085             }
1086 19         79 return @out;
1087             }
1088              
1089              
1090             #pod =method new
1091             #pod
1092             #pod my $wire = Beam::Wire->new( %attributes );
1093             #pod
1094             #pod Create a new container.
1095             #pod
1096             #pod =cut
1097              
1098             sub BUILD {
1099 127     127 0 4196 my ( $self ) = @_;
1100              
1101 127 100 100     1240 if ( $self->file && !path( $self->file )->exists ) {
1102 3         184 my $file = $self->file;
1103 3         16 Beam::Wire::Exception::Constructor->throw(
1104             attr => 'file',
1105             error => qq{Container file '$file' does not exist},
1106             );
1107             }
1108              
1109             # Create all the eager services
1110 124         2608 my %meta = $self->get_meta_names;
1111 124         477 for my $key ( keys %{ $self->config } ) {
  124         3705  
1112 213         6657 my $config = $self->config->{$key};
1113 213 100       2030 if ( ref $config eq 'HASH' ) {
1114 203   66     1173 my $lifecycle = $config->{lifecycle} || $config->{ $meta{lifecycle} };
1115 203 100 100     891 if ( $lifecycle && $lifecycle eq 'eager' ) {
1116 1         5 $self->get($key);
1117             }
1118             }
1119             }
1120 123         1840 return;
1121             }
1122              
1123             my %deprecated_warnings;
1124             sub _deprecated {
1125 4     4   11 my ( $warning ) = @_;
1126 4 50       17 return if $deprecated_warnings{ $warning };
1127 4         83 warn $deprecated_warnings{ $warning } = $warning . "\n";
1128             }
1129              
1130             # Load a config file
1131             sub _load_config {
1132 35     35   184 my ( $self, $path ) = @_;
1133 35         113 local $Config::Any::YAML::NO_YAML_XS_WARNING = 1;
1134              
1135 35         63 my $loader;
1136 35         70 eval {
1137 35         780 $loader = Config::Any->load_files( {
1138             files => [$path], use_ext => 1, flatten_to_hash => 1
1139             } );
1140             };
1141 35 100       675686 if ( $@ ) {
1142 1         24 Beam::Wire::Exception::Config->throw(
1143             file => $self->file,
1144             config_error => $@,
1145             );
1146             }
1147              
1148 34 50       222 return "HASH" eq ref $loader ? (values(%{$loader}))[0] : {};
  34         1505  
1149             }
1150              
1151             sub _resolve_relative_path {
1152 14     14   49 my ( $self, $file, $dirs ) = @_;
1153 14   66     229 $dirs //= $self->dir;
1154 14         173 ; printf STDERR qq{Searching for path '%s' in %s}, $file, Dumper( $dirs ) if DEBUG;
1155 14         36 for my $dir ( @{ $dirs } ) {
  14         54  
1156 11 50 33     328 $dir = !(blessed $dir && $dir->isa('Path::Tiny')) ? path($dir) : $dir;
1157 11 100       105 if ($dir->child($file)->exists) {
1158 9         980 return $dir->child( $file );
1159             }
1160             }
1161             # Allow the file to fall through so we get an error message with
1162             # the relative filename that we tried looking up.
1163 5         121 return $file;
1164             }
1165              
1166             # Check config file for known issues and report
1167             # Optionally attempt to get all configured items for complete test
1168             # Intended for use with beam-wire script
1169             sub validate {
1170 4     4 0 2347 my $error_count = 0;
1171 4         19 my @valid_dependency_nodes = qw( class method args extends lifecycle on config );
1172 4         14 my ( $self, $instantiate, $show_all_errors ) = @_;
1173              
1174 4         8 while ( my ( $name, $v ) = each %{ $self->{config} } ) {
  5         29  
1175              
1176 5 50       14 if ($instantiate) {
1177 0 0       0 if ($show_all_errors) {
1178 0         0 eval {
1179 0         0 $self->get($name);
1180             };
1181 0 0       0 print $@ if $@;
1182             }
1183             else {
1184 0         0 $self->get($name);
1185             }
1186 0         0 next;
1187             };
1188              
1189 5         9 my %config = %{ $self->get_config($name) };
  5         16  
1190 5         80 %config = $self->merge_config(%config);
1191              
1192 4         14 my @classy = grep { exists $config{$_} } qw( class extends );
  8         24  
1193 4         9 my @other = grep { exists $config{$_} } qw( value ref config );
  12         28  
1194              
1195 4 50       13 if ( @other > 1 ) {
1196 0         0 $error_count++;
1197 0         0 my $error = 'use only one of "value", "ref", or "config"';
1198              
1199 0 0       0 if ($show_all_errors) {
1200 0         0 print qq(Invalid config for service '$name': $error\n);
1201 0         0 next;
1202             }
1203              
1204             Beam::Wire::Exception::InvalidConfig->throw(
1205 0         0 name => $name,
1206             file => $self->file,
1207             error => $error,
1208             );
1209             }
1210              
1211 4 100 66     37 if ( @classy && @other ) { # @other == 1
1212 3         6 $error_count++;
1213 3         10 my $error = qq{"$other[0]" cannot be used with "class" or "extends"};
1214              
1215 3 50       8 if ($show_all_errors) {
1216 0         0 print qq(Invalid config for service '$name': $error\n);
1217 0         0 next;
1218             }
1219              
1220             Beam::Wire::Exception::InvalidConfig->throw(
1221 3         20 name => $name,
1222             file => $self->file,
1223             error => $error,
1224             );
1225             }
1226              
1227 1 50 33     13 if ( exists $config{value} && ( exists $config{class} || exists $config{extends})) {
      33        
1228 0         0 $error_count++;
1229 0 0       0 if ($show_all_errors) {
1230 0         0 print qq(Invalid config for service '$name': "value" cannot be used with "class" or "extends"\n);
1231 0         0 next;
1232             }
1233              
1234             Beam::Wire::Exception::InvalidConfig->throw(
1235 0         0 name => $name,
1236             file => $self->file,
1237             error => '"value" cannot be used with "class" or "extends"',
1238             );
1239             }
1240              
1241 1 50       6 if ( $config{config} ) {
1242 0         0 my $conf_path = path( $config{config} );
1243 0 0       0 if ( $self->file ) {
1244 0         0 $conf_path = path( $self->file )->parent->child($conf_path);
1245             }
1246 0         0 %config = %{ $self->_load_config("$conf_path") };
  0         0  
1247             }
1248              
1249 1 0 33     4 unless ( $config{value} || $config{class} || $config{extends} || $config{ref} ) {
      0        
      0        
1250 0         0 next;
1251             }
1252              
1253 1 50       5 if ($config{class}) {
1254 0 0       0 eval "require " . $config{class} if $config{class};
1255             }
1256             #TODO: check method chain & serial
1257             }
1258 0           return $error_count;
1259             }
1260              
1261             #pod =head1 EXCEPTIONS
1262             #pod
1263             #pod If there is an error internal to Beam::Wire, an exception will be thrown. If there is an
1264             #pod error with creating a service or calling a method, the exception thrown will be passed-
1265             #pod through unaltered.
1266             #pod
1267             #pod =head2 Beam::Wire::Exception
1268             #pod
1269             #pod The base exception class
1270             #pod
1271             #pod =cut
1272              
1273             package Beam::Wire::Exception;
1274 29     29   341 use Moo;
  29         65  
  29         244  
1275             with 'Throwable';
1276 29     29   17089 use Types::Standard qw( :all );
  29         69  
  29         259  
1277 29     29   857021 use overload q{""} => sub { $_[0]->error };
  29     10   69  
  29         419  
  10         26116  
1278              
1279             has error => (
1280             is => 'ro',
1281             isa => Str,
1282             );
1283              
1284             #pod =head2 Beam::Wire::Exception::Constructor
1285             #pod
1286             #pod An exception creating a Beam::Wire object
1287             #pod
1288             #pod =cut
1289              
1290             package Beam::Wire::Exception::Constructor;
1291 29     29   3729 use Moo;
  29         68  
  29         257  
1292 29     29   16679 use Types::Standard qw( :all );
  29         85  
  29         199  
1293             extends 'Beam::Wire::Exception';
1294              
1295             has attr => (
1296             is => 'ro',
1297             isa => Str,
1298             required => 1,
1299             );
1300              
1301             #pod =head2 Beam::Wire::Exception::Config
1302             #pod
1303             #pod An exception loading the configuration file.
1304             #pod
1305             #pod =cut
1306              
1307             package Beam::Wire::Exception::Config;
1308 29     29   809559 use Moo;
  29         72  
  29         249  
1309 29     29   16720 use Types::Standard qw( :all );
  29         68  
  29         193  
1310             extends 'Beam::Wire::Exception';
1311              
1312             has file => (
1313             is => 'ro',
1314             isa => Maybe[InstanceOf['Path::Tiny']],
1315             );
1316              
1317             has config_error => (
1318             is => 'ro',
1319             isa => Str,
1320             required => 1,
1321             );
1322              
1323             has '+error' => (
1324             lazy => 1,
1325             default => sub {
1326             my ( $self ) = @_;
1327             return sprintf 'Could not load container file "%s": Error from config parser: %s',
1328             $self->file,
1329             $self->config_error;
1330             },
1331             );
1332              
1333             #pod =head2 Beam::Wire::Exception::Service
1334             #pod
1335             #pod An exception with service information inside
1336             #pod
1337             #pod =cut
1338              
1339             package Beam::Wire::Exception::Service;
1340 29     29   813740 use Moo;
  29         96  
  29         294  
1341 29     29   18734 use Types::Standard qw( :all );
  29         123  
  29         247  
1342             extends 'Beam::Wire::Exception';
1343              
1344             has name => (
1345             is => 'ro',
1346             isa => Str,
1347             required => 1,
1348             );
1349              
1350             has file => (
1351             is => 'ro',
1352             isa => Maybe[InstanceOf['Path::Tiny']],
1353             );
1354              
1355             #pod =head2 Beam::Wire::Exception::NotFound
1356             #pod
1357             #pod The requested service or configuration was not found.
1358             #pod
1359             #pod =cut
1360              
1361             package Beam::Wire::Exception::NotFound;
1362 29     29   778490 use Moo;
  29         493  
  29         251  
1363             extends 'Beam::Wire::Exception::Service';
1364              
1365             has '+error' => (
1366             lazy => 1,
1367             default => sub {
1368             my ( $self ) = @_;
1369             my $name = $self->name;
1370             my $file = $self->file;
1371             return "Service '$name' not found" . ( $file ? " in file '$file'" : '' );
1372             },
1373             );
1374              
1375             #pod =head2 Beam::Wire::Exception::InvalidConfig
1376             #pod
1377             #pod The configuration is invalid:
1378             #pod
1379             #pod =over 4
1380             #pod
1381             #pod =item *
1382             #pod
1383             #pod Both "value" and "class" or "extends" are defined. These are mutually-exclusive.
1384             #pod
1385             #pod =back
1386             #pod
1387             #pod =cut
1388              
1389             package Beam::Wire::Exception::InvalidConfig;
1390 29     29   20340 use Moo;
  29         61  
  29         166  
1391             extends 'Beam::Wire::Exception::Service';
1392             use overload q{""} => sub {
1393 7     7   21922 my ( $self ) = @_;
1394 7         28 my $file = $self->file;
1395              
1396 7 100       79 sprintf "Invalid config for service '%s': %s%s",
1397             $self->name,
1398             $self->error,
1399             ( $file ? " in file '$file'" : "" ),
1400             ;
1401 29     29   16545 };
  29         81  
  29         382  
1402              
1403             #pod =head1 EVENTS
1404             #pod
1405             #pod The container emits the following events.
1406             #pod
1407             #pod =head2 configure_service
1408             #pod
1409             #pod This event is emitted when a new service is configured, but before it is
1410             #pod instantiated or any classes loaded. This allows altering of the
1411             #pod configuration before the service is built. Already-built services will
1412             #pod not fire this event.
1413             #pod
1414             #pod Event handlers get a L<Beam::Wire::Event::ConfigService> object as their
1415             #pod only argument.
1416             #pod
1417             #pod This event will bubble up from child containers.
1418             #pod
1419             #pod =head2 build_service
1420             #pod
1421             #pod This event is emitted when a new service is built. Cached services will
1422             #pod not fire this event.
1423             #pod
1424             #pod Event handlers get a L<Beam::Wire::Event::BuildService> object as their
1425             #pod only argument.
1426             #pod
1427             #pod This event will bubble up from child containers.
1428             #pod
1429             #pod =cut
1430              
1431             1;
1432              
1433             __END__
1434              
1435             =pod
1436              
1437             =encoding UTF-8
1438              
1439             =head1 NAME
1440              
1441             Beam::Wire - Lightweight Dependency Injection Container
1442              
1443             =head1 VERSION
1444              
1445             version 1.031
1446              
1447             =head1 SYNOPSIS
1448              
1449             # wire.yml
1450             captain:
1451             class: Person
1452             args:
1453             name: Malcolm Reynolds
1454             rank: Captain
1455             first_officer:
1456             $class: Person
1457             name: Zoë Alleyne Washburne
1458             rank: Commander
1459              
1460             # script.pl
1461             use Beam::Wire;
1462             my $wire = Beam::Wire->new( file => 'wire.yml' );
1463             my $captain = $wire->get( 'captain' );
1464             print $captain->name; # "Malcolm Reynolds"
1465              
1466             =head1 DESCRIPTION
1467              
1468             Beam::Wire is a configuration module and a dependency injection
1469             container. In addition to complex data structures, Beam::Wire configures
1470             and creates plain old Perl objects.
1471              
1472             A dependency injection (DI) container creates an inversion of control:
1473             Instead of manually creating all the dependent objects (also called
1474             "services") before creating the main object that we actually want, a DI
1475             container handles that for us: We describe the relationships between
1476             objects, and the objects get built as needed.
1477              
1478             Dependency injection is sometimes called the opposite of garbage
1479             collection. Rather than ensure objects are destroyed in the right order,
1480             dependency injection makes sure objects are created in the right order.
1481              
1482             Using Beam::Wire in your application brings great flexibility,
1483             allowing users to easily add their own code to customize how your
1484             project behaves.
1485              
1486             For an L<introduction to the Beam::Wire service configuration format,
1487             see Beam::Wire::Help::Config|Beam::Wire::Help::Config>.
1488              
1489             =head1 ATTRIBUTES
1490              
1491             =head2 file
1492              
1493             The path of the file where services are configured (typically a YAML
1494             file). The file's contents should be a single hashref. The keys are
1495             service names, and the values are L<service
1496             configurations|Beam::Wire::Help::Config>.
1497              
1498             =head2 dir
1499              
1500             The directory path or paths to use when searching for inner container files.
1501             Defaults to using the directory which contains the file specified by the
1502             L<file attribute|/file> followed by the C<BEAM_PATH> environment variable
1503             (separated by colons C<:>).
1504              
1505             =head2 config
1506              
1507             The raw configuration data. By default, this data is loaded by
1508             L<Config::Any|Config::Any> using the file specified by the L<file attribute|/file>.
1509              
1510             See L<Beam::Wire::Help::Config for details on what the configuration
1511             data structure looks like|Beam::Wire::Help::Config>.
1512              
1513             If you don't want to load a file, you can specify this attribute in the
1514             Beam::Wire constructor.
1515              
1516             =head2 services
1517              
1518             A hashref of cached services built from the L<configuration|/config>. If
1519             you want to inject a pre-built object for other services to depend on,
1520             add it here.
1521              
1522             =head2 meta_prefix
1523              
1524             The character that begins a meta-property inside of a service's C<args>. This
1525             includes C<$ref>, C<$class>, C<$method>, and etc...
1526              
1527             The default value is C<$>. The empty string is allowed.
1528              
1529             =head1 METHODS
1530              
1531             =head2 get
1532              
1533             my $service = $wire->get( $name );
1534             my $service = $wire->get( $name, %overrides )
1535              
1536             The get method resolves and returns the service named C<$name>, creating
1537             it, if necessary, with L<the create_service method|/create_service>.
1538              
1539             C<%overrides> is an optional list of name-value pairs. If specified,
1540             get() will create an new, anonymous service that extends the named
1541             service with the given config overrides. For example:
1542              
1543             # test.pl
1544             use Beam::Wire;
1545             my $wire = Beam::Wire->new(
1546             config => {
1547             foo => {
1548             args => {
1549             text => 'Hello, World!',
1550             },
1551             },
1552             },
1553             );
1554              
1555             my $foo = $wire->get( 'foo', args => { text => 'Hello, Chicago!' } );
1556             print $foo; # prints "Hello, Chicago!"
1557              
1558             This allows you to create factories out of any service, overriding service
1559             configuration at run-time.
1560              
1561             If C<$name> contains a slash (C</>) character (e.g. C<foo/bar>), the left
1562             side (C<foo>) will be used as the name of an inner container, and the
1563             right side (C<bar>) is a service inside that container. For example,
1564             these two lines are equivalent:
1565              
1566             $bar = $wire->get( 'foo/bar' );
1567             $bar = $wire->get( 'foo' )->get( 'bar' );
1568              
1569             Inner containers can be nested as deeply as desired (C<foo/bar/baz/fuzz>).
1570              
1571             =head2 set
1572              
1573             $wire->set( $name => $service );
1574              
1575             The set method configures and stores the specified C<$service> with the
1576             specified C<$name>. Use this to add or replace built services.
1577              
1578             Like L<the get() method, above|/get>, C<$name> can contain a slash (C</>)
1579             character to traverse through nested containers.
1580              
1581             =head2 get_config
1582              
1583             my $conf = $wire->get_config( $name );
1584              
1585             Get the config with the given C<$name>. Like L<the get() method,
1586             above|/get>, C<$name> can contain slash (C</>) characters to traverse
1587             through nested containers.
1588              
1589             =head2 normalize_config
1590              
1591             my $out_conf = $self->normalize_config( $in_conf );
1592              
1593             Normalize the given C<$in_conf> into to hash that L<the create_service
1594             method|/create_service> expects. This method allows a service to be
1595             defined with prefixed meta-names (C<$class> instead of C<class>) and
1596             the arguments specified without prefixes.
1597              
1598             For example, these two services are identical.
1599              
1600             foo:
1601             class: Foo
1602             args:
1603             fizz: buzz
1604              
1605             foo:
1606             $class: Foo
1607             fizz: buzz
1608              
1609             The C<$in_conf> must be a hash, and must already pass L<an is_meta
1610             check|/is_meta>.
1611              
1612             =head2 create_service
1613              
1614             my $service = $wire->create_service( $name, %config );
1615              
1616             Create the service with the given C<$name> and C<%config>. Config can
1617             contain the following keys:
1618              
1619             =over 4
1620              
1621             =item class
1622              
1623             The class name of an object to create. Can be combined with C<version>,
1624             C<method>, and C<args>. An object of any class can be created with Beam::Wire.
1625              
1626             =item version
1627              
1628             The minimum version required for a class based service. Has to be combined with
1629             C<class>.
1630              
1631             =item args
1632              
1633             The arguments to the constructor method. Used with C<class> and
1634             C<method>. Can be a simple value, or a reference to an array or
1635             hash which will be dereferenced and passed in to the constructor
1636             as a list.
1637              
1638             If the C<class> consumes the L<Beam::Service role|Beam::Service>,
1639             the service's C<name> and C<container> will be added to the C<args>.
1640              
1641             =item method
1642              
1643             The method to call to create the object. Only used with C<class>.
1644             Defaults to C<"new">.
1645              
1646             This can also be an array of hashes which describe a list of methods
1647             that will be called on the object. The first method should create the
1648             object, and each subsequent method can be used to modify the object. The
1649             hashes should contain a C<method> key, which is a string containing the
1650             method to call, and optionally C<args> and C<return> keys. The C<args>
1651             key works like the top-level C<args> key, above. The optional C<return>
1652             key can have the special value C<"chain">, which will use the return
1653             value from the method as the value for the service (L<The tutorial shows
1654             examples of this|Beam::Wire::Help::Config/Multiple Constructor
1655             Methods>).
1656              
1657             If an array is used, the top-level C<args> key is not used.
1658              
1659             =item value
1660              
1661             The value of this service. Can be a simple value, or a reference to an
1662             array or hash. This value will be simply returned by this method, and is
1663             mostly useful when using container files.
1664              
1665             C<value> can not be used with C<class> or C<extends>.
1666              
1667             =item ref
1668              
1669             A reference to another service. This may be paired with C<call> or C<path>.
1670              
1671             =item config
1672              
1673             The path to a configuration file, relative to L<the dir attribute|/dir>.
1674             The file will be read with L<Config::Any>, and the resulting data
1675             structure returned. If the config file does not exist, will return the
1676             data in the C<$default> attribute. If the C<$default> attribute does not
1677             exist, will return C<undef>.
1678              
1679             C<config> can not be used with C<class> or C<extends>.
1680              
1681             =item env
1682              
1683             Get the value from an environment variable. If the environment variable does not exist, will return the
1684             data in the C<$default> attribute. If the C<$default> attribute does not exist, will return C<undef>.
1685              
1686             C<env> can not be used with C<class> or C<extends>.
1687              
1688             =item extends
1689              
1690             The name of a service to extend. The named service's configuration will
1691             be merged with this configuration (via L<the merge_config
1692             method|/merge_config>).
1693              
1694             This can be used in place of the C<class> key if the extended configuration
1695             contains a class.
1696              
1697             =item with
1698              
1699             Compose a role into the object's class before creating the object. This
1700             can be a single string, or an array reference of strings which are roles
1701             to combine.
1702              
1703             This uses L<Moo::Role|Moo::Role> and L<the create_class_with_roles
1704             method|Role::Tiny/create_class_with_roles>, which should work with any
1705             class (as it uses L<the Role::Tiny module|Role::Tiny> under the hood).
1706              
1707             This can be used with the C<class> key.
1708              
1709             =item on
1710              
1711             Attach an event handler to a L<Beam::Emitter subclass|Beam::Emitter>. This
1712             is an array of hashes of event names and handlers. A handler is made from
1713             a service reference (C<$ref> or an anonymous service), and a subroutine to
1714             call on that service (C<$sub>).
1715              
1716             For example:
1717              
1718             emitter:
1719             class: My::Emitter
1720             on:
1721             - my_event:
1722             $ref: my_handler
1723             $sub: on_my_event
1724              
1725             This can be used with the C<class> key.
1726              
1727             =back
1728              
1729             This method uses L<the parse_args method|/parse_args> to parse the C<args> key,
1730             L<resolving references|resolve_ref> as needed.
1731              
1732             =head2 merge_config
1733              
1734             my %merged = $wire->merge_config( %config );
1735              
1736             If C<%config> contains an C<extends> key, merge the extended config together
1737             with this one, returning the merged service configuration. This works recursively,
1738             so a service can extend a service that extends another service just fine.
1739              
1740             When merging, hashes are combined, with the child configuration taking
1741             precedence. The C<args> key is handled specially to allow a hash of
1742             args to be merged. A single element array of args is merged too, if the
1743             element is a hash.
1744              
1745             The configuration returned is a safe copy and can be modified without
1746             effecting the original config.
1747              
1748             =head2 parse_args
1749              
1750             my @args = $wire->parse_args( $for_name, $class, $args );
1751              
1752             Parse the arguments (C<$args>) for the given service (C<$for_name>) with
1753             the given class (C<$class>).
1754              
1755             C<$args> can be an array reference, a hash reference, or a simple
1756             scalar. The arguments will be searched for references using L<the
1757             find_refs method|/find_refs>, and then a list of arguments will be
1758             returned, ready to pass to the object's constructor.
1759              
1760             Nested containers are handled specially by this method:
1761              
1762             =over
1763              
1764             =item * Inner references are not resolved by the parent container.
1765             This ensures that references are always relative to the container they're in.
1766              
1767             =item * If a file is specified but cannot be found, a C<default> can be provided
1768             as a fallback.
1769              
1770             =back
1771              
1772             =head2 find_refs
1773              
1774             my @resolved = $wire->find_refs( $for_name, @args );
1775              
1776             Go through the C<@args> and recursively resolve any references and
1777             services found inside, returning the resolved result. References are
1778             identified with L<the is_meta method|/is_meta>.
1779              
1780             If a reference contains a C<$ref> key, it will be resolved by L<the
1781             resolve_ref method|/resolve_ref>. Otherwise, the reference will be
1782             treated as an anonymous service, and passed directly to L<the
1783             create_service method|/create_service>.
1784              
1785             This is used when L<creating a service|create_service> to ensure all
1786             dependencies are created first.
1787              
1788             =head2 is_meta
1789              
1790             my $is_meta = $wire->is_meta( $ref_hash, $root );
1791              
1792             Returns true if the given hash reference describes some kind of
1793             Beam::Wire service. This is used to identify service configuration
1794             hashes inside of larger data structures.
1795              
1796             A service hash reference must contain at least one key, and must either
1797             contain a L<prefixed|/meta_prefix> key that could create or reference an
1798             object (one of C<class>, C<extends>, C<config>, C<value>, C<env>, or C<ref>) or,
1799             if the C<$root> flag exists, be made completely of unprefixed meta keys
1800             (as returned by L<the get_meta_names method|/get_meta_names>).
1801              
1802             The C<$root> flag is used by L<the get method|/get> to allow unprefixed
1803             meta keys in the top-level hash values.
1804              
1805             =head2 get_meta_names
1806              
1807             my %meta_keys = $wire->get_meta_names;
1808              
1809             Get all the possible service keys with the L<meta prefix|/meta_prefix> already
1810             attached.
1811              
1812             =head2 resolve_ref
1813              
1814             my @value = $wire->resolve_ref( $for_name, $ref_hash );
1815              
1816             Resolves the given dependency from the configuration hash (C<$ref_hash>)
1817             for the named service (C<$for_name>). Reference hashes contain the
1818             following keys:
1819              
1820             =over 4
1821              
1822             =item $ref
1823              
1824             The name of a service in the container. Required.
1825              
1826             =item $path (deprecated)
1827              
1828             B<Deprecated>: Bare hashrefs can be treated as child containers with C<$ref>
1829             now. If there are other uses of C<$path> that cannot be updated to C<$ref>,
1830             please L<open a GitHub issue to discuss it|https://github.com/preaction/Beam-Wire/issues>.
1831              
1832             A data path to pick some data out of the reference. Useful with C<value>
1833             and C<config> services.
1834              
1835             # container.yml
1836             bounties:
1837             value:
1838             malcolm: 50000
1839             zoe: 35000
1840             simon: 100000
1841              
1842             captain:
1843             class: Person
1844             args:
1845             name: Malcolm Reynolds
1846             bounty:
1847             $ref: bounties
1848             $path: /malcolm
1849              
1850             =item $call
1851              
1852             Call a method on the referenced object and use the resulting value. This
1853             may be a string, which will be the method name to call, or a hash with
1854             C<$method> and C<$args>, which are the method name to call and the
1855             arguments to that method, respectively.
1856              
1857             captain:
1858             class: Person
1859             args:
1860             name: Malcolm Reynolds
1861             location:
1862             $ref: beacon
1863             $call: get_location
1864             bounty:
1865             $ref: news
1866             $call:
1867             $method: get_bounty
1868             $args:
1869             name: mreynolds
1870              
1871             =back
1872              
1873             =head2 fix_refs
1874              
1875             my @fixed = $wire->fix_refs( $for_container_name, @args );
1876              
1877             Similar to L<the find_refs method|/find_refs>. This method searches
1878             through the C<@args> and recursively fixes any reference paths to be
1879             absolute. References are identified with L<the is_meta
1880             method|/is_meta>.
1881              
1882             This is used by L<the get_config method|/get_config> to ensure that the
1883             configuration can be passed directly in to L<the create_service
1884             method|create_service>.
1885              
1886             =head2 new
1887              
1888             my $wire = Beam::Wire->new( %attributes );
1889              
1890             Create a new container.
1891              
1892             =head1 EXCEPTIONS
1893              
1894             If there is an error internal to Beam::Wire, an exception will be thrown. If there is an
1895             error with creating a service or calling a method, the exception thrown will be passed-
1896             through unaltered.
1897              
1898             =head2 Beam::Wire::Exception
1899              
1900             The base exception class
1901              
1902             =head2 Beam::Wire::Exception::Constructor
1903              
1904             An exception creating a Beam::Wire object
1905              
1906             =head2 Beam::Wire::Exception::Config
1907              
1908             An exception loading the configuration file.
1909              
1910             =head2 Beam::Wire::Exception::Service
1911              
1912             An exception with service information inside
1913              
1914             =head2 Beam::Wire::Exception::NotFound
1915              
1916             The requested service or configuration was not found.
1917              
1918             =head2 Beam::Wire::Exception::InvalidConfig
1919              
1920             The configuration is invalid:
1921              
1922             =over 4
1923              
1924             =item *
1925              
1926             Both "value" and "class" or "extends" are defined. These are mutually-exclusive.
1927              
1928             =back
1929              
1930             =head1 EVENTS
1931              
1932             The container emits the following events.
1933              
1934             =head2 configure_service
1935              
1936             This event is emitted when a new service is configured, but before it is
1937             instantiated or any classes loaded. This allows altering of the
1938             configuration before the service is built. Already-built services will
1939             not fire this event.
1940              
1941             Event handlers get a L<Beam::Wire::Event::ConfigService> object as their
1942             only argument.
1943              
1944             This event will bubble up from child containers.
1945              
1946             =head2 build_service
1947              
1948             This event is emitted when a new service is built. Cached services will
1949             not fire this event.
1950              
1951             Event handlers get a L<Beam::Wire::Event::BuildService> object as their
1952             only argument.
1953              
1954             This event will bubble up from child containers.
1955              
1956             =head1 ENVIRONMENT VARIABLES
1957              
1958             =over 4
1959              
1960             =item BEAM_PATH
1961              
1962             A colon-separated list of directories to look up inner container files. Use this
1963             to allow adding containers for (e.g.) Docker/Kubernetes deployments.
1964              
1965             =item BEAM_WIRE_DEBUG
1966              
1967             If set, print a bunch of internal debugging information to STDERR.
1968              
1969             =back
1970              
1971             =head1 AUTHORS
1972              
1973             =over 4
1974              
1975             =item *
1976              
1977             Doug Bell <preaction@cpan.org>
1978              
1979             =item *
1980              
1981             Al Newkirk <anewkirk@ana.io>
1982              
1983             =back
1984              
1985             =head1 CONTRIBUTORS
1986              
1987             =for stopwords Al Tom Ben Moon Bruce Armstrong Diab Jerius Kent Fredric mauke Mohammad S Anwar mohawk2 Sven Willenbuecher XSven
1988              
1989             =over 4
1990              
1991             =item *
1992              
1993             Al Tom <al-tom.ru@yandex.ru>
1994              
1995             =item *
1996              
1997             Ben Moon <guiltydolphin@gmail.com>
1998              
1999             =item *
2000              
2001             Bruce Armstrong <bruce@armstronganchor.net>
2002              
2003             =item *
2004              
2005             Diab Jerius <djerius@cfa.harvard.edu>
2006              
2007             =item *
2008              
2009             Kent Fredric <kentnl@cpan.org>
2010              
2011             =item *
2012              
2013             mauke <lukasmai.403@gmail.com>
2014              
2015             =item *
2016              
2017             Mohammad S Anwar <mohammad.anwar@yahoo.com>
2018              
2019             =item *
2020              
2021             mohawk2 <mohawk2@users.noreply.github.com>
2022              
2023             =item *
2024              
2025             Sven Willenbuecher <sven.willenbuecher@kuehne-nagel.com>
2026              
2027             =item *
2028              
2029             XSven <XSven@users.noreply.github.com>
2030              
2031             =back
2032              
2033             =head1 COPYRIGHT AND LICENSE
2034              
2035             This software is copyright (c) 2018-2021 by Doug Bell.
2036              
2037             This is free software; you can redistribute it and/or modify it under
2038             the same terms as the Perl 5 programming language system itself.
2039              
2040             =cut