IOC::Slinky::Container - an alternative dependency-injection container
# 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;
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.
Returns an container instance based on the configuration specified by the hashref $conf. See "CONFIGURATION" for details.
$conf
Returns the $obj if $key lookup id is found in the container, otherwise it returns undef.
$obj
$key
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.
container
# an empty container $c = IOC::Slinky::Container->new( config => { container => { } } );
A container value can be one of the following:
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 ], } } );
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.
_class
_constructor = optional, overrides the method name to call, defaults to "new"
_constructor
_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.
_constructor_args
_constructor_passthru = optional, boolean, default to 0 (false) when this is TRUE, pass the _constructor_args as is without doing any automatic dereference of hashrefs and arrayrefs.
_constructor_passthru
_singleton = optional, defaults to 1, upon lookup, the object is instantiated once and only once in the lifetime of the container.
_singleton
_lookup_id = optional, alias of this object.
_lookup_id
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 );
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.
_ref
$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'), # );
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.
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
Dexter Tad-y, <dtady@cpan.org>
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.
To install IOC::Slinky::Container, copy and paste the appropriate command in to your terminal.
cpanm
cpanm IOC::Slinky::Container
CPAN shell
perl -MCPAN -e shell install IOC::Slinky::Container
For more information on module installation, please visit the detailed CPAN module installation guide.