NAME
IOC::Slinky::Container - an alternative dependency-injection container
SYNOPSIS
# in myapp.yml
---
container:
db_dsn: "DBI:mysql:database=myapp"
db_user: "myapp"
db_pass: "myapp"
logger:
_class: FileLogger
_constructor_args:
filename: "/var/log/myapp/debug.log"
myapp:
_class: "MyApp"
_constructor_args:
dbh:
_class: "DBI"
_constructor: "connect"
_constructor_args:
- { _ref => "db_dsn" }
- { _ref => "db_user" }
- { _ref => "db_pass" }
- { RaiseError => 1 }
logger:
_ref: logger
# in myapp.pl
# ...
use IOC::Slinky::Container;
use YAML qw/LoadFile/;
my $c = IOC::Slinky::Container->new( config => LoadFile('myapp.yml') );
my $app = $c->lookup('myapp');
$app->run;
DESCRIPTION
This module aims to be a (1) transparent and (2) simple dependency-injection (DI) container; and usually preconfigured from a configuration file.
A DI-container is a special object used to load and configure other components/objects. Each object can then be globally resolved using a unique lookup id.
For more information about the benefits of the technique, see Dependency Injection.
METHODS
- CLASS->new( config => $conf )
-
Returns an container instance based on the configuration specified by the hashref
$conf
. See "CONFIGURATION" for details. - $container->lookup($key)
-
Returns the
$obj
if$key
lookup id is found in the container, otherwise it returns undef.
CONFIGURATION
Rules
The configuration should be a plain hash reference.
A single top-level key container
should be a hash reference; where its keys will act as global namespace for all objects to be resolved.
# an empty container
$c = IOC::Slinky::Container->new(
config => {
container => {
}
}
);
A container value can be one of the following:
- Native
-
These are native Perl data structures.
$c = IOC::Slinky::Container->new( config => { container => { null => undef, greeting => "Hello World", pi => 3.1416, plain_href => { a => 1 }, plain_aref => [ 1, 2, 3 ], } } );
- Constructed Object
-
These are objects/values returned via a class method call, typically used for object construction. A constructed object is specified by a hashref with special meta fields:
_class
= when present, the container then treats this hashref as a constructed object spec. Otherwise this hash reference will be treated as a native value._constructor
= optional, overrides the method name to call, defaults to "new"_constructor_args
= optional, can be a scalar, hashref or an arrayref. Hashrefs and arrayrefs are dereferenced as hashes and lists respectively. Scalar values are passed as-is. Nesting is allowed._singleton
= optional, defaults to 1, upon lookup, the object is instantiated once and only once in the lifetime of the container._lookup_id
= optional, alias of this object.The rest of the hashref keys will also be treated as method calls, useful for attribute/setters initialization immediately after the constructor was called. (Setter Injection)
$c = IOC::Slinky::Container->new( config => { container => { # constructor injection dbh => { _class => "DBI", _constructor => "connect", _constructor_args => [ "DBD:SQLite:dbname=/tmp/my.db", "user", "pass", { RaiseError => 1 }, ], }, # setter injection y2k => { _singleton => 0, _class => "DateTime", year => 2000, month => 1, day => 1, }, } } ); my $dbh = $c->lookup('dbh'); # is roughly equivalent to (though this is a singleton): # my $dbh = DBI->connect( # "DBI:SQlite:dbname=/tmp/my.db", # "user", # "pass", # { RaiseError => 1 } # ); my $y2k = $c->lookup('y2k'); # is equivalent to: # my $y2k = DateTime->new; # $y2k->year( 2000 ); # $y2k->month( 1 ); # $y2k->day( 1 );
- Reference
-
References are "pointers" to the globally accessible container values. References are defined by a hashref with a special meta field
_ref
, the value of which will be used to lookup when requested.$c = IOC::Slinky::Container->new( config => { container => { dsn => "DBI:mysql:database=myapp", user => "myapp", pass => "myapp_password", dbh => { _class => "DBI", _constructor => "connect", _constructor_args => [ { _ref => "dsn" }, { _ref => "user" }, { _ref => "pass" }, ], }, } } ); my $dbh = $c->lookup('dbh'); # is roughly equivalent to: # $dbh = DBI->connect( # $c->lookup('dsn'), # $c->lookup('user'), # $c->lookup('pass'), # );
Recommended Practices
IOC::Slinky::Container's configuration is simply a hash-reference with a specific structure. It can come from virtually anywhere. Our recommended usage then is to externalize the configuration (e.g. in a file), and to use YAML for conciseness and ease-of-editing.
use IOC::Slinky::Container;
use YAML qw/LoadFile/;
my $c = IOC::Slinky::Container->new( config => LoadFile("/etc/myapp.yml") );
# ...
As a best practice, IOC::Slinky::Container should NOT be used as a service locator (see Service Locator Pattern). The container should only be referenced at the integration/top-level code. Most of your modules/classes should not even see or bother about the container in the first place. The goal is to have a modular, pluggable, reusable set of classes.
SEE ALSO
Bread::Broad - a Moose-based DI framework
IOC - the ancestor of Bread::Board
Peco::Container - another DI container
http://en.wikipedia.org/wiki/Dependency_Injection
YAML - for externalized configuration syntax
AUTHOR
Dexter Tad-y, <dtady@cpan.org>
COPYRIGHT AND LICENSE
Copyright 2011 by Dexter Tad-y
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.