# NAME
Beam::Wire - Lightweight Dependency Injection Container
# VERSION
version 1.017
# STATUS
# SYNOPSIS
# wire.yml
captain:
class: Person
args:
name: Malcolm Reynolds
rank: Captain
first_officer:
$class: Person
name: Zoë Alleyne Washburne
rank: Commander
# script.pl
use Beam::Wire;
my $wire = Beam::Wire->new( file => 'wire.yml' );
my $captain = $wire->get( 'captain' );
print $captain->name; # "Malcolm Reynolds"
# DESCRIPTION
Beam::Wire is a configuration module and a dependency injection
container. In addition to complex data structures, Beam::Wire configures
and creates plain old Perl objects.
A dependency injection (DI) container creates an inversion of control:
Instead of manually creating all the dependent objects (also called
"services") before creating the main object that we actually want, a DI
container handles that for us: We describe the relationships between
objects, and the objects get built as needed.
Dependency injection is sometimes called the opposite of garbage
collection. Rather than ensure objects are destroyed in the right order,
dependency injection makes sure objects are created in the right order.
Using Beam::Wire in your application brings great flexibility,
allowing users to easily add their own code to customize how your
project behaves.
For an [introduction to the Beam::Wire service configuration format,
see Beam::Wire::Help::Config](https://metacpan.org/pod/Beam::Wire::Help::Config).
# ATTRIBUTES
## file
The path of the file where services are configured (typically a YAML
file). The file's contents should be a single hashref. The keys are
service names, and the values are [service
configurations](https://metacpan.org/pod/Beam::Wire::Help::Config).
## dir
The directory path to use when searching for inner container files.
Defaults to the directory which contains the file specified by the
[file attribute](#file).
## config
The raw configuration data. By default, this data is loaded by
[Config::Any](https://metacpan.org/pod/Config::Any) using the file specified by the [/file](https://metacpan.org/pod/file
attribute).
See [Beam::Wire::Help::Config for details on what the configuration
data structure looks like](https://metacpan.org/pod/Beam::Wire::Help::Config).
If you don't want to load a file, you can specify this attribute in the
Beam::Wire constructor.
## services
A hashref of cached services built from the [configuration](#config). If
you want to inject a pre-built object for other services to depend on,
add it here.
## meta\_prefix
The character that begins a meta-property inside of a service's `args`. This
includes `$ref`, `$path`, `$method`, and etc...
The default value is `$`. The empty string is allowed.
# METHODS
## get
my $service = $wire->get( $name );
my $service = $wire->get( $name, %overrides )
The get method resolves and returns the service named `$name`, creating
it, if necessary, with [the create\_service method](#create_service).
`%overrides` is an optional list of name-value pairs. If specified,
get() will create an new, anonymous service that extends the named
service with the given config overrides. For example:
# test.pl
use Beam::Wire;
my $wire = Beam::Wire->new(
config => {
foo => {
args => {
text => 'Hello, World!',
},
},
},
);
my $foo = $wire->get( 'foo', args => { text => 'Hello, Chicago!' } );
print $foo; # prints "Hello, Chicago!"
This allows you to create factories out of any service, overriding service
configuration at run-time.
If `$name` contains a slash (`/`) character (e.g. `foo/bar`), the left
side (`foo`) will be used as the name of an inner container, and the
right side (`bar`) is a service inside that container. For example,
these two lines are equivalent:
$bar = $wire->get( 'foo/bar' );
$bar = $wire->get( 'foo' )->get( 'bar' );
Inner containers can be nested as deeply as desired (`foo/bar/baz/fuzz`).
## set
$wire->set( $name => $service );
The set method configures and stores the specified `$service` with the
specified `$name`. Use this to add or replace built services.
Like [the get() method, above](#get), `$name` can contain a slash (`/`)
character to traverse through nested containers.
## get\_config
my $conf = $wire->get_config( $name );
Get the config with the given `$name`. Like [the get() method,
above](#get), `$name` can contain slash (`/`) characters to traverse
through nested containers.
## normalize\_config
my $out_conf = $self->normalize_config( $in_conf );
Normalize the given `$in_conf` into to hash that [the create\_service
method](#create_service) expects. This method allows a service to be
defined with prefixed meta-names (`$class` instead of `class`) and
the arguments specified without prefixes.
For example, these two services are identical.
foo:
class: Foo
args:
fizz: buzz
foo:
$class: Foo
fizz: buzz
The `$in_conf` must be a hash, and must already pass [an is\_meta
check](#is_meta).
## create\_service
my $service = $wire->create_service( $name, %config );
Create the service with the given `$name` and `%config`. Config can
contain the following keys:
- class
The class name of an object to create. Can be combined with `method`,
and `args`.
- args
The arguments to the constructor method. Used with `class` and
`method`. Can be a simple value, or a reference to an array or
hash which will be dereferenced and passed in to the constructor
as a list.
- method
The method to call to create the object. Only used with `class`.
Defaults to `"new"`.
This can also be an array of hashes which describe a list of methods
that will be called on the object. The first method should create the
object, and each subsequent method can be used to modify the object. The
hashes should contain a `method` key, which is a string containing the
method to call, and optionally `args` and `return` keys. The `args`
key works like the top-level `args` key, above. The optional `return`
key can have the special value `"chain"`, which will use the return
value from the method as the value for the service ([The tutorial shows
examples of this](https://metacpan.org/pod/Beam::Wire::Help::Config#Multiple-Constructor-Methods)).
If an array is used, the top-level `args` key is not used.
- value
The value of this service. Can be a simple value, or a reference to an
array or hash. This value will be simply returned by this method, and is
mostly useful when using container files.
`value` can not be used with `class` or `extends`.
- config
The path to a configuration file, relative to [the dir attribute](#dir).
The file will be read with [Config::Any](https://metacpan.org/pod/Config::Any), and the resulting data
structure returned.
- extends
The name of a service to extend. The named service's configuration will
be merged with this configuration (via [the merge\_config
method](#merge_config)).
This can be used in place of the `class` key if the extended configuration
contains a class.
- with
Compose a role into the object's class before creating the object. This
can be a single string, or an array reference of strings which are roles
to combine.
This uses [Moo::Role](https://metacpan.org/pod/Moo::Role) and [the create\_class\_with\_roles
method](https://metacpan.org/pod/Role::Tiny#create_class_with_roles), which should work with any
class (as it uses [the Role::Tiny module](https://metacpan.org/pod/Role::Tiny) under the hood).
This can be used with the `class` key.
- on
Attach an event handler to a [Beam::Emitter subclass](https://metacpan.org/pod/Beam::Emitter). This
is an array of hashes of event names and handlers. A handler is made from
a service reference (`$ref` or an anonymous service), and a subroutine to
call on that service (`$sub`).
For example:
emitter:
class: My::Emitter
on:
- my_event:
$ref: my_handler
$sub: on_my_event
This can be used with the `class` key.
This method uses [the parse\_args method](#parse_args) to parse the `args` key,
[resolving references](https://metacpan.org/pod/resolve_ref) as needed.
## merge\_config
my %merged = $wire->merge_config( %config );
If `%config` contains an `extends` key, merge the extended config together
with this one, returning the merged service configuration. This works recursively,
so a service can extend a service that extends another service just fine.
When merging, hashes are combined, with the child configuration taking
precedence. The `args` key is handled specially to allow a hash of
args to be merged.
The configuration returned is a safe copy and can be modified without
effecting the original config.
## parse\_args
my @args = $wire->parse_args( $for_name, $class, $args );
Parse the arguments (`$args`) for the given service (`$for_name`) with
the given class (`$class`).
`$args` can be an array reference, a hash reference, or a simple
scalar. The arguments will be searched for references using [the
find\_refs method](#find_refs), and then a list of arguments will be
returned, ready to pass to the object's constructor.
Nested containers are handled specially by this method: Their inner
references are not resolved by the parent container. This ensures that
references are always relative to the container they're in.
## find\_refs
my @resolved = $wire->find_refs( $for_name, @args );
Go through the `@args` and recursively resolve any references and
services found inside, returning the resolved result. References are
identified with [the is\_meta method](#is_meta).
If a reference contains a `$ref` key, it will be resolved by [the
resolve\_ref method](#resolve_ref). Otherwise, the reference will be
treated as an anonymous service, and passed directly to [the
create\_service method](#create_service).
This is used when [creating a service](https://metacpan.org/pod/create_service) to ensure all
dependencies are created first.
## is\_meta
my $is_meta = $wire->is_meta( $ref_hash );
Returns true if the given hash reference describes some kind of
Beam::Wire service. This is used to identify service configuration
hashes inside of larger data structures.
A service hash reference must contain at least one key, and must either
be made completely of meta keys (as returned by [the get\_meta\_names
method](#get_meta_names)), or contain a [prefixed](#meta_prefix) key that
could create or reference an object (one of `class`, `extends`,
`config`, `value`, or `ref`);
## get\_meta\_names
my %meta_keys = $wire->get_meta_names;
Get all the possible service keys with the [meta prefix](#meta_prefix) already
attached.
## resolve\_ref
my @value = $wire->resolve_ref( $for_name, $ref_hash );
Resolves the given dependency from the configuration hash (`$ref_hash`)
for the named service (`$for_name`). Reference hashes contain the
following keys:
- $ref
The name of a service in the container. Required.
- $path
A data path to pick some data out of the reference. Useful with `value`
and `config` services.
# container.yml
bounties:
value:
malcolm: 50000
zoe: 35000
simon: 100000
captain:
class: Person
args:
name: Malcolm Reynolds
bounty:
$ref: bounties
$path: /malcolm
- $call
Call a method on the referenced object and use the resulting value. This
may be a string, which will be the method name to call, or a hash with
`$method` and `$args`, which are the method name to call and the
arguments to that method, respectively.
captain:
class: Person
args:
name: Malcolm Reynolds
location:
$ref: beacon
$call: get_location
bounty:
$ref: news
$call:
$method: get_bounty
$args:
name: mreynolds
## fix\_refs
my @fixed = $wire->fix_refs( $for_name, @args );
Similar to [the find\_refs method](#find_refs). This method searches
through the `@args` and recursively fixes any reference paths to be
absolute. References are identified with [the is\_meta
method](#is_meta).
This is used by [the get\_config method](#get_config) to ensure that the
configuration can be passed directly in to [the create\_service
method](https://metacpan.org/pod/create_service).
## new
my $wire = Beam::Wire->new( %attributes );
Create a new container.
# EXCEPTIONS
If there is an error internal to Beam::Wire, an exception will be thrown. If there is an
error with creating a service or calling a method, the exception thrown will be passed-
through unaltered.
## Beam::Wire::Exception
The base exception class
## Beam::Wire::Exception::Constructor
An exception creating a Beam::Wire object
## Beam::Wire::Exception::Config
An exception loading the configuration file.
## Beam::Wire::Exception::Service
An exception with service information inside
## Beam::Wire::Exception::NotFound
The requested service or configuration was not found.
## Beam::Wire::Exception::InvalidConfig
The configuration is invalid:
- Both "value" and "class" or "extends" are defined. These are mutually-exclusive.
# AUTHORS
- Doug Bell
- Al Newkirk
# CONTRIBUTORS
- Bruce Armstrong
- Bruce Armstrong
- Kent Fredric
# COPYRIGHT AND LICENSE
This software is copyright (c) 2015 by Doug Bell.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
# POD ERRORS
Hey! **The above document had some coding errors, which are explained below:**
- Around line 21:
Non-ASCII character seen before =encoding in 'Zoë'. Assuming CP1252
- Around line 70:
alternative text '/file' contains non-escaped | or /