Bread-Board-0.37/0000775000175000017500000000000013505520232013022 5ustar yanickyanickBread-Board-0.37/xt/0000775000175000017500000000000013505520232013455 5ustar yanickyanickBread-Board-0.37/xt/release/0000775000175000017500000000000013505520232015075 5ustar yanickyanickBread-Board-0.37/xt/release/unused-vars.t0000644000175000017500000000036213505520232017535 0ustar yanickyanick#!perl use Test::More 0.96 tests => 1; eval { require Test::Vars }; SKIP: { skip 1 => 'Test::Vars required for testing for unused vars' if $@; Test::Vars->import; subtest 'unused vars' => sub { all_vars_ok(); }; }; Bread-Board-0.37/lib/0000775000175000017500000000000013505520232013570 5ustar yanickyanickBread-Board-0.37/lib/Bread/0000775000175000017500000000000013505520232014605 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board.pm0000644000175000017500000005324413505520232016200 0ustar yanickyanickpackage Bread::Board; our $AUTHORITY = 'cpan:STEVAN'; $Bread::Board::VERSION = '0.37'; use strict; use warnings; use Carp qw(confess); use Scalar::Util qw(blessed); # ABSTRACT: A solderless way to wire up your application components use Bread::Board::Types; use Bread::Board::ConstructorInjection; use Bread::Board::SetterInjection; use Bread::Board::BlockInjection; use Bread::Board::Literal; use Bread::Board::Container; use Bread::Board::Container::Parameterized; use Bread::Board::Dependency; use Bread::Board::LifeCycle::Singleton; use Bread::Board::Service::Inferred; use Bread::Board::Service::Alias; use Moose::Exporter 2.1200; Moose::Exporter->setup_import_methods( as_is => [qw[ as container depends_on service alias wire_names include typemap infer literal ]], ); sub as (&) { $_[0] } our $CC; our $in_container; sub set_root_container { confess "Can't set the root container when we're already in a container" if $in_container; $CC = shift; } sub container ($;$$) { my $name = shift; my $c; if (blessed $name) { confess 'an object used as a container must inherit from Bread::Board::Container or Bread::Board::Container::Parameterized' unless $name->isa('Bread::Board::Container') || $name->isa('Bread::Board::Container::Parameterized'); confess 'container($object, ...) is not supported for parameterized containers' if scalar @_ > 1; # this is basically: # container( A::Bread::Board::Container->new, ... ) # or someone using &container as a constructor $c = $name; # if we're in the context of another container # then we're a subcontainer of it $CC->add_sub_container($c) if defined $CC; } else { my $is_inheriting = $name =~ s/^\+//; confess "Inheriting containers isn't possible outside of the context of a container" if $is_inheriting && !defined $CC; # if we have more than 1 argument, then we are a parameterized # container, so we need to act accordingly if (scalar @_ > 1) { confess 'Declaring container parameters when inheriting is not supported' if $is_inheriting; my $param_names = shift; $c = Bread::Board::Container::Parameterized->new({ name => $name, allowed_parameter_names => $param_names, }); } else { $c = $is_inheriting ? $CC->fetch($name) : Bread::Board::Container->new({ name => $name }); } # if we're in the context of another container # then we're a subcontainer of it, unless we're inheriting, # in which case we already got a parent $CC->add_sub_container($c) if !$is_inheriting && defined $CC; } my $body = shift; # if we have more arguments # then they are likely a body # and so we should execute it if (defined $body) { local $_ = $c; local $CC = $c; local $in_container = 1; $body->($c); } return $c; } sub include ($) { my $file = shift; if (my $ret = do $file) { return $ret; } else { confess "Couldn't compile $file: $@" if $@; confess "Couldn't open $file for reading: $!" if $!; confess "Unknown error when compiling $file " . "(or $file doesn't return a true value)"; } } sub service ($@) { my $name = shift; my $s; my $is_inheriting = ($name =~ s/^\+//); if (scalar @_ == 1) { confess "Service inheritance doesn't make sense for literal services" if $is_inheriting; $s = Bread::Board::Literal->new(name => $name, value => $_[0]); } elsif (scalar(@_) % 2 == 0) { my %params = @_; my $class = $params{service_class}; $class ||= defined $params{service_type} ? "Bread::Board::$params{service_type}Injection" : exists $params{block} ? 'Bread::Board::BlockInjection' : 'Bread::Board::ConstructorInjection'; $class->does('Bread::Board::Service') or confess "The service class must do the Bread::Board::Service role"; if ($is_inheriting) { confess "Inheriting services isn't possible outside of the context of a container" unless defined $CC; my $container = ($CC->isa('Bread::Board::Container::Parameterized') ? $CC->container : $CC); my $prototype_service = $container->fetch($name); confess sprintf( "Trying to inherit from service '%s', but found a %s", $name, blessed $prototype_service, ) unless $prototype_service->does('Bread::Board::Service'); $s = $prototype_service->clone_and_inherit_params( service_class => $class, %params, ); } else { $s = $class->new(name => $name, %params); } } else { confess "A service is defined by a name and either a single value or hash of parameters; you have supplied neither"; } return $s unless defined $CC; $CC->add_service($s); } sub alias ($$@) { my $name = shift; my $path = shift; my %params = @_; my $s = Bread::Board::Service::Alias->new( name => $name, aliased_from_path => $path, %params, ); return $s unless defined $CC; $CC->add_service($s); } sub typemap ($@) { my $type = shift; (scalar @_ == 1) || confess "typemap takes a single argument"; my $service; if (blessed $_[0]) { if ($_[0]->does('Bread::Board::Service')) { $service = $_[0]; } elsif ($_[0]->isa('Bread::Board::Service::Inferred')) { $service = $_[0]->infer_service( $type ); } else { confess $_[0] . " isn't a service"; } } else { $service = $CC->fetch( $_[0] ); } $CC->add_type_mapping_for( $type, $service ); } sub infer { if (@_ == 1) { return Bread::Board::Service::Inferred->new( current_container => $CC, service => $_[0], infer_params => 1, ); } else { my %params = @_; return Bread::Board::Service::Inferred->new( current_container => $CC, service_args => \%params, infer_params => 1, ); } } sub wire_names { +{ map { $_ => depends_on($_) } @_ }; } sub depends_on ($) { my $path = shift; Bread::Board::Dependency->new(service_path => $path); } my $LITERAL_ANON = 0; sub literal($) { my $value = shift; Bread::Board::Literal->new( name => 'LITERAL_ANON_' . $LITERAL_ANON++, value => $value, ); } 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board - A solderless way to wire up your application components =head1 VERSION version 0.37 =head1 SYNOPSIS use Bread::Board; my $c = container 'MyApp' => as { service 'log_file_name' => "logfile.log"; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => [ 'log_file_name' ], ); container 'Database' => as { service 'dsn' => "dbi:SQLite:dbname=my-app.db"; service 'username' => "user234"; service 'password' => "****"; service 'dbh' => ( block => sub { my $s = shift; require DBI; DBI->connect( $s->param('dsn'), $s->param('username'), $s->param('password'), ) || die "Could not connect"; }, dependencies => [ 'dsn', 'username', 'password' ] ); }; service 'application' => ( class => 'MyApplication', dependencies => { logger => 'logger', dbh => 'Database/dbh', } ); }; no Bread::Board; # removes keywords # get an instance of MyApplication # from the container my $app = $c->resolve( service => 'application' ); # now user your MyApplication # as you normally would ... $app->run; =head1 DESCRIPTION Bread::Board is an inversion of control framework with a focus on dependency injection and lifecycle management. It's goal is to help you write more decoupled objects and components by removing the need for you to manually wire those objects/components together. Want to know more? See the L. +-----------------------------------------+ | A B C D E F G H I J | |-----------------------------------------| | o o | 1 o-o-o-o-o v o-o-o-o-o 1 | o o | | o o | 2 o-o-o-o-o o-o-o-o-o 2 | o o | | o o | 3 o-o-o-o-o o-o-o-o-o 3 | o o | | o o | 4 o-o-o-o-o o-o-o-o-o 4 | o o | | o o | 5 o-o-o-o-o o-o-o-o-o 5 | o o | | | 6 o-o-o-o-o o-o-o-o-o 6 | | | o o | 7 o-o-o-o-o o-o-o-o-o 7 | o o | | o o | 8 o-o-o-o-o o-o-o-o-o 8 | o o | | o o | 9 o-o-o-o-o o-o-o-o-o 9 | o o | | o o | 10 o-o-o-o-o o-o-o-o-o 10 | o o | | o o | 11 o-o-o-o-o o-o-o-o-o 11 | o o | | | 12 o-o-o-o-o o-o-o-o-o 12 | | | o o | 13 o-o-o-o-o o-o-o-o-o 13 | o o | | o o | 14 o-o-o-o-o o-o-o-o-o 14 | o o | | o o | 15 o-o-o-o-o o-o-o-o-o 15 | o o | | o o | 16 o-o-o-o-o o-o-o-o-o 16 | o o | | o o | 17 o-o-o-o-o o-o-o-o-o 17 | o o | | | 18 o-o-o-o-o o-o-o-o-o 18 | | | o o | 19 o-o-o-o-o o-o-o-o-o 19 | o o | | o o | 20 o-o-o-o-o o-o-o-o-o 20 | o o | | o o | 21 o-o-o-o-o o-o-o-o-o 21 | o o | | o o | 22 o-o-o-o-o o-o-o-o-o 22 | o o | | o o | 22 o-o-o-o-o o-o-o-o-o 22 | o o | | | 23 o-o-o-o-o o-o-o-o-o 23 | | | o o | 24 o-o-o-o-o o-o-o-o-o 24 | o o | | o o | 25 o-o-o-o-o o-o-o-o-o 25 | o o | | o o | 26 o-o-o-o-o o-o-o-o-o 26 | o o | | o o | 27 o-o-o-o-o o-o-o-o-o 27 | o o | | o o | 28 o-o-o-o-o ^ o-o-o-o-o 28 | o o | +-----------------------------------------+ Loading this package will automatically load the rest of the packages needed by your Bread::Board configuration. =head1 EXPORTED FUNCTIONS The functions of this package provide syntactic sugar to help you build your Bread::Board configuration. You can build such a configuration by constructing the objects manually instead, but your code may be more difficult to understand. =head2 C =head3 simple case container $name, \&body; This function constructs and returns an instance of L. The (optional) C<&body> block may be used to add services or sub-containers within the newly constructed container. Usually, the block is not passed directly, but passed using the C function. For example, container 'MyWebApp' => as { service my_dispatcher => ( class => 'MyWebApp::Dispatcher', ); }; If C<$name> starts with C<'+'>, and the container is being declared inside another container, then this declaration will instead extend an existing container with the name C<$name> (without the C<'+'>). =head3 from an instance container $container_instance, \&body In many cases, subclassing L is the easiest route to getting access to this framework. You can do this and still get all the benefits of the syntactic sugar for configuring that class by passing an instance of your container subclass to C. You could, for example, configure your container inside the C method of your class: package MyWebApp; use Moose; extends 'Bread::Board::Container'; sub BUILD { my $self = shift; container $self => as { service dbh => ( ... ); }; } =head3 with parameters container $name, \@parameters, \&body A third way of using the C function is to build a parameterized container. These are useful as a way of providing a placeholder for parts of the configuration that may be provided later. You may not use an instance object in place of the C<$name> in this case. For more detail on how you might use parameterized containers, see L. =head2 C as { some_code() }; This is just a replacement for the C keyword that is easier to read when defining containers. =head2 C service $name, $literal; service $name, %service_description; Within the C blocks for your containers, you may construct services using the C function. This can construct several different kinds of services based upon how it is called. =head3 literal services To build a literal service (a L object), just specify a scalar value or reference you want to use as the literal value: # In case you need to adjust the gravitational constant of the Universe service gravitational_constant => 6.673E-11; =head3 using injections To build a service using one of the injection services, just fill in all the details required to use that sort of injection: service search_service => ( class => 'MyApp::Search', block => sub { my $s = shift; MyApp::Search->new($s->param('url'), $s->param('type')); }, dependencies => { url => 'search_url', }, parameters => { type => { isa => 'Str', default => 'text' }, }, ); The type of injection performed depends on the parameters used. You may use the C parameter to pick a specific injector class. For instance, this is useful if you need to use L or have defined a custom injection service. If you specify a C, block injection will be performed using L. If neither of these is present, constructor injection will be used with L (and you must provide the C option). =head3 service dependencies The C parameter takes a hashref of dependency names mapped to L objects, but there are several coercions and sugar functions available to make specifying dependencies as easy as possible. The simplest case is when the names of the services you're depending on are the same as the names that the service you're defining will be accessing them with. In this case, you can just specify an arrayref of service names: service foo => ( dependencies => [ 'bar', 'baz' ], # ... ); If you need to use a different name, you can specify the dependencies as a hashref instead: service foo => ( dependencies => { dbh => 'foo_dbh', }, # ... ); You can also specify parameters when depending on a parameterized service: service foo => ( dependencies => [ { bar => { bar_param => 1 } }, 'baz', ], # ... ); Finally, services themselves can also be specified as dependencies, in which case they will just be resolved directly: service foo => ( dependencies => { dsn => Bread::Board::Literal->new( name => 'dsn', value => 'dbi:mysql:mydb', ), }, # ... ); As a special case, an arrayref of dependencies will be interpreted as a service which returns an arrayref containing the resolved values of those dependencies: service foo => ( dependencies => { # items will resolve to [ $bar_service->get, $baz_service->get ] items => [ 'bar', Bread::Board::Literal->new(name => 'baz', value => 'BAZ'), ], }, # ... ); =head3 inheriting and extending services If the C<$name> starts with a C<'+'>, the service definition will instead extend an existing service with the given C<$name> (without the C<'+'>). This works similarly to the C syntax in Moose. It is most useful when defining a container class where the container is built up in C methods, as each class in the inheritance hierarchy can modify services defined in superclasses. The C and C options will be merged with the existing values, rather than overridden. Note that literal services can't be extended, because there's nothing to extend. You can still override them entirely by declaring the service name without a leading C<'+'>. =head2 C literal($value); Creates an anonymous L object with the given value. service 'dbh' => ( block => sub { my $s = shift; require DBI; DBI->connect( $s->param('dsn'), $s->param('username'), $s->param('password'), ) || die "Could not connect"; }, dependencies => { dsn => literal 'dbi:SQLite:somedb', username => literal 'foo', password => literal 'password', }, ); =head2 C depends_on($service_path); The C function creates a L object for the named C<$service_path> and returns it. =head2 C wire_names(@service_names); This function is just a shortcut for passing a hash reference of dependencies into the service. It is not typically needed, since Bread::Board can usually understand what you mean - these declarations are all equivalent: service foo => ( class => 'Pity::TheFoo', dependencies => { foo => depends_on('foo'), bar => depends_on('bar'), baz => depends_on('baz'), }, ); service foo => ( class => 'Pity::TheFoo', dependencies => wire_names(qw( foo bar baz )), ); service foo => ( class => 'Pity::TheFoo', dependencies => { foo => 'foo', bar => 'bar', baz => 'baz', }, ); service foo => ( class => 'Pity::TheFoo', dependencies => [ qw(foo bar baz ) ], ); =head2 C typemap $type, $service; typemap $type, $service_path; This creates a type mapping for the named type. Typically, it is paired with the C call like so: typemap 'MyApp::Model::UserAccount' => infer; For more details on what type mapping is and how it works, see L. =head2 C infer; infer(%hints); This is used with C to help create the typemap inference. It can be used with no arguments to do everything automatically. However, in some cases, you may want to pass a service instance as the argument or a hash of service arguments to change how the type map works. For example, if your type needs to be constructed using a setter injection, you can use an inference similar to this: typemap 'MyApp::Model::UserPassword' => infer( service_class => 'Bread::Board::SetterInjection', ); For more details on what type mapping is and how it works, see L. =head2 C include $file; This is a shortcut for loading a Bread::Board configuration from another file. include "filename.pl"; The above is pretty much identical to running: do "filename.pl"; However, you might find it more readable to use C. =head2 C alias $service_name, $service_path, %service_description; This helper allows for the creation of L, which allows you to define a service in one place and then reuse that service with a different name somewhere else. This is sort of like a symbolic link for services. Aliases will be L, so an alias can alias an alias. For example, service file_logger => ( class => 'MyApp::Logger::File', ); alias my_logger => 'file_logger'; =head1 OTHER FUNCTIONS These are not exported, but might be helpful to you. =head2 C set_root_container $container; You may use this to set a top-level root container for all container definitions. For example, my $app = container MyApp => as { ... }; Bread::Board::set_root_container($app); my $config = container Config => as { ... }; Here the C<$config> container would be created as a sub-container of C<$app>. =head1 ACKNOWLEDGEMENTS Thanks to Daisuke Maki for his contributions and for really pushing the development of this module along. Chuck "sprongie" Adams, for testing/using early (pre-release) versions of this module, and some good suggestions for naming it. Matt "mst" Trout, for finally coming up with the best name for this module. Gianni "dakkar" Ceccarelli for writing lots of documentation, and Net-a-Porter.com for paying his salary while he was doing it. =head1 ARTICLES L Thomas Klausner showing a use-case for Bread::Board. =head1 SEE ALSO =over 4 =item L This provides more powerful syntax for writing Bread::Board container classes. =item L Bread::Board is basically my re-write of IOC. =item L =back =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/0000775000175000017500000000000013505520232015634 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/Types.pm0000644000175000017500000001556513505520232017310 0ustar yanickyanickpackage Bread::Board::Types; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: types and coercions for Bread::Board $Bread::Board::Types::VERSION = '0.37'; use Moose::Util::TypeConstraints; use Scalar::Util qw(blessed); use Bread::Board::Service; use Bread::Board::Dependency; ## for Bread::Board::Container class_type 'Bread::Board::Container'; class_type 'Bread::Board::Container::Parameterized'; subtype 'Bread::Board::Container::SubContainerList' => as 'HashRef[Bread::Board::Container|Bread::Board::Container::Parameterized]'; coerce 'Bread::Board::Container::SubContainerList' => from 'ArrayRef[Bread::Board::Container]' => via { +{ map { $_->name => $_ } @$_ } }; subtype 'Bread::Board::Container::ServiceList' => as 'HashRef[Bread::Board::Service]'; coerce 'Bread::Board::Container::ServiceList' => from 'ArrayRef[Bread::Board::Service]' => via { +{ map { $_->name => $_ } @$_ } }; ## for Bread::Board::Service::WithDependencies ... subtype 'Bread::Board::Service::Dependencies' => as 'HashRef[Bread::Board::Dependency]'; my $ANON_INDEX = 1; sub _coerce_to_dependency { my ($dep) = @_; if (!blessed($dep)) { if (ref $dep eq 'HASH') { my ($service_path) = keys %$dep; my ($service_params) = values %$dep; $dep = Bread::Board::Dependency->new( service_path => $service_path, service_params => $service_params ); } elsif (ref $dep eq 'ARRAY') { require Bread::Board::BlockInjection; my $name = '_ANON_COERCE_' . $ANON_INDEX++ . '_'; my @deps = map { _coerce_to_dependency($_) } @$dep; my @dep_names = map { "${name}DEP_$_" } 0..$#deps; $dep = Bread::Board::Dependency->new( service_name => $name, service => Bread::Board::BlockInjection->new( name => $name, dependencies => { map { $dep_names[$_] => $deps[$_]->[1] } 0..$#deps }, block => sub { my ($s) = @_; return [ map { $s->param($_) } @dep_names ]; }, ), ); $dep->service->parent($dep); } else { $dep = Bread::Board::Dependency->new(service_path => $dep); } } if ($dep->isa('Bread::Board::Dependency')) { return [$dep->service_name => $dep]; } else { return [$dep->name => Bread::Board::Dependency->new(service => $dep)]; } } coerce 'Bread::Board::Service::Dependencies' => from 'HashRef[Bread::Board::Service | Bread::Board::Dependency | Str | HashRef | ArrayRef]' => via { +{ map { $_ => _coerce_to_dependency($_[0]->{$_})->[1] } keys %{$_[0]} } } => from 'ArrayRef[Bread::Board::Service | Bread::Board::Dependency | Str | HashRef]' => via { +{ map { @{ _coerce_to_dependency($_) } } @{$_[0]} } }; ## for Bread::Board::Service::WithParameters ... subtype 'Bread::Board::Service::Parameters' => as 'HashRef'; coerce 'Bread::Board::Service::Parameters' => from 'ArrayRef' => via { +{ map { $_ => { optional => 0 } } @$_ } }; no Moose::Util::TypeConstraints; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Types - types and coercions for Bread::Board =head1 VERSION version 0.37 =head1 DESCRIPTION This package defines types and coercions for L. =head1 TYPES =head2 C A hashref mapping strings to instances of L or L. Can be coerced from an arrayref of containers: the keys will be the containers' names. =head2 C A hashref mapping strings to instances of L. Can be coerced from an arrayref of services: the keys will be the services' names. =head2 C Hashref mapping strings to instances of L. The values of the hashref can be coerced in several different ways: =over 4 =item a string will be interpreted as the L<< C|Bread::Board::Dependency/service_path >> =item a hashref with a single key the key will be interpreted as a L<< C|Bread::Board::Dependency/service_path >>, and the value as a hashref for L<< C|Bread::Board::Dependency/service_params >> =item an arrayref each element will be interpreted as a dependency (possibly through all the coercions listed here); see below for an example =item a L object will be interpreted as a dependency on that service =item a L object will be taken as-is =back Instead of a hashref of any of the above things, you can use an arrayref: it will be coerced to hashref, using the (coerced) dependencies' names as keys. =head3 Examples service foo => ( class => 'Foo', dependencies => { { bar => { attribute => 12 } }, }, ); The service C depends on the parameterized service C, and C will be instantiated passing the hashref C<< { attribute => 12 } >> to its L<< C|Bread::Board::Service::WithParameters/get >> method. service foo => ( class => 'Foo', dependencies => { things => [ 'bar', 'baz' ], }, ); The service C depends on the services C and C, and when instantiating C, its constructor will receive something like C<< things => [ $instance_of_bar, $instance_of_baz ] >>. service foo => ( class => 'Foo', dependencies => { things => [ { bar => { attribute => 12 } }, { bar => { attribute => 27 } }, ], }, ); You can mix&match the coercions! This C will get two different instances of C in its C attribute, each C instantiated with a different value. =head2 C Hashref mapping strings to L specifications. Can be coerced from an arrayref of strings: [qw(a b c)] becomes: { a => { optional => 0 }, b => { optional => 0 }, c => { optional => 0 }, } =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service.pm0000644000175000017500000001462413505520232017577 0ustar yanickyanickpackage Bread::Board::Service; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: Base service role $Bread::Board::Service::VERSION = '0.37'; use Moose::Role; use Module::Runtime (); use Moose::Util::TypeConstraints 'find_type_constraint'; with 'Bread::Board::Traversable'; has 'name' => ( is => 'rw', isa => 'Str', required => 1 ); has 'params' => ( traits => [ 'Hash' ], is => 'rw', isa => 'HashRef', lazy => 1, builder => 'init_params', clearer => 'clear_params', handles => { get_param => 'get', get_param_keys => 'keys', _clear_param => 'delete', _set_param => 'set', } ); has 'is_locked' => ( is => 'rw', isa => 'Bool', default => sub { 0 } ); has 'lifecycle' => ( is => 'rw', isa => 'Str', trigger => sub { my ($self, $lifecycle) = @_; if ($self->does('Bread::Board::LifeCycle')) { my $base = (Class::MOP::class_of($self)->superclasses)[0]; Class::MOP::class_of($base)->rebless_instance_back($self); return if $lifecycle eq 'Null'; } my $lifecycle_role = $lifecycle =~ /^\+/ ? substr($lifecycle, 1) : "Bread::Board::LifeCycle::${lifecycle}"; Module::Runtime::require_module($lifecycle_role); Class::MOP::class_of($lifecycle_role)->apply($self); } ); sub init_params { +{} } sub param { my $self = shift; return $self->get_param_keys if scalar @_ == 0; return $self->get_param( $_[0] ) if scalar @_ == 1; ((scalar @_ % 2) == 0) || confess "parameter assignment must be an even numbered list"; my %new = @_; while (my ($key, $value) = each %new) { $self->set_param( $key => $value ); } return; } { my %mergeable_params = ( dependencies => { interface => 'Bread::Board::Service::WithDependencies', constraint => 'Bread::Board::Service::Dependencies', }, parameters => { interface => 'Bread::Board::Service::WithParameters', constraint => 'Bread::Board::Service::Parameters', }, ); sub clone_and_inherit_params { my ($self, %params) = @_; confess "Changing a service's class is not possible when inheriting" unless $params{service_class} eq blessed $self; for my $p (keys %mergeable_params) { if (exists $params{$p}) { if ($self->does($mergeable_params{$p}->{interface})) { my $type = find_type_constraint $mergeable_params{$p}->{constraint}; my $val = $type->assert_coerce($params{$p}); $params{$p} = { %{ $self->$p }, %{ $val }, }; } else { confess "Trying to add $p to a service not supporting them"; } } } $self->clone(%params); } } requires 'get'; sub lock { (shift)->is_locked(1) } sub unlock { (shift)->is_locked(0) } no Moose::Util::TypeConstraints; no Moose::Role; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service - Base service role =head1 VERSION version 0.37 =head1 DESCRIPTION This role is the basis for all services in L. It provides (or requires the implementation of) the minimum necessary building blocks: creating an instance, setting/getting parameters, instance lifecycle. =head1 ATTRIBUTES =head2 C Read/write string, required. Every service needs a name, by which it can be referenced when L. =head2 C Boolean, defaults to false. Used during L to detect loops. =head2 C $service->lifecycle('Singleton'); Read/write string; it should be either a partial class name under the C namespace (like C for C) or a full class name prefixed with C<+> (like C<+My::Special::Lifecycle>). The name is expected to refer to a loadable I, which will be applied to the service instance. =head1 METHODS =head2 C Locks the service; you should never need to call this method in normal code. =head2 C Unlocks the service; you should never need to call this method in normal code. =head2 C my $value = $service->get(); This method I be implemented by the consuming class. It's expected to instantiate whatever object or value this service should resolve to. =head2 C Builder for the service parameters, defaults to returning an empty hashref. =head2 C Clearer of the service parameters. =head2 C my @param_names = $service->param(); my $param_value = $service->param($param_name); $service->param($name1=>$value1,$name2=>$value2); Getter/setter for the service parameters; notice that calling this method with no arguments returns the list of parameter names. I: these are not the same as the L (although those will be copied here before C is called), nor are they the same thing as L (although the resolved dependencies will be copied here before C is called). =head2 C When declaring a service using the L<< C helper function|Bread::Board/service >>, if the name you use starts with a C<'+'>, the service definition will extend an existing service with the given name (without the C<'+'>). This method implements the extension semantics: the C and C options will be merged with the existing values, rather than overridden. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service/0000775000175000017500000000000013505520232017234 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/Service/Deferred.pm0000644000175000017500000000753713505520232021324 0ustar yanickyanickpackage Bread::Board::Service::Deferred; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: Helper for holding a service that is not quite constructed yet $Bread::Board::Service::Deferred::VERSION = '0.37'; use Moose (); use overload # cover your basic operatins ... 'bool' => sub { 1 }, '""' => sub { $_[0] = $_[0]->{service}->get; if (my $func = overload::Method($_[0], '""')) { return $_[0]->$func(); } return overload::StrVal($_[0]); }, # cover your basic dereferncers '%{}' => sub { return $_[0] if (caller)[0] eq 'Bread::Board::Service::Deferred'; $_[0] = $_[0]->{service}->get; $_[0] }, '@{}' => sub { $_[0] = $_[0]->{service}->get; $_[0] }, '${}' => sub { $_[0] = $_[0]->{service}->get; $_[0] }, '&{}' => sub { $_[0] = $_[0]->{service}->get; $_[0] }, '*{}' => sub { $_[0] = $_[0]->{service}->get; $_[0] }, ## and as a last ditch resort ... nomethod => sub { $_[0] = $_[0]->{service}->get; return overload::StrVal($_[0]) if $_[3] eq '""' && !overload::Method($_[0], $_[3]); if (my $func = overload::Method($_[0], $_[3])) { return $_[0]->$func($_[1]); } return $_[0]; # if all else fails, just return the object }, ; sub new { my ($class, %params) = @_; (Scalar::Util::blessed($params{service}) && $params{service}->does('Bread::Board::Service')) || Carp::confess "You can only defer Bread::Board::Service instances"; bless { service => $params{service} } => $class; } sub meta { if ($_[0]->{service}->can('class')) { my $class = $_[0]->{service}->class; return $class->meta; } $_[0] = $_[0]->{service}->get; (shift)->meta; } sub can { if ($_[0]->{service}->can('class')) { my $class = $_[0]->{service}->class; return $class->can($_[1]); } $_[0] = $_[0]->{service}->get; (shift)->can(shift); } sub isa { if ($_[0]->{service}->can('class')) { my $class = $_[0]->{service}->class; return 1 if $class eq $_[1]; return $class->isa($_[1]); } $_[0] = $_[0]->{service}->get; (shift)->isa(shift); } sub DESTROY { (shift)->{service} = undef } sub AUTOLOAD { my ($subname) = our $AUTOLOAD =~ /([^:]+)$/; $_[0] = $_[0]->{service}->get; my $func = $_[0]->can($subname); (ref($func) eq 'CODE') || Carp::confess "You cannot call '$subname'"; goto &$func; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service::Deferred - Helper for holding a service that is not quite constructed yet =head1 VERSION version 0.37 =head1 DESCRIPTION Class for proxy objects used when L. This class uses a few nasty tricks: replacing C<$_[0]>, using C, overriding C C and C, heavy operator overloading... you should probably not take inspiration from this code unless you really know what you're doing. In practice, a variable containing an instance of C will have its value changed to the actual value instantiated by the service at the first opportunity, and you should not notice that this class was ever there. =for Pod::Coverage can isa meta new =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service/Deferred/0000775000175000017500000000000013505520232020754 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/Service/Deferred/Thunk.pm0000644000175000017500000000335713505520232022411 0ustar yanickyanickpackage Bread::Board::Service::Deferred::Thunk; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: Helper for using services with incomplete parameters $Bread::Board::Service::Deferred::Thunk::VERSION = '0.37'; use Moose; has 'thunk' => ( traits => [ 'Code' ], is => 'bare', isa => 'CodeRef', required => 1, handles => { 'inflate' => 'execute' } ); 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service::Deferred::Thunk - Helper for using services with incomplete parameters =head1 VERSION version 0.37 =head1 DESCRIPTION This class is used when L. Since the service needs parameters to instantiate its value, and no values were provided for those parameters, the best we can do is use a coderef that will accept the parameters and call C on the service. =head1 METHODS =head2 C my $service_value = $deferred_thunk->inflate(%service_parameters); This will call C on the service, passing it all the C<%service_parameters>. Normal parameter validation and service lifecycle apply. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service/Alias.pm0000644000175000017500000000402213505520232020617 0ustar yanickyanickpackage Bread::Board::Service::Alias; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: aliases another service $Bread::Board::Service::Alias::VERSION = '0.37'; use Moose; use Try::Tiny; has aliased_from_path => ( is => 'ro', isa => 'Str', ); has aliased_from => ( is => 'ro', does => 'Bread::Board::Service', lazy => 1, builder => '_build_aliased_from', handles => ['get'], # is this sufficient? ); with 'Bread::Board::Service'; sub _build_aliased_from { my $self = shift; my $path = $self->aliased_from_path; confess "Can't create an alias service without a service to alias from" unless $path; return try { $self->fetch($path); } catch { die "While resolving alias " . $self->name . ": $_"; }; } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service::Alias - aliases another service =head1 VERSION version 0.37 =head1 DESCRIPTION This L class implements L. =head1 ATTRIBUTES =head2 C Read-only string attribute, the path of the service this alias refers to (it can be an alias itself) =head2 C Lazy read-only attribute, built by calling L<< C|Bread::Board::Traversable/fetch >> on this service using the L as path to fetch =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service/Inferred.pm0000644000175000017500000001630113505520232021327 0ustar yanickyanickpackage Bread::Board::Service::Inferred; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: Helper for inferring a service from a Moose object $Bread::Board::Service::Inferred::VERSION = '0.37'; use Moose; use Moose::Util::TypeConstraints 'find_type_constraint'; use Try::Tiny; use Bread::Board::Types; use Bread::Board::ConstructorInjection; has 'current_container' => ( is => 'ro', isa => 'Bread::Board::Container', required => 1, ); has 'service' => ( is => 'ro', isa => 'Bread::Board::ConstructorInjection', predicate => 'has_service', ); has 'service_args' => ( is => 'ro', isa => 'HashRef', lazy => 1, default => sub { +{} } ); has 'infer_params' => ( is => 'ro', isa => 'Bool', default => sub { 0 }, ); sub infer_service { my $self = shift; my $type = shift; my $seen = shift || {}; my $type_constraint = find_type_constraint( $type ); my $current_container = $self->current_container; # the type must exist ... (defined $type_constraint) || confess "$type is not an existing valid Moose type"; # the type must be either # a class type, or a subtype # of object. ($type_constraint->isa('Moose::Meta::TypeConstraint::Class') || $type_constraint->is_subtype_of('Object')) || confess 'Only class types, role types, or subtypes of Object can be inferred. ' . 'I don\'t know what to do with type (' . $type_constraint->name . ')'; my %params = ( name => 'type:' . $type, ); if ($self->has_service) { my $service = $self->service; %params = ( %params, name => $service->name, class => $service->class, dependencies => $service->dependencies, parameters => $service->parameters, ); } else { %params = ( %params, %{ $self->service_args } ); } # if the class is specified, then # we can use that reliably, otherwise # we need to try and figure out the # class name ... unless ( exists $params{'class'} ) { # if it is a class type, it is easy if ($type_constraint->isa('Moose::Meta::TypeConstraint::Class')) { $params{'class'} = $type_constraint->class; } # if it is not a class type, then # we will make the assumption that # the name of the type constraint # is also the name of the class. else { $params{'class'} = $type_constraint->name; } } my $meta = Class::MOP::class_of($params{'class'}) || confess "Could not get the meta object for class(" . $params{'class'} . ")"; ($meta->isa('Moose::Meta::Class')) || confess "We can only infer Moose classes" . ($meta->isa('Moose::Meta::Role') ? (', ' . $meta->name . ' is a role and therefore not concrete enough') : ''); my @required_attributes = grep { $_->is_required && $_->has_type_constraint } $meta->get_all_attributes; $params{'dependencies'} ||= {}; $params{'parameters'} ||= {}; # defer this for now ... $seen->{ $type } = $params{'name'}; foreach my $attribute (@required_attributes) { my $name = $attribute->name; next if exists $params{'dependencies'}->{ $name }; my $type_constraint = $attribute->type_constraint; my $type_name = $type_constraint->isa('Moose::Meta::TypeConstraint::Class') ? $type_constraint->class : $type_constraint->name; my $service; if ($current_container->has_type_mapping_for( $type_name )) { $service = $current_container->get_type_mapping_for( $type_name ) } elsif ( exists $seen->{ $type_name } ) { if ( blessed($seen->{ $type_name }) ) { # if the type has already been # inferred, then we use it $service = $seen->{ $type_name }; } else { # if not, then we have to use # the built in laziness and # make it a dependency $service = Bread::Board::Dependency->new( service_path => $seen->{ $type_name } ); } } else { if ( $type_constraint->isa('Moose::Meta::TypeConstraint::Class') || $type_constraint->is_subtype_of('Object') ) { $service = Bread::Board::Service::Inferred->new( current_container => $self->current_container )->infer_service( $type_name, $seen ); } else { if ($self->infer_params) { $params{'parameters'}->{ $name } = { isa => $type_name }; } else { confess 'Only class types, role types, or subtypes of Object can be inferred. ' . 'I don\'t know what to do with type (' . $type_name . ')'; } } } $params{'dependencies'}->{ $name } = $service if defined $service; } if ( $self->infer_params ) { map { $params{'parameters'}->{ $_->name } = { optional => 1, ($_->has_type_constraint ? ( isa => $_->type_constraint ) : ()) }; } grep { ( not $_->is_required ) } $meta->get_all_attributes } # NOTE: # this is always going to be # constructor injection because # that is what we do when we # infer. No other type of # injection makes sense here. # - SL my $service; if ($self->has_service) { $service = $self->service->clone(%params); } else { $service = Bread::Board::ConstructorInjection->new(%params); } # NOTE: # We need to do this so that # anything created by a typemap # can still also refer back to # an actual service in the parent # container. # - SL $self->current_container->add_service( $service ); $service; } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service::Inferred - Helper for inferring a service from a Moose object =head1 VERSION version 0.37 =head1 DESCRIPTION CAUTION, EXPERIMENTAL FEATURE. Docs to come, as well as refactoring. =head1 METHODS =head2 C =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service/WithClass.pm0000644000175000017500000000340313505520232021471 0ustar yanickyanickpackage Bread::Board::Service::WithClass; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: role for services returning instances of a given class $Bread::Board::Service::WithClass::VERSION = '0.37'; use Moose::Role; use Module::Runtime (); use Bread::Board::Types; with 'Bread::Board::Service'; has 'class' => ( is => 'rw', isa => 'Str', predicate => 'has_class', ); before 'get' => sub { my $self = shift; Module::Runtime::require_module($self->class) if $self->has_class; }; no Moose::Role; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service::WithClass - role for services returning instances of a given class =head1 VERSION version 0.37 =head1 DESCRIPTION This a sub-role of L for services that return instances of a given class. =head1 ATTRIBUTES =head2 C Read/write string attribute, the name of the class that this service will probably instantiate. =head1 METHODS =head2 C Predicate for the L attribute, true if it has been set. =head2 C This role adds a C modifier to the C method, ensuring that the module implementing the L is loaded. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service/WithParameters.pm0000644000175000017500000001201713505520232022530 0ustar yanickyanickpackage Bread::Board::Service::WithParameters; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: Services with parameters $Bread::Board::Service::WithParameters::VERSION = '0.37'; use Moose::Role; use MooseX::Params::Validate qw(validated_hash); use Bread::Board::Types; with 'Bread::Board::Service'; has 'parameters' => ( traits => [ 'Hash', 'Copy' ], is => 'ro', isa => 'Bread::Board::Service::Parameters', lazy => 1, coerce => 1, builder => '_build_parameters', handles => { 'has_parameters' => 'count' } ); has '_parameter_keys_to_remove' => ( is => 'rw', isa => 'ArrayRef', clearer => '_clear_parameter_keys_to_remove', predicate => '_has_parameter_keys_to_remove', ); before 'get' => sub { my $self = shift; my %params = $self->check_parameters(@_); $self->_parameter_keys_to_remove( [ keys %params ] ); $self->params({ %{ $self->params }, %params }); }; after 'get' => sub { my $self = shift; return unless $self->_has_parameter_keys_to_remove; map { $self->_clear_param( $_ ) } @{ $self->_parameter_keys_to_remove }; $self->_clear_parameter_keys_to_remove; }; sub _build_parameters { +{} } sub check_parameters { my $self = shift; return validated_hash(\@_, ( %{ $self->parameters }, # NOTE: # cache the parameters in a per-service # basis, this should be more than adequate # since each service can only have one set # of parameters at a time. If this does end # up breaking then we can give it a better # key at that point. # - SL (MX_PARAMS_VALIDATE_CACHE_KEY => Scalar::Util::refaddr($self)) )) if $self->has_parameters; return (); } sub has_required_parameters { my $self = shift; scalar grep { ! $_->{optional} } values %{ $self->parameters }; } sub has_parameter_defaults { my $self = shift; scalar grep { $_->{default} } values %{ $self->parameters }; } no Moose::Role; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service::WithParameters - Services with parameters =head1 VERSION version 0.37 =head1 DESCRIPTION This is a sub-role of L, for parameterized services. These are services that will instantiate different values depending on parameters that are passed to the C method. You can pass those parameters via the L<< C attribute of C|Bread::Board::Dependency/service_params >>, or via the L<< C method of C|Bread::Board::Service::Deferred::Thunk/inflate >>. =head1 ATTRIBUTES =head2 C Read-only hashref, will be passed as-is to L<< C's C|MooseX::Params::Validate/validated_hash >>, so you can use things like C and C in addition to type constraints: service something => ( class => 'Thing', parameters => { type => { isa => 'Str', default => 'text' }, }, ); This attribute uses coercions on L<< C|Bread::Board::Types/Bread::Board::Service::Parameters >> so that you can also say: service something => ( class => 'Thing', parameters => ['type'], ); and it will be equivalent to: service something => ( class => 'Thing', parameters => { type => { optional => 0 }, }, ); =head1 METHODS =head2 C Predicate for the L attribute. =head2 C Returns true if any of the L have a C value. =head2 C Returns true if any of the L does I have C set to true. =head2 C my %parameters = $service->check_parameters(name1=>$value1,name2=>$value2); my %parameters = $service->check_parameters({name1=>$value1,name2=>$value2}); If any L are defined, this function validates its arguments against the parameters' definitions (using L). It will die if the validation fails, or return the validated parameters (including default value) if it succeeds. =head2 C I the C method, arguments to C are passed through L and added to the L<< C|Bread::Board::Service/params >> hashref. I the C method, those keys/values will be removed. In practice, this makes all parameters available to the actual C method body. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service/WithDependencies.pm0000644000175000017500000001535313505520232023021 0ustar yanickyanickpackage Bread::Board::Service::WithDependencies; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: Services with dependencies $Bread::Board::Service::WithDependencies::VERSION = '0.37'; use Moose::Role; use Try::Tiny; use Bread::Board::Types; use Bread::Board::Service::Deferred; use Bread::Board::Service::Deferred::Thunk; with 'Bread::Board::Service'; has 'dependencies' => ( traits => [ 'Hash', 'Clone' ], is => 'rw', isa => 'Bread::Board::Service::Dependencies', lazy => 1, coerce => 1, default => sub { +{} }, trigger => sub { my $self = shift; $_->parent($self) foreach values %{$self->dependencies}; }, handles => { 'add_dependency' => 'set', 'get_dependency' => 'get', 'has_dependency' => 'exists', 'has_dependencies' => 'count', 'get_all_dependencies' => 'kv', } ); around 'init_params' => sub { my $next = shift; my $self = shift; +{ %{ $self->$next() }, $self->resolve_dependencies } }; after 'get' => sub { (shift)->clear_params }; sub resolve_dependencies { my $self = shift; my %deps; if ($self->has_dependencies) { foreach my $dep ($self->get_all_dependencies) { my ($key, $dependency) = @$dep; my $service = $dependency->service; # NOTE: # this is what checks for # circular dependencies if ($service->is_locked) { confess "You cannot defer a parameterized service" if $service->does('Bread::Board::Service::WithParameters') && $service->has_parameters; $deps{$key} = Bread::Board::Service::Deferred->new(service => $service); } else { # since we can't pass in parameters here, # we return a deferred thunk and you can do # with it what you will. if ( $service->does('Bread::Board::Service::WithParameters') && $service->has_required_parameters && (not $service->has_parameter_defaults) && (not $dependency->has_service_params) ) { $deps{$key} = Bread::Board::Service::Deferred::Thunk->new( thunk => sub { my %params = @_; $service->lock; return try { $service->get( %params ) } finally { $service->unlock } catch { die $_ } } ); } else { $service->lock; try { $deps{$key} = $dependency->has_service_params ? $service->get( %{ $dependency->service_params }) : $service->get; } finally { $service->unlock } catch { die $_ }; } } } } return %deps; } no Moose::Role; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service::WithDependencies - Services with dependencies =head1 VERSION version 0.37 =head1 DESCRIPTION This is a sub-role of L, for services with dependencies. It provides the mechanism to recursively resolve dependencies. =head1 ATTRIBUTES =head2 C Hashref, constrained by L<< C|Bread::Board::Types/Bread::Board::Service::Dependencies >>. Values must be instances of L, but can be coerced from various other types, see L. =head1 METHODS =head2 C $service->add_dependency(name=>$dep); Adds a new dependency. =head2 C my $dep = $service->get_dependency('name'); Gets a dependency by name. =head2 C if ($service->has_dependency('name')) { ... } Returns true if this service has a dependency with the given name. =head2 C if ($service->has_dependencies) { ... } Returns true if this service has any dependency. =head2 C my %deps = $service->get_all_dependencies; Returns all the dependencies for this service, as a key-value list. =head2 C Builder for the service parameters, augmented to inject all the L into the L<< C|Bread::Board::Service/params >> attribute, so that C can use them. =head2 C I the C method, the L<< C|Bread::Board::Service/params >> attribute is cleared, to make sure that dependencies will be resolved again on the next call (of course, if the service is using a L, the whole "getting" only happens once). =head2 C my %name_object_map = $self->resolve_dependencies; For each element of L, calls its L<< C|Bread::Board::Dependency/service >> method to retrieve the service we're dependent on, then tries to instantiate the value of the service. This can happen in a few different ways: =over 4 =item the service is not locked, and does not require any parameter just call C on it =item the service is not locked, requires parameters, but the dependency has values for them call C<< $service->get(%{$dependency->service_params}) >> =item the service is not locked, requires parameters, and we don't have values for them we can't instantiate anything at this point, so we use a L instance, on which you can call the C method, passing it all the needed parameters, to get the actual instance =item the service is locked we return a L that will proxy to the instance that the service will eventually return; yes, this means that in many cases circular dependencies can be resolved, at the cost of a proxy object =back =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Service/WithConstructor.pm0000644000175000017500000000316013505520232022751 0ustar yanickyanickpackage Bread::Board::Service::WithConstructor; our $AUTHORITY = 'cpan:STEVAN'; $Bread::Board::Service::WithConstructor::VERSION = '0.37'; use Moose::Role; use Try::Tiny; with 'Bread::Board::Service::WithClass'; has 'constructor_name' => ( is => 'rw', isa => 'Str', lazy => 1, builder => '_build_constructor_name', ); sub _build_constructor_name { my $self = shift; # using Class::MOP::class_of on a Moo # object causes mayhem, so we take care of that # special case first. See GH#61 try { $self->class->isa('Moo::Object') && 'new' } || try { Class::MOP::class_of($self->class)->constructor_name } || 'new'; } no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Service::WithConstructor =head1 VERSION version 0.37 =head1 DESCRIPTION =head1 METHODS =over 4 =item B =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Container/0000775000175000017500000000000013505520232017556 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/Container/Parameterized.pm0000644000175000017500000001301313505520232022704 0ustar yanickyanickpackage Bread::Board::Container::Parameterized; our $AUTHORITY = 'cpan:STEVAN'; $Bread::Board::Container::Parameterized::VERSION = '0.37'; use Moose; use Moose::Util 'find_meta'; use Bread::Board::Container::FromParameterized; # ABSTRACT: A parameterized container use Bread::Board::Container; with 'Bread::Board::Traversable'; has 'name' => ( is => 'rw', isa => 'Str', required => 1 ); has 'allowed_parameter_names' => ( is => 'ro', isa => 'ArrayRef', required => 1, ); has 'container' => ( is => 'ro', isa => 'Bread::Board::Container', lazy => 1, builder => '_build_container', handles => [qw[ add_service get_service has_service get_service_list has_services services add_sub_container get_sub_container has_sub_container get_sub_container_list has_sub_containers sub_containers ]] ); sub _build_container { my $self = shift; Bread::Board::Container->new( name => $self->name ) } sub fetch { die "Cannot fetch from a parameterized container"; } sub resolve { die "Cannot resolve from a parameterized container"; } sub create { my ($self, %params) = @_; my @allowed_names = sort @{ $self->allowed_parameter_names }; my @given_names = sort keys %params; (scalar @allowed_names == scalar @given_names) || confess "You did not pass the correct number of parameters"; ((join "" => @allowed_names) eq (join "" => @given_names)) || confess "Incorrect parameter list, got: (" . (join "" => @given_names) . ") expected: (" . (join "" => @allowed_names) . ")"; my $clone = $self->container->clone( name => ($self->container->name eq $self->name ? join "|" => $self->name, @given_names : $self->container->name) ); my $from_parameterized_meta = find_meta('Bread::Board::Container::FromParameterized'); $clone = $from_parameterized_meta->rebless_instance($clone); if ($self->has_parent) { my $cloned_parent = $self->parent->clone; $cloned_parent->sub_containers({ %{ $cloned_parent->sub_containers }, $self->name => $clone, }); $clone->parent($cloned_parent); } foreach my $key ( @given_names ) { $clone->add_sub_container( $params{ $key }->clone( name => $key ) ); } $clone; } __PACKAGE__->meta->make_immutable; no Moose; no Moose::Util; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Container::Parameterized - A parameterized container =head1 VERSION version 0.37 =head1 DESCRIPTION This class implements a sort of container factory for L: a parameterized container is a, in practice, a function from a set of parameters (which must be containers) to an actual container. See L for an example. =head1 ATTRIBUTES =head2 C Read/write string, required. Every container needs a name, by which it can be referenced when L. =head2 C Read-only arrayref of strings, required. These are the names of the containers that must be passed to L<< C|create ( %params ) >> to get an actual container out of this parameterized object. =head2 C This attribute holds the "prototype" container. Services inside it can depend on service paths that include the container names given in L. =head1 METHODS =head2 C =head2 C =head2 C =head2 C =head2 C =head2 C =head2 C =head2 C =head2 C =head2 C All these methods are delegated to the "prototype" L, so that this object can be defined as if it were a normal container. =head2 C my $container = $parameterized_container->create(%params); After checking that the keys of C<%params> are exactly the same strings that are present in L, this method clones the prototype L, adds the C<%params> to the clone as sub-containers, and returns the clone. If this was not a top-level container, the parent is also cloned, and the container clone is added to the parent clone. Please note that the container returned by this method does I have the same name as the parameterized container, and that calling this method with different parameter values will return different containers, but all with the same name. It's probably a bad idea to instantiate a non-top-level parameterized container more than once. =head2 C =head2 C These two methods die, since services in a parameterized container won't usually resolve, and attempting to do so is almost always a mistake. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Container/FromParameterized.pm0000644000175000017500000000267413505520232023543 0ustar yanickyanickpackage Bread::Board::Container::FromParameterized; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: container with weak parent reference $Bread::Board::Container::FromParameterized::VERSION = '0.37'; use Moose; extends 'Bread::Board::Container'; has '+parent' => ( weak_ref => 0, ); __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Container::FromParameterized - container with weak parent reference =head1 VERSION version 0.37 =head1 DESCRIPTION When L an actual container from a L, the returned container is re-blessed into this class. The only difference between this class and L is that the C attribute here is a weak reference. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Manual.pod0000644000175000017500000000711013505520232017552 0ustar yanickyanick# PODNAME: Bread::Board::Manual # ABSTRACT: A manual for Bread::Board __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Manual - A manual for Bread::Board =head1 VERSION version 0.37 =head1 INTRODUCTION As we have said, Bread::Board is an inversion of control framework with a focus on dependency injection and lifecycle management. The goal that Bread::Board strives towards is to promote more decoupled designs. It does this by removing the need for your objects to be concerned with resolving and/or creating their dependencies as well as knowing (or caring) what their lifecycle is. =head2 Why should I use Bread::Board First, it should be noted that Bread::Board and IoC are not universally useful. Smaller applications and one-off scripts would be overburdened by the abstractions of Bread::Board. But the larger your application becomes, the more something like Bread::Board can help. As applications grow so the management of resources and their dependencies becomes more of a burden. Making sure all your components are properly initialized, in the right order and at all times that you need them, can become a twisty maze. Bread::Board is intended to help you manage this twisty maze and remove the need for you to manage this manually. Take your typical Catalyst application, the Catalyst framework itself contains its own mini IoC framework through the component subsystem. Catalyst will manage your models and views making sure that they will be available when you need them in the controllers. Catalyst makes all this easy to manage through its configuration system. This is great inside your Catalyst application, but what about outside of it? Any sufficiently complex web application will have a set of scripts and/or command-line applications to help support it. At this point you are left to your own devices and must manage these components and their dependency chains on your own. By decoupling IoC into its own stand-alone subsystem it becomes possible to get that same ease-of-use you get inside something like a Catalyst application, outside of it. This, in a nutshell, is what Bread::Board aims to provide. =head1 SECTIONS =over 4 =item L This is perhaps the best place to start, it will introduce you to the basic concepts that make up Bread::Board. =item L With Bread::Board you provide names for your service so that you can find them later. This document explores the new (read: experimental) typemap feature which allows you to map services to types as well. =item L Bread::Board is an extensible and open system, this document will explore how you can extend Bread::Board and get greater re-use of your components. =item L This is a set of examples meant to show how Bread::Board can be used in real-world scenarios. It is recommended that you read the above documentation first as many of these examples use the concepts discussed in them. =back =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/LifeCycle.pm0000644000175000017500000000222013505520232020023 0ustar yanickyanickpackage Bread::Board::LifeCycle; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: base lifecycle role $Bread::Board::LifeCycle::VERSION = '0.37'; use Moose::Role; no Moose::Role; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::LifeCycle - base lifecycle role =head1 VERSION version 0.37 =head1 DESCRIPTION This is an empty role. Roles that define L should consume this role. For an example, see L and L. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Manual/0000775000175000017500000000000013505520232017051 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/Manual/Concepts.pod0000644000175000017500000004401313505520232021333 0ustar yanickyanick# PODNAME: Bread::Board::Manual::Concepts # ABSTRACT: An overview of the concepts in Bread::Board __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Manual::Concepts - An overview of the concepts in Bread::Board =head1 VERSION version 0.37 =head1 INTRODUCTION This document attempts to convey the central concepts of Bread::Board and show how they work together to manage both object lifecycles and object dependencies. In this document we use the raw OO syntax of Bread::Board, this is so that the concepts being illustrated are not clouded by syntactic sugar. We only introduce the I layer at the end, at which point we hope that it will become clear what is going on "under the hood" when you use it. =head1 CONCEPTS =head2 What is Inversion of Control? Inversion of Control (or IoC) is the very simple idea of releasing control of some part of your application over to some other part of your application, be it your code or an outside framework. IoC is a common paradigm in GUI frameworks, whereby you give up control of your application flow to the framework and install your code at callbacks hooks within the framework. For example, take a very simple command line interface; the application asks a question, the user responds, the application processes the answer and asks another question, and so on until it is done. Now consider the GUI approach for the same application; the application displays a screen and goes into an event loop, users actions are processed with event handlers and callback functions. The GUI framework has inverted the control of the application flow and relieved your code from having to deal with it. IoC is also sometimes referred to as 'Dependency Injection' or the 'Dependency Injection Principle', and many people confused the two. However IoC and dependency injection are not the same, in fact the concepts behind dependency injection are actually just an I IoC principles in action, in particular about your applications dependency relationships. IoC is also sometimes referred to as the Hollywood Principle because of the I approach of things like callback functions and event handlers. Howard Lewis Ship, the creator of the HiveMind IoC Framework, once referred to dependency injection as being the inverse of garbage collection. With garbage collection you hand over the details of the destruction of your objects to the garbage collector. With dependency injection you are handing over control of object creation, which also includes the satisfaction of your dependency relationships. The following sections will explain the basis concepts around the Bread::Board and how it relates to the concept of IoC. =head2 Containers The central part of just about any IoC framework is the container. A container's responsibilities are roughly to dispense services and to handle the resolution of said service's dependency relationships. First we can start with a simple container for our services to live in. We give the container a name so that we can address it later on, think of this like a package namespace. my $c = Bread::Board::Container->new( name => 'Application' ); Next we need to add a service to that container (we will explain services a little later on). $c->add_service( Bread::Board::BlockInjection->new( name => 'logger', block => sub { Logger->new() } ) ); Now if we want an instance of our 'logger' service, we simply ask the container for it. my $logger_service = $c->fetch('logger'); And we then can ask the service to give us an instance of our Logger object. my $logger = $logger_service->get; Or if we want to make this even simpler we can use the C method of the container object. my $logger = $c->resolve( service => 'logger' ); The C method will look up the service asked for and return the instance, which is basically equivalent to the chained C and C calls above. =head2 Dependency Management Dependency management is also quite simple, and is easily shown with an example. But first lets create another component for our container, a database connection. $c->add_service( Bread::Board::BlockInjection->new( name => 'db_conn', block => sub { DBI->connect('dbi:mysql:test', '', '') } ) ); Now lets add an authenticator to our container. The authenticator requires both a database connection and a logger instance in its constructor. We specify dependency relationships between services by providing a HASH of Bread::Board::Dependency objects which themselves have a path to the services they depend upon. In this case since all these services are in the same container, the service path is simply the name. $c->add_service( Bread::Board::BlockInjection->new( name => 'authenticator', block => sub { my $service = shift; Authenticator->new( db_conn => $service->param('db_conn'), logger => $service->param('logger') ); }, dependencies => { db_conn => Bread::Board::Dependency->new( service_path => 'db_conn' ), logger => Bread::Board::Dependency->new( service_path => 'logger' ), } ) ); As you can see, the first argument to our service subroutine is actually our service instance. Through this we can access the resolved dependencies and use them in our Authenticator object's constructor. The above example is deceptively simple, but really powerful. What you don't see on the surface is that Bread::Board is completely managing initialization order for you. No longer to do you need to worry if your database is connected or your logger initialized and in what order you need to do that initialization, Bread::Board handles that all for you, including circular dependencies. This may not seem terribly interesting in such a small example, but the larger an application grows, the more sensitive it becomes to these kinds of initialization order issues. =head2 Lifecycle Management The default lifecycle for Bread::Board::Service components is a 'prototype' lifecycle, which means each time we ask for say, the logger, we will get a new instance back. There is also another option for lifecycle management that we call 'Singleton'. Here is an example of how we would use the 'Singleton' lifecycle to ensure that you always get back the same logger instance. $c->add_service( Bread::Board::BlockInjection->new( lifecycle => 'Singleton', name => 'logger', block => sub { Logger->new() } ) ); Now each time we request a new logger component from our container we will get the exact same instance. Being able to change between the different lifecycles by simply changing one service parameter can come in very handy as you application grows. Extending this idea, it is possible to see how you could create your own custom service objects to manage your specific lifecycle needs, such as a pool of database connections. =head2 Services Up until now, we have shown the default way of creating a service by using the Bread::Board::BlockInjection and an anonymous subroutine. But this is not the only way to go about this. Those who have encountered IoC in the Java world may be familiar with the idea that there are 3 'types' of IoC/Dependency Injection; Constructor Injection, Setter Injection, and Interface Injection. In Bread::Board we support both Constructor and Setter injection, it is the authors opinion though that Interface injection was not only too complex, but highly java specific and the concept did not adapt itself well to perl. =over 4 =item Block Injection While not in the 'official' 3 types (mostly because it's not possible in Java), but found in a few Ruby IoC frameworks, BlockInjection is by far the most versatile type. It simply requires a subroutine and a name and you do all the rest of it yourself. $c->add_service( Bread::Board::BlockInjection->new( name => 'logger', class => 'ComplexLogger', block => sub { my $s = shift; my $l = ComplexLogger->new( file => $s->param('log_file') ); $l->init_with_timezone( $s->param('timezone') ); $l->log_timestamp; $l; }, dependencies => { log_file => Bread::Board::Dependency->new( service_path => 'log_file' ), timezone => Bread::Board::Dependency->new( service_path => 'timezone' ), } ) ); BlockInjection comes in really handy when your object requires more then just constructor parameters and needs some more complex initialization code. As long as your subroutine block returns an object, everything else is fair game. Also note the optional 'class' parameter, which when supplied will perform a basic type check on the result of the subroutine block. =item Constructor Injection Bread::Board also supports Constructor Injection. With constructor injection, the service calls the class's constructor and feeds it the dependencies you specify. This promotes what is called a "Good Citizen" object, or an object who is completely initialized upon construction. $c->add_service( Bread::Board::ConstructorInjection->new( name => 'authenticator', class => 'Authenticator', dependencies => { db_conn => Bread::Board::Dependency->new( service_path => 'db_conn' ), logger => Bread::Board::Dependency->new( service_path => 'logger' ), } ) ); Since Bread::Board is built both with L and for use with L objects, it makes the assumption here that the constructor takes named arguments. Here is our earlier authenticator service rewritten to use constructor injection. This is by far the simplest injection type as it requires little more then a class name and a HASH of dependencies. =item Setter Injection Bread::Board also supports Setter Injection. The idea behind setter injection is that for each component dependency a corresponding setter method must exist. This style has been popularized by the Spring java framework. I will be honest, I don't find this type of injection as useful as block or constructor, but it can come in handy if your object prefers you to call setters to initialize it. Here is a fairly contrived example using the L module. $c->add_service( Bread::Board::SetterInjection->new( name => 'json', class => 'JSON', dependencies => { utf8 => Bread::Board::Literal->new( name => 'true', value => 1 ) pretty => Bread::Board::Literal->new( name => 'true', value => 1 ) } ) ); Setter injection actually creates the object without passing any arguments to the constructor, then loops through the keys in the dependency HASH and treats each key as a method name, and each value as that method's argument. In this case, the above is the equivalent of doing: my $json = JSON->new; $json->utf8(1); $json->pretty(1); You might have been wondering about the fact we didn't specify Bread::Board::Dependency objects in our dependency HASH, but instead supplied Bread::Board::Literal instances. Bread::Board::Literal is just another Service type that simply holds a literal value, or a constant. When dependencies are specified like this, Bread::Board internally converts them into Bread::Board::Dependency whose service is already resolved to that service. =back =head2 Hierarchical Containers Up until now, we have seen basic containers which only have a single level of components. As your application grows larger it may become useful to have a more hierarchical approach to your containers. Bread::Board::Container supports this behavior through its many sub-container methods. Here is an example of how we might re-arrange the previous examples using sub-containers. my $app_c = Bread::Board::Container->new( name => 'app' ); my $db_c = Bread::Board::Container->new( name => 'database' ); $db_c->add_service( Bread::Board::BlockInjection->new( name => 'db_conn' block => sub { my $s = shift; return DBI->connect( $s->param('dsn'), $s->param('username'), $s->param('password') ); }, dependencies => { dsn => Bread::Board::Literal->new( name => 'dsn', value => 'dbi:mysql:test' ), username => Bread::Board::Literal->new( name => 'username', value => 'user' ), password => Bread::Board::Literal->new( name => 'password', value => '****' ), } ) ); $app_c->add_sub_container( $db_c ); my $log_c = Bread::Board::Container->new( name => 'logging' ); $log_c->add_service( Bread::Board::Literal->new( name => 'log_file', value => '/var/log/app.log' ) ); $log_c->add_service( Bread::Board::ConstructorInjection->new( name => 'logger', class => 'Logger', dependencies => { log_file => Bread::Board::Dependency->new( service_path => 'log_file' ) } ) ); $app_c->add_sub_container( $log_c ); my $sec_c = Bread::Board::Container->new( name => 'security' ); $sec_c->add_service( Bread::Board::ConstructorInjection->new( name => 'authenticator', class => 'Authenticator', dependencies => { db_conn => Bread::Board::Dependency->new( service_path => '../database/db_conn' ), logger => Bread::Board::Dependency->new( service_path => '../logging/logger' ), } ) ); $app_c->add_sub_container( $sec_c ); $app_c->add_service( Bread::Board::ConstructorInjection->new( name => 'app', class => 'Application', dependencies => { auth => Bread::Board::Dependency->new( service_path => '/security/authenticator' ), db_conn => Bread::Board::Dependency->new( service_path => '/database/db_conn' ), logger => Bread::Board::Dependency->new( service_path => '/logging/logger' ), } ) ); So, as an example that can be seen above, hierarchical containers can be used as a form of namespacing to organize your Bread::Board configuration better. As it is shown with the 'authenticator' service, it is possible to address services outside of your container using path notation. In this case the 'authenticator' service makes the assumption that its parent container has both a 'database' and a 'logging' sub-container and they contain a 'db_conn' and 'logger' service respectively. And as is shown in the 'app' service, it is also possible to address services using an absolute path notation. =head2 Sugar Layer So, up until now we have been creating all our Bread::Board objects by hand. As you can tell, this is both verbose and tedious. To make your life easier, Bread::Board provides a simple I layer over these objects. Here is the equivalent of the above Bread::Board configuration using the sugar layer. my $c = container 'app' => as { container 'database' => as { service 'db_conn' => ( block => sub { my $s = shift; return DBI->connect( $s->param('dsn'), $s->param('username'), $s->param('password') ); }, dependencies => { dsn => ( service 'dsn' => 'dbi:mysql:test' ), username => ( service 'username' => 'user' ), password => ( service 'password' => '****' ), } ) }; container 'logging' => as { service 'log_file' => '/var/log/app.log'; service 'logger' => ( class => 'Logger', dependencies => { log_file => depends_on('log_file'), } ) }; container 'security' => as { service 'authenticator' => ( class => 'Authenticator', dependencies => { db_conn => depends_on('../database/db_conn'), logger => depends_on('../logging/logger'), } ) }; service 'app' => ( class => 'Application', dependencies => { auth => depends_on('/security/authenticator'), db_conn => depends_on('/database/db_conn'), logger => depends_on('/logging/logger'), } ) }; As you can see this not only makes the code shorter, but more declarative and easier to read. =head1 SEE ALSO This article is based on an article I wrote for The Perl Journal about my earlier L module. That article can be found online at L. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Manual/Concepts/0000775000175000017500000000000013505520232020627 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/Manual/Concepts/Typemap.pod0000644000175000017500000001175413505520232022760 0ustar yanickyanick# PODNAME: Bread::Board::Manual::Concepts::Typemap # ABSTRACT: An overview of the typemapping feature __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Manual::Concepts::Typemap - An overview of the typemapping feature =head1 VERSION version 0.37 =head1 INTRODUCTION A new (read: experimental) feature of Bread::Board is typemapped services. These are services which are mapped to a particular type rather then just a name. This feature has the potential to make obsolete a large amount of the Bread::Board configuration by simply asking Bread::Board to figure things out on its own. Here is a small example of how this works. # define the classes making sure # to specify required items and # their types { package Stapler; use Moose; package Desk; use Moose; package Chair; use Moose; package Cubicle; use Moose; has 'desk' => ( is => 'ro', isa => 'Desk', required => 1 ); has 'chair' => ( is => 'ro', isa => 'Chair', required => 1 ); package Employee; use Moose; has [ 'first_name', 'last_name' ] => ( is => 'ro', isa => 'Str', required => 1, ); has 'stapler' => ( is => 'rw', isa => 'Stapler', predicate => 'has_stapler' ); has 'work_area' => ( is => 'ro', isa => 'Cubicle', required => 1 ); } # now create the container, and # map the Employee type and ask # Bread::Board to infer all the # other relationships my $c = container 'Initech' => as { typemap 'Employee' => infer; }; # now you can create new Employee objects # by calling ->resolve with the type and # supplying the required parameters (see # below for details). my $micheal = $c->resolve( type => 'Employee', parameters => { first_name => 'Micheal', last_name => 'Bolton' } ); my $cube = $micheal->work_area; # this will be a Cubicle object $cube->desk; # this will be a Desk object $cube->chair; # this will be a Chair object $micheal->has_stapler; # this is false # We can create another Employee object # and this time we pass in the optional # parameter for the non-required 'stapler' # attribute my $milton = $c->resolve( type => 'Employee', parameters => { first_name => 'Milton', last_name => 'Waddams', stapler => Stapler->new } ); $milton->has_stapler; # this is true In the above example, we created a number of Moose classes that had specific required relationships. When we called C for the B object, Bread::Board figured out those relationships and set up dependencies and parameters accordingly. For the C object, we saw the B type and then basically called C on the B object. We then saw the B and B objects and called C on those as well. The result of this recursive inference was that the B, B, B and B relationships were modeled in Bread::Board as dependent services. Bread::Board also took it one step further. We were able to resolve the B, B and B types automatically because they were already defined by Moose as subtypes of the I type. We knew that it could introspect those classes and get more information. However, this was not the case with the I and I attributes of the B object. In that case, we determined that we couldn't resolve those objects and (because it was a top-level inference) instead turned them into required parameters for the inferred B service. And lastly, with a top-level inference (not one caused by recursion) Bread::Board will also look at all the remaining non-required attributes and turn them into optional parameters. In this case we have a C attribute that is not required and so is listed as an optional parameter, meaning that it is not required, but still subject to type checking. =head1 CONCLUSION This example should give a good basic overview of this feature and more details can be found in the test suite (F). These show examples of how to typemap roles to concrete classes and how to supply hints to C to help Bread::Board figure out specific details. As I mentioned above, this feature should be considered experimental and we are still working out details and writing tests for it. Any contributions are welcome. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Manual/Concepts/Advanced.pod0000644000175000017500000001541013505520232023037 0ustar yanickyanick# PODNAME: Bread::Board::Manual::Concepts::Advanced # ABSTRACT: An overview of some of the more advanced Bread::Board concepts __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Manual::Concepts::Advanced - An overview of some of the more advanced Bread::Board concepts =head1 VERSION version 0.37 =head1 INTRODUCTION In the L document we attempted to explain the conceptual foundations of Bread::Board. In that we exposed you to the idea of a container and a service and showed how they could be used. In that document we built a hierarchical container which organized different sets of services into what could be seen as subsystems within an overall application. While this alone has plenty of value, you might be asking yourself, what about re-use? Bread::Board already encourages decoupled object design by removing the need to manually wire your application components together, but what about re-using Bread::Board components themselves? This document will illustrate some of the more advanced concepts in Bread::Board with the specific focus on re-use and extension. =head1 ADVANCED CONCEPTS NOTE: This is just a quick sketch of these docs, more to come in the next few releases, for now I need to get this one out the door. =head2 Subclassing Bread::Board was built from the very start to be an open system and to allow for the subclassing of all its internal components. Here is a simple example of extending L to build a container specific to your application. package My::Application::Container; use Moose; use Bread::Board; extends 'Bread::Board::Container'; has 'log_file_name' => ( is => 'ro', isa => 'Str', default => 'logfile.log', ); sub BUILD { my $self = shift; container $self => as { service 'log_file' => $self->log_file_name; service 'logger' => ( class => 'My::FileLogger', lifecycle => 'Singleton', dependencies => { log_file => depends_on('log_file'), } ); service 'application' => ( class => 'My::Application', dependencies => { logger => depends_on('logger'), } ); }; } Then you can simply create an instance of the container and instantiate an instance of the application. my $c = My::Application::Container->new( name => 'MyLoggingContainer', log_file_name => 'other_logfile.log' ); my $app = $c->resolve( service => 'application'); It should be noted that when calling the constructor of a subclass of Bread::Board::Container, you must pass the "name" attribute as a parameter. Additionally you could use the C<+name> syntax in the subclass itself like so: has '+name' => ( default => 'MyLoggingContainer' ); which will remove the requirement in the constructor unless you choose to override it. It is also possible to extend/specialize a L type to customize it for your needs. More to come later. =head2 Parameterized Containers Extending containers is just one form of re-use, just like extending a class in plain old OOP. But Bread::Board also provides another means of re-use, and that is parameterized containers. If you are familiar with functors in Standard ML or O'Caml then this might look familiar to you. A parameterized container is basically a container which expects another container (or containers) as an argument and produces a third container as the result. Lets take a simple example here of a Logger object which logs to a database. my $db_logger = container 'DatabaseLogger' => [ 'DBConnInfo' ] => as { service 'handle' => ( class => 'My::Database::Logger', dependencies => { dsn => depends_on('DBConnInfo/dsn'), username => depends_on('DBConnInfo/username'), password => depends_on('DBConnInfo/password'), } ); }; It is parameterized with a C container which has three services, a C, a C and a C. Now let's create a simple container which fulfills these requirements. my $db_conn_info = container 'DatabaseConnection' => as { service 'dsn' => 'dbi:mysql:foo'; service 'username' => 'bar'; service 'password' => '***'; }; The above container fulfills the bare minimum, but this could have just as easily have been a much more complex container which also had a service for a L schema, or a L directory object. As long as the container provided the three required services, that was all that the C parameterized container required. Now, a parameterized container is not a usable container, you must create an instance of it. That is as simple as calling the C method, like so. my $my_db_logger = $db_logger->create( DBConnInfo => $db_conn_info ); After which you can use it just like any other Bread::Board container would be used. my $log_handle = $my_db_logger->resolve( service => 'handle' ); Parameterized containers can also be nested, here is an example of an Application container that expects a Logger. my $app = container 'Application' => [ 'Logger' ] => as { service 'app' => ( class => 'My::Application', dependencies => { log_handle => depends_on('Logger/handle') } ); }; And here we instantiate an instance of our Application container using the DatabaseLogger. my $db_app = $app->create( Logger => $db_logger->create( DBConnInfo => $db_conn_info ) ); And of course, since the Logger is a parameter we could just as easily pass in a simpler screen logger for a test environment or something. Here is what that would look like. my $simple_logger = container 'SimpleLogger' => as { service 'handle' => ( class => 'My::Simple::Logger' ); }; my $simple_app = $app->create( Logger => $simple_logger ); Parameterized containers provide a useful and powerful means of re-use and abstraction, making it easy to create flexible containers to model your applications subsystems. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Manual/Example.pod0000644000175000017500000000312713505520232021151 0ustar yanickyanick# PODNAME: Bread::Board::Manual::Example # ABSTRACT: A set of examples of Bread::Board usage __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Manual::Example - A set of examples of Bread::Board usage =head1 VERSION version 0.37 =head1 DESCRIPTION The goal of this set of documents it to provide examples of how you can use Bread::Board in different situations. It is still pretty raw and sparse right now, but more will come soon. Each example attempts to show a real world problem and how one might go about solving it with Bread::Board. These examples are currently are born out of discussions I had with people about how they might be able to utilize Bread::Board. =over 4 =item L An example of how to get more dynamic behavior for your dependencies. =item L An example of using parameterized containers and dynamic dependencies together to build a set of re-usable components within an application. =back =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Manual/Example/0000775000175000017500000000000013505520232020444 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/Manual/Example/LogDispatch.pod0000644000175000017500000001036413505520232023353 0ustar yanickyanick# PODNAME: Bread::Board::Manual::Example::LogDispatch # ABSTRACT: An example of composing a dynamic Log::Dispatch object. __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Manual::Example::LogDispatch - An example of composing a dynamic Log::Dispatch object. =head1 VERSION version 0.37 =head1 SYNOPSIS my $c = container 'Logging' => as { service 'Logger' => ( block => sub { my $s = shift; my $c = $s->parent; my $outputs = $c->get_sub_container('Outputs'); my $log = Log::Dispatch->new; foreach my $name ( $outputs->get_service_list ) { $log->add( $outputs->get_service( $name )->get ); } $log; } ); container 'Outputs' => as { service 'File' => ( block => sub { Log::Dispatch::File->new( name => 'file', min_level => 'debug', filename => 'logfile' ) } ); service 'Screen' => ( block => sub { Log::Dispatch::Screen->new( name => 'screen', min_level => 'warning', ) } ); }; }; my $logger = $c->resolve( service => 'Logging/Logger' ); =head1 DESCRIPTION This example was inspired by a discussion I had with Jay Shirley. He wanted to know an easy way to have a dynamic list of output types for his Log::Dispatch object. Often with Bread::Board you will be wiring up components that are of a fixed type and set, but this is not always the case. It is in these cases when you can simply use the Bread::Board objects themselves to fetch your dependencies. The value passed into the block of a BlockInjection service is the service itself. Calling the C method on that service will give you the container that service is in. From there you can introspect the other containers and services any which way you want to. This example can be made even more dynamic if you build the 'Logging' component as a parameterized container whose parameter is the 'Outputs' container. Here is what that would look like. my $logging = container 'Logging' => [ 'Outputs' ] => as { service 'Logger' => ( block => sub { my $s = shift; my $c = $s->parent; my $outputs = $c->get_sub_container('Outputs'); my $log = Log::Dispatch->new; foreach my $name ( $outputs->get_service_list ) { $log->add( $outputs->get_service( $name )->get ); } $log; } ); }; my $outputs = container 'Outputs' => as { service 'File' => ( block => sub { Log::Dispatch::File->new( name => 'file', min_level => 'debug', filename => 'logfile' ) } ); service 'Screen' => ( block => sub { Log::Dispatch::Screen->new( name => 'screen', min_level => 'warning', ) } ); }; my $c = $logging->create( Outputs => $outputs ); my $ld = $c->resolve( service => 'Logging/Logger' ); This example illustrates how when a parameterized container is instantiated, the parameters become sub-containers of the resulting container. This makes it just as easy to fetch the 'Outputs' container and use it inside the 'Logger' service. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Manual/Example/FormSensible.pod0000644000175000017500000001553513505520232023547 0ustar yanickyanick# PODNAME: Bread::Board::Manual::Example::FormSensible # ABSTRACT: A Form::Sensible and Catalyst example. __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Manual::Example::FormSensible - A Form::Sensible and Catalyst example. =head1 VERSION version 0.37 =head1 SYNOPSIS __PACKAGE__->config( # ... your other Catalyst configs ... # first put our universal # FormBuilder container # inside the config FormBuilder => container 'FormBuilder' => [ 'Fields' ] => as { service 'Form' => ( class => 'Form::Sensible', block => sub { my $s = shift; my $c = $s->parent; my $fields = $c->get_sub_container('Fields'); my $form = Form::Sensible::Form->new( name => $s->param('name') ); foreach my $name ( $fields->get_service_list ) { $form->add_field( $fields->get_service( $name )->get ); } if ( my $state = $s->param('state') ) { $form->set_values( $state ); } $form; }, parameters => { name => { isa => 'Str' }, state => { isa => 'HashRef', optional => 1 }, } ); }, # Then we can build a set of # Fields for the 'foo' form Fields => { foo => container 'FooFields' => [ 'Model' ] => as { service 'Username' => ( class => 'Form::Sensible::Field::Text', block => sub { Form::Sensible::Field::Text->new( name => 'username', validation => { regex => qr/^[0-9a-z]*$/ } ); } ); service 'Password' => ( class => 'Form::Sensible::Field::Text', block => sub { Form::Sensible::Field::Text->new( name => 'password', render_hints => { 'HTML' => { field_type => 'password' } } ); } ); service 'Submit' => ( class => 'Form::Sensible::Field::Trigger', block => sub { Form::Sensible::Field::Trigger->new( name => 'submit' ); } ); service 'AccessLevel' => ( class => 'Form::Sensible::Field::Select', block => sub { my $s = shift; my $select = Form::Sensible::Field::Select->new( name => 'access_level', ); foreach my $access_level ( $s->param('schema')->resultset('AccessLevels')->all ) { $select->add_option( $access_level->id, $access_level->name ); } $select; }, dependencies => { schema => depends_on('Model/schema') , }, ); } } ); # later, in a # catalyst action ... sub process_foo : Local { my ($self, $c) = @_; my $Model = container 'Model' => as { service 'schema' => $c->model('DBIC') }; my $Form = $c->config->{FormBuilder}->create( Fields => $c->config->{Fields}->{foo}->create( Model => $Model ) ); my $f = $Form->resolve( service => 'Form', parameters => { name => 'foo', state => $c->req->parameters } ); my $result = $f->validate; if ($result->is_valid) { # ... } else { # ... } } =head1 DESCRIPTION This example came out of a discussion with Jay Kuri about how Bread::Board might be used in conjunction with his Form::Sensible module. My idea was to create a generic form builder which is parameterized by a Fields container. This could be used to store all kind of application wide behaviors. Since this in the context of Catalyst it made sense to me for this to be stuffed into the Catalyst config hash. I also decided to use service parameters in the Form service, this allows you to pass in a specific name and to optionally pass in a captured state to the Form::Sensible::Form instance that is being created. The next idea was that the Fields container parameter could be created for each specific form in the application. In the above example all the services are hardcoded, but this could be made more re-usable using the C keyword from Bread::Board itself, or some degree of subclassing of the Container objects. Jay also asked about passing in the Catalyst model into the fields so that he could populate something like a select pull-down menu. Again I used parameterized modules, in this case we parameterized the FooFields container with a Model container which had a schema service (which was a DBIx::Class schema object). From here we move into a Catalyst action to show how this might be used. We start out by wrapping the Catalyst DBIC model with a simple container, and then proceed to build our C<$Form> object. The C<$Form> is a Bread::Board container born of 3 levels of parameterized containers, it is worth spending a little time pondering exactly what is happening there. So once we have the C<$Form> container, all we need to do is create an instance of our Form::Sensible::Form, passing in the name and the captured state. This example could likely be expanded even further to show the use of the Form::Sensible rendering as well. Further creative use of parameterized containers and a couple utility methods in the Catalyst controllers could produce fairly robust and easy to use API for an application. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/LifeCycle/0000775000175000017500000000000013505520232017473 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/LifeCycle/Singleton/0000775000175000017500000000000013505520232021435 5ustar yanickyanickBread-Board-0.37/lib/Bread/Board/LifeCycle/Singleton/WithParameters.pm0000644000175000017500000000614213505520232024733 0ustar yanickyanickpackage Bread::Board::LifeCycle::Singleton::WithParameters; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: singleton lifecycle role for a parameterized service $Bread::Board::LifeCycle::Singleton::WithParameters::VERSION = '0.37'; use Moose::Role; with 'Bread::Board::LifeCycle'; has 'instances' => ( traits => [ 'Hash', 'NoClone' ], is => 'rw', isa => 'HashRef', lazy => 1, default => sub { +{} }, clearer => 'flush_instances', handles => { 'has_instance_at_key' => 'exists', 'get_instance_at_key' => 'get', 'set_instance_at_key' => 'set', } ); around 'get' => sub { my $next = shift; my $self = shift; my $key = $self->generate_instance_key(@_); # return it if we got it ... return $self->get_instance_at_key($key) if $self->has_instance_at_key($key); # otherwise fetch it ... my $instance = $self->$next(@_); # if we get a copy, and our copy # has not already been set ... $self->set_instance_at_key($key => $instance) unless $self->has_instance_at_key($key); # return whatever we have ... return $self->get_instance_at_key($key); }; sub generate_instance_key { my ($self, @args) = @_; return "$self" unless @args; return join "|" => sort map { "$_" } @args } no Moose::Role; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::LifeCycle::Singleton::WithParameters - singleton lifecycle role for a parameterized service =head1 VERSION version 0.37 =head1 DESCRIPTION Sub-role of L, this role defines the "singleton" lifecycle for a parameterized service. The C method will only do its work the first time it is invoked for each set of parameters; subsequent invocations with the same parameters will return the same object. =head1 ATTRIBUTES =head2 C Hashref mapping keys to objects, used to cache the results of L =head1 METHODS =head2 C Generates a key using L (passing it all the arguments); if the L attribute does not hold an object for that key, it will build it (by calling the underlying C method) and store it in L. The object (either retrieved from L or freshly built) will be returned. =head2 C Generates a (hopefully) unique key from the given arguments (usually, whatever was passed to L). The current implementation stringifies all arguments, so different references to identical values will be considered different. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/LifeCycle/Singleton.pm0000644000175000017500000000576013505520232022001 0ustar yanickyanickpackage Bread::Board::LifeCycle::Singleton; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: service role for singleton lifecycle $Bread::Board::LifeCycle::Singleton::VERSION = '0.37'; use Moose::Role; use Try::Tiny; with 'Bread::Board::LifeCycle'; has 'instance' => ( traits => [ 'NoClone' ], is => 'rw', isa => 'Any', predicate => 'has_instance', clearer => 'flush_instance' ); has 'resolving_singleton' => ( traits => [ 'NoClone' ], is => 'rw', isa => 'Bool', default => 0, ); around 'get' => sub { my $next = shift; my $self = shift; # return it if we got it ... return $self->instance if $self->has_instance; my $instance; if ($self->resolving_singleton) { $instance = Bread::Board::Service::Deferred->new(service => $self); } else { $self->resolving_singleton(1); my @args = @_; try { # otherwise fetch it ... $instance = $self->$next(@args); } catch { die $_; } finally { $self->resolving_singleton(0); }; } # if we get a copy, and our copy # has not already been set ... $self->instance($instance); # return whatever we have ... return $self->instance; }; no Moose::Role; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::LifeCycle::Singleton - service role for singleton lifecycle =head1 VERSION version 0.37 =head1 DESCRIPTION Sub-role of L, this role defines the "singleton" lifecycle for a service. The C method will only do its work the first time it is invoked; subsequent invocations will return the same object. =head1 ATTRIBUTES =head2 C The object build by the last call to C to actually do any work, and returned by any subsequent call to C. =head1 METHODS =head2 C The first time this is called (or the first time after calling L), the actual C method will be invoked, and its return value cached in the L attribute. The value of that attribute will always be returned, so you can call C as many time as you need, and always receive the same instance. =head2 C Predicate for the L attribute. =head2 C Clearer for the L attribute. Clearing the attribute will cause the next call to C to instantiate a new object. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Traversable.pm0000644000175000017500000001222613505520232020445 0ustar yanickyanickpackage Bread::Board::Traversable; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: role for traversing a container service tree $Bread::Board::Traversable::VERSION = '0.37'; use Moose::Role; with 'MooseX::Clone' => { -version => 0.05 }; has 'parent' => ( is => 'rw', isa => 'Bread::Board::Traversable', weak_ref => 1, clearer => 'detach_from_parent', predicate => 'has_parent', ); sub get_root_container { my $c = shift; while ($c->has_parent) { $c = $c->parent; } return $c; } sub fetch { my ($self, $path) = @_; my $root; if ($path =~ /^\//) { $root = $self->get_root_container; } else { $root = $self; while (!$root->isa('Bread::Board::Container')) { $root = $root->parent; } } my @path = grep { $_ } split /\// => $path; if ($path[0] eq '..') { my $c = $root; do { shift @path; $c = $c->parent || confess "Expected parent for " . $c->name . " but found none"; } while (defined $path[0] && $path[0] eq '..' && $c->has_parent); $root = $c; } return $root unless @path; my $c = $root; while (my $h = shift @path) { $c = _get_container_or_service($c, $h); } if (!$self->isa('Bread::Board::Service::Alias')) { my %seen; while ($c->isa('Bread::Board::Service::Alias')) { $c = $c->aliased_from; confess "Cycle detected in aliases" if exists $seen{$c}; $seen{$c}++; } } return $c; } sub _get_container_or_service { my ($c, $name) = @_; (blessed $c) || confess "Expected object, got $c"; if ($c->isa('Bread::Board::Dependency')) { # make sure to evaluate this from the parent return _get_container_or_service($c->parent->parent, $name); } if ($c->does('Bread::Board::Service::WithDependencies')) { return $c->get_dependency($name) if $c->has_dependency($name); confess "Could not find dependency ($name) from service " . $c->name; } # name() is implemented in Service and Container # get_sub_container and get_service is implemented in Container # there must be a better way to do this if ($c->does('Bread::Board::Service')) { if ($c->name eq $name) { warn "Traversing into the current service ($name) is deprecated." . " You should remove the $name component from the path."; return $c; } } elsif ($c->isa('Bread::Board::Container')) { if ($c->name eq $name) { warn "Traversing into the current container ($name) is deprecated;" . " you should remove the $name component from the path"; return $c; } return $c->get_sub_container($name) if $c->has_sub_container($name); return $c->get_service($name) if $c->has_service($name); } confess "Could not find container or service for $name in " . $c->name; } no Moose::Role; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Traversable - role for traversing a container service tree =head1 VERSION version 0.37 =head1 SYNOPSIS my $service = $container->fetch('/some/service/path'); my $root = $service->get_root_container; =head1 DESCRIPTION This role provides the basic functionality to traverse a container / service tree. Instances of classes consuming this role will get a parent-child relationship between them. =head1 ATTRIBUTES =head2 C Weak ref to another L object, read/write accessor (although you should probably not change this value directly in normal code). =head1 METHODS =head2 C Predicate for the L attribute, true if a parent has been set. =head2 C Clearer for the L attribute, you should probably not call this method in normal code. =head2 C Returns the farthest ancestor of the invocant, i.e. the top-most container this object is a part of. =head2 C my $service = $this->fetch('/absolute/path'); my $service = $this->fetch('relative/path'); my $service = $this->fetch('../relative/path'); Given a (relative or absolute) path to a service or container, this method walks the tree and returns the L or L instance for that path. Dies if no object can be found for the given path. L are resolved in this call, by calling L<< C|Bread::Board::Service::Alias/aliased_from >> until we get an actual service. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Dependency.pm0000644000175000017500000000560613505520232020255 0ustar yanickyanickpackage Bread::Board::Dependency; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: dependency for a service $Bread::Board::Dependency::VERSION = '0.37'; use Moose; use Bread::Board::Service; with 'Bread::Board::Traversable'; has 'service_path' => ( is => 'ro', isa => 'Str', predicate => 'has_service_path' ); has 'service_name' => ( is => 'ro', isa => 'Str', lazy => 1, default => sub { my $self = shift; ($self->has_service_path) || confess "Could not determine service name without service path"; (split '/' => $self->service_path)[-1]; } ); has 'service_params' => ( is => 'ro', isa => 'HashRef', predicate => 'has_service_params' ); has 'service' => ( is => 'ro', does => 'Bread::Board::Service | Bread::Board::Dependency', lazy => 1, default => sub { my $self = shift; ($self->has_service_path) || confess "Could not fetch service without service path"; $self->fetch($self->service_path); }, handles => [ 'get', 'is_locked', 'lock', 'unlock' ] ); __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Dependency - dependency for a service =head1 VERSION version 0.37 =head1 DESCRIPTION This class holds the information for a dependency of a L. When L, instances of this class will be used to access the services that will provide the depended-on values. This class consumes the L role to retrieve services given their path. =head1 ATTRIBUTES =head2 C The path to use (possibly relative to the dependency itself) to access the L. =head2 C The service this dependency points at. Usually built lazily from the L, but could just be passed in to the constructor. =head2 C Name of the L, defaults to the last element of the L. =head1 METHODS =head2 C Predicate for the L attribute. =head2 C =head2 C =head2 C =head2 C These methods are delegated to the L. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/ConstructorInjection.pm0000644000175000017500000000451313505520232022363 0ustar yanickyanickpackage Bread::Board::ConstructorInjection; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: service instantiating objects via a constructor $Bread::Board::ConstructorInjection::VERSION = '0.37'; use Moose; use Try::Tiny; use Bread::Board::Types; with 'Bread::Board::Service::WithConstructor', 'Bread::Board::Service::WithParameters', 'Bread::Board::Service::WithDependencies'; has '+class' => (required => 1); sub get { my $self = shift; my $constructor = $self->constructor_name; $self->class->$constructor( %{ $self->params } ); } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::ConstructorInjection - service instantiating objects via a constructor =head1 VERSION version 0.37 =head1 DESCRIPTION This L class instantiates objects by calling the constructor on a class. This class consumes L, L, L. =head1 ATTRIBUTES =head2 C Attribute provided by L. This service makes it a required attribute: you can't call a constructor if you don't have a class. =head2 C Optional string, indicates the name of the class method to invoke to construct the object. If not provided, defaults to the constructor name obtained via L, or C if introspection does not work. =head1 METHODS =head2 C Calls the constructor (as indicated by L) on the L, passing all the L as a B. Returns whatever the constructor returned (hopefully a correctly-constructed object of the right class). =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Literal.pm0000644000175000017500000000533313505520232017570 0ustar yanickyanickpackage Bread::Board::Literal; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: service providing a literal value $Bread::Board::Literal::VERSION = '0.37'; use Moose; with 'Bread::Board::Service'; has 'value' => ( is => 'rw', required => 1, ); sub get { (shift)->value } sub clone_and_inherit_params { confess 'Trying to inherit from a literal service'; } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Literal - service providing a literal value =head1 VERSION version 0.37 =head1 SYNOPSIS my $c = container PrettyBoring => as { # These are Bread::Board::Literal services service connect_string => 'dbi:mysql:boring_db'; service service_url => 'http://api.example.com/v0/boring'; # And some other services depending on them... service dbconn => ( class => 'DBI', block => sub { my $s = shift; DBI->new($s->param('connect_string'); }, dependencies => wire_names(qw( connect_string )), ); service service_request => ( class => 'HTTP::Request', block => sub { my $s = shift; HTTP::Request->new(POST => $s->param('service_url')); }, dependencies => wire_names(qw( service_url )); }; }; # OR to use directly: my $literal = Bread::Board::Literal->new( name => 'the_answer_to_life_the_universe_and_everything', value => 42, ); $c->add_service($literal); =head1 DESCRIPTION A literal service is one that stores a literal scalar or reference for use in your Bread::Board. Beware of using references in your literals as they may cause your Bread::Board to leak memory. If this is a concern, you may want to weaken your references. See L. =head1 ATTRIBUTES =head2 C Required attribute with read/write accessor. This is the value that L will return. =head1 METHODS =head2 C Returns the L, unaltered. =head2 C Dies: a literal service is (essentially) a constant, it does not make sense to inherit from it. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/BlockInjection.pm0000644000175000017500000000441213505520232021066 0ustar yanickyanickpackage Bread::Board::BlockInjection; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: service instantiated via custom subroutine $Bread::Board::BlockInjection::VERSION = '0.37'; use Moose; with 'Bread::Board::Service::WithParameters', 'Bread::Board::Service::WithDependencies', 'Bread::Board::Service::WithClass'; has 'block' => ( is => 'rw', isa => 'CodeRef', required => 1, ); sub get { my $self = shift; $self->block->($self) } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::BlockInjection - service instantiated via custom subroutine =head1 VERSION version 0.37 =head1 DESCRIPTION This L class instantiates objects by calling a coderef supplied in the L attribute. This class consumes L, L, L. =head1 ATTRIBUTES =head2 C A coderef, required. Will be invoked as a method on the service object, so it can call L<<< C<< $_[0]->params >>|Bread::Board::Service/params >>> to access parameters and (resolved) dependencies. It should return an instance of L. =head2 C Attribute provided by L; if it is set, L should return an instance of this class (and the class will be already loaded, so there's no need to C it). =head1 METHODS =head2 C Predicate for L. If the service does not declare a class, the L can of course return whatever it wants. =head2 C Calls the L as a method on the service, and returns whatever that returned. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Dumper.pm0000644000175000017500000000601313505520232017424 0ustar yanickyanickpackage Bread::Board::Dumper; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: Pretty printer for visualizing the layout of your Bread::Board $Bread::Board::Dumper::VERSION = '0.37'; use Moose; sub dump { my ($self, $thing, $indent) = @_; $indent = defined $indent ? $indent . ' ' : ''; my $output = ''; if ($thing->isa('Bread::Board::Dependency')) { $output .= join('', $indent, "depends_on: ", $thing->service_path || $thing->service->name, "\n"); } elsif ($thing->does('Bread::Board::Service')) { $output .= join('', $indent, "service: ", $thing->name, "\n" ); if ($thing->does('Bread::Board::Service::WithDependencies')) { my $deps = $thing->dependencies; for my $key (sort keys %{$deps}) { $output .= $self->dump($deps->{$key}, $indent); } } } elsif ($thing->isa('Bread::Board::Container')) { $output = join('', $indent, "container: ", $thing->name, "\n" ); $output .= $self->_dump_container($thing, $indent); } elsif ($thing->isa('Bread::Board::Container::Parameterized')) { my $params = join ', ', @{ $thing->allowed_parameter_names }; $output = join('', $indent, "container: ", $thing->name, " [$params]\n" ); $output .= $self->_dump_container($thing, $indent); } return $output; } sub _dump_container { my ($self, $c, $indent) = @_; my $output = ''; my $subs = $c->sub_containers; for my $key (sort keys %{$subs}) { $output .= $self->dump($subs->{$key}, $indent); } my $services = $c->services; for my $key (sort keys %{$services}) { $output .= $self->dump($services->{$key}, $indent); } return $output; } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Dumper - Pretty printer for visualizing the layout of your Bread::Board =head1 VERSION version 0.37 =head1 SYNOPSIS use Bread::Board::Dumper; print Bread::Board::Dumper->new->dump($container); # container: Application # container: Controller # container: View # service: TT # depends_on: include_path # container: Model # service: dsn # service: schema # depends_on: pass # depends_on: ../dsn # depends_on: user =head1 DESCRIPTION This is a useful utility for dumping a clean view of a Bread::Board container. =head1 AUTHOR (actual) Daisuke Maki =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/Container.pm0000644000175000017500000002766413505520232020131 0ustar yanickyanickpackage Bread::Board::Container; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: A container for services and other containers $Bread::Board::Container::VERSION = '0.37'; use Moose; use Moose::Util::TypeConstraints 'find_type_constraint'; use MooseX::Params::Validate 0.14; use Bread::Board::Types; with 'Bread::Board::Traversable'; has 'name' => ( is => 'rw', isa => 'Str', required => 1 ); has 'services' => ( traits => [ 'Hash', 'Clone' ], is => 'rw', isa => 'Bread::Board::Container::ServiceList', coerce => 1, lazy => 1, default => sub{ +{} }, trigger => sub { my $self = shift; $_->parent($self) foreach values %{$self->services}; }, handles => { 'get_service' => 'get', 'has_service' => 'exists', 'get_service_list' => 'keys', 'has_services' => 'count', } ); has 'sub_containers' => ( traits => [ 'Hash', 'Clone' ], is => 'rw', isa => 'Bread::Board::Container::SubContainerList', coerce => 1, lazy => 1, default => sub{ +{} }, trigger => sub { my $self = shift; $_->parent($self) foreach values %{$self->sub_containers}; }, handles => { 'get_sub_container' => 'get', 'has_sub_container' => 'exists', 'get_sub_container_list' => 'keys', 'has_sub_containers' => 'count', } ); has 'type_mappings' => ( traits => [ 'Hash' ], is => 'rw', isa => 'Bread::Board::Container::ServiceList', lazy => 1, default => sub{ +{} }, handles => { '_get_type_mapping_for' => 'get', '_has_type_mapping_for' => 'exists', '_mapped_types' => 'keys', } ); sub get_type_mapping_for { my $self = shift; my ($type) = @_; return $self->_get_type_mapping_for($type) if $self->_has_type_mapping_for($type); for my $possible ($self->_mapped_types) { return $self->_get_type_mapping_for($possible) if $possible->isa($type); } return; } sub has_type_mapping_for { my $self = shift; my ($type) = @_; return 1 if $self->_has_type_mapping_for($type); for my $possible ($self->_mapped_types) { return 1 if $possible->isa($type); } return; } sub add_service { my ($self, $service) = @_; (blessed $service && $service->does('Bread::Board::Service')) || confess "You must pass in a Bread::Board::Service instance, not $service"; $service->parent($self); $self->services->{$service->name} = $service; } sub add_sub_container { my ($self, $container) = @_; ( blessed $container && ( $container->isa('Bread::Board::Container') || $container->isa('Bread::Board::Container::Parameterized') ) ) || confess "You must pass in a Bread::Board::Container instance, not $container"; $container->parent($self); $self->sub_containers->{$container->name} = $container; } sub add_type_mapping_for { my ($self, $type, $service) = @_; my $type_constraint = find_type_constraint( $type ); (defined $type_constraint) || confess "You must pass a valid Moose type, and it must exist already"; (blessed $service && $service->does('Bread::Board::Service')) || confess "You must pass in a Bread::Board::Service instance, not $service"; $self->type_mappings->{ $type_constraint->name } = $service; } sub resolve { my ($self, %params) = validated_hash(\@_, service => { isa => 'Str', optional => 1 }, type => { isa => 'Str', optional => 1 }, parameters => { isa => 'HashRef', optional => 1 }, ); my %parameters = exists $params{'parameters'} ? %{ $params{'parameters'} } : (); if (my $service_path = $params{'service'}) { my $service = $self->fetch( $service_path ); # NOTE: # we might want to allow Bread::Board::Service::Deferred::Thunk # objects as well, but I am not sure that is a valid use case # for this, so for now we just don't go there. # - SL (blessed $service && $service->does('Bread::Board::Service')) || confess "You can only resolve services, " . (defined $service ? $service : 'undef') . " is not a Bread::Board::Service"; return $service->get( %parameters ); } elsif (my $type = $params{'type'}) { ($self->has_type_mapping_for( $type )) || confess "Could not find a mapped service for type ($type)"; my $service = $self->get_type_mapping_for( $type ); my $result = $service->get( %parameters ); (find_type_constraint( $type )->check( $result )) || confess "The result of the service for type ($type) did not" . " pass the type constraint with $result"; return $result; } else { confess "Cannot call resolve without telling it what to resolve."; } } __PACKAGE__->meta->make_immutable; no Moose::Util::TypeConstraints; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::Container - A container for services and other containers =head1 VERSION version 0.37 =head1 SYNOPSIS use Bread::Board; my $c = container MCP => as { container Users => as { service flynn => ...; service bradley => ...; service dillinger => ...; }; container Programs => as { container Rebels => as { service tron => ...; service yori => ...; alias flynn => '/Users/flynn'; }; # nested container container Slaves => as { service sark => ...; service crom => ...; }; }; }; # OR directly... my $guardians => Bread::Board::Container->new( name => 'Guardians' ); $guardians->add_service( Bread::Board::ConstructorInjection->new( name => 'dumont', ..., ) ); $c->get_sub_container('Programs')->add_sub_container($guardians); =head1 DESCRIPTION This class implements the container for L: a container is a thing that contains services and other containers. Each container and service has a name, so you end up with a tree of named nodes, just like files and directories in a filesystem: each item can be referenced using a path (see L for the details). =head1 ATTRIBUTES =head2 C Read/write string, required. Every container needs a name, by which it can be referenced when L. =head2 C Hashref, constrained by L<< C|Bread::Board::Types/Bread::Board::Container::ServiceList >>, mapping names to services directly contained in this container. Every service added here will have its L<< C|Bread::Board::Traversable/parent >> set to this container. You can pass an arrayref of services instead of a hashref, the keys will be the names of the services. You should probably use L and L to manipulate this attribute, instead of modifying it directly. =head2 C Hashref, constrained by L<< C|Bread::Board::Types/Bread::Board::Container::SubContainerList >>, mapping names to containers directly contained in this container. Every container added here will have its L<< C|Bread::Board::Traversable/parent >> set to this container. You can pass an arrayref of containers instead of a hashref, the keys will be the names of the containers. You should probably use L and L to manipulate this attribute, instead of modifying it directly. Containers added here can either be normal L or L. =head1 METHODS =head2 C $container->add_service($service); Adds a service into the L map, using its name as the key. =head2 C my $service = $container->get_service($name); Returns a service by name, or C if there's no such service in the L map. =head2 C if ($container->has_service($name)) { ... } Returns true if a service with the given name name exists in the L map, false otherwise. =head2 C if ($container->has_services) { ... } Returns true if the L map contains any services, false if it's empty. =head2 C my @service_names = $container->get_service_list(); Returns the names off all services present in the L map. =head2 C $container->add_sub_container($container); Adds a container into the L map, using its name as the key. =head2 C my $container = $container->get_sub_container($name); Returns a container by name, or C if there's no such container in the L map. =head2 C if ($container->has_sub_container($name)) { ... } Returns true if a container with the given name name exists in the L map, false otherwise. =head2 C if ($container->has_sub_containers) { ... } Returns true if the L map contains any contains, false if it's empty. =head2 C my @container_names = $container->get_sub_container_list(); Returns the names off all containers present in the L map. =head2 C $containers->add_type_mapping_for( $type_name, $service ); Adds a mapping from a L to a service: whenever we try to L that type, we'll use that service to instantiate it. =head2 C my $service = $container->get_type_mapping_for( $type_name ); Returns the service to use to instantiate the given type name. Important: if a mapping for the exact type can't be found, but a mapping for a I of it can, you'll get the latter instead: package Superclass { use Moose }; package Subclass { use Moose; exends 'Superclass' }; $c->add_type_mapping_for( 'Subclass', Bread::Board::ConstructorInjection->new(name=>'sc',class=>'Subclass'), ); my $o = $c->get_type_mapping_for('Superclass')->get; C<$o> is an instance of C. If there are more than one sub-type mapped, you get a random one. This is probably a bad idea. =head2 C if ($container->has_type_mapping_for( $type_name )) { ... } Returns true if we have a service defined to instantiate the given type name, but see the note on L about subtype mapping. =head2 C my $object = $container->resolve(service=>$service_name); my $object = $container->resolve(service=>$service_name,parameters=>\%p); When given a service name, this method will L the service, then call L<< C|Bread::Board::Service/get >> on it, optionally passing the given parameters. my $object = $container->resolve(type=>$type); my $object = $container->resolve(type=>$type,parameters=>\%p); When given a type name, this method will use L to get the service, then call L<< C|Bread::Board::Service/get >> on it, optionally passing the given parameters. If the instance is not of the expected type, the method will die. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/lib/Bread/Board/SetterInjection.pm0000644000175000017500000000416113505520232021303 0ustar yanickyanickpackage Bread::Board::SetterInjection; our $AUTHORITY = 'cpan:STEVAN'; # ABSTRACT: service instantiating objects via setter functions $Bread::Board::SetterInjection::VERSION = '0.37'; use Moose; use Bread::Board::Types; with 'Bread::Board::Service::WithConstructor', 'Bread::Board::Service::WithParameters', 'Bread::Board::Service::WithDependencies'; has '+class' => (required => 1); sub get { my $self = shift; my $constructor = $self->constructor_name; my $o = $self->class->$constructor(); $o->$_($self->param($_)) foreach $self->param; return $o; } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Bread::Board::SetterInjection - service instantiating objects via setter functions =head1 VERSION version 0.37 =head1 DESCRIPTION This L class instantiates objects by calling C on a class, then calling setters on the returned object. This class consumes L, L, L. =head1 ATTRIBUTES =head2 C Attribute provided by L. This service makes it a required attribute: you can't call a constructor if you don't have a class. =head1 METHODS =head2 C Calls the C method on the L to get the object to return; then, for each of the L, calls a setter with the same name as the parameter, passing it the parameter's value. =head1 AUTHOR Stevan Little =head1 BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Bread-Board-0.37/Makefile.PL0000644000175000017500000000406513505520232014777 0ustar yanickyanick# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.012. use strict; use warnings; use 5.006; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "A solderless way to wire up your application components", "AUTHOR" => "Stevan Little ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Bread-Board", "LICENSE" => "perl", "MIN_PERL_VERSION" => "5.006", "NAME" => "Bread::Board", "PREREQ_PM" => { "Carp" => 0, "Module::Runtime" => 0, "Moose" => 0, "Moose::Exporter" => "2.1200", "Moose::Role" => 0, "Moose::Util" => 0, "Moose::Util::TypeConstraints" => 0, "MooseX::Clone" => "0.05", "MooseX::Params::Validate" => "0.14", "Scalar::Util" => 0, "Try::Tiny" => 0, "overload" => 0, "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "FindBin" => 0, "IO::Handle" => 0, "IPC::Open3" => 0, "Test::Fatal" => 0, "Test::Moose" => 0, "Test::More" => 0, "Test::Requires" => 0 }, "VERSION" => "0.37", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Carp" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "FindBin" => 0, "IO::Handle" => 0, "IPC::Open3" => 0, "Module::Runtime" => 0, "Moose" => 0, "Moose::Exporter" => "2.1200", "Moose::Role" => 0, "Moose::Util" => 0, "Moose::Util::TypeConstraints" => 0, "MooseX::Clone" => "0.05", "MooseX::Params::Validate" => "0.14", "Scalar::Util" => 0, "Test::Fatal" => 0, "Test::Moose" => 0, "Test::More" => 0, "Test::Requires" => 0, "Try::Tiny" => 0, "overload" => 0, "strict" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Bread-Board-0.37/MANIFEST0000644000175000017500000000621713505520232014157 0ustar yanickyanickCODE_OF_CONDUCT.md CONTRIBUTORS Changes INSTALL LICENSE MANIFEST META.json META.yml Makefile.PL README.mkdn SIGNATURE cpanfile doap.xml lib/Bread/Board.pm lib/Bread/Board/BlockInjection.pm lib/Bread/Board/ConstructorInjection.pm lib/Bread/Board/Container.pm lib/Bread/Board/Container/FromParameterized.pm lib/Bread/Board/Container/Parameterized.pm lib/Bread/Board/Dependency.pm lib/Bread/Board/Dumper.pm lib/Bread/Board/LifeCycle.pm lib/Bread/Board/LifeCycle/Singleton.pm lib/Bread/Board/LifeCycle/Singleton/WithParameters.pm lib/Bread/Board/Literal.pm lib/Bread/Board/Manual.pod lib/Bread/Board/Manual/Concepts.pod lib/Bread/Board/Manual/Concepts/Advanced.pod lib/Bread/Board/Manual/Concepts/Typemap.pod lib/Bread/Board/Manual/Example.pod lib/Bread/Board/Manual/Example/FormSensible.pod lib/Bread/Board/Manual/Example/LogDispatch.pod lib/Bread/Board/Service.pm lib/Bread/Board/Service/Alias.pm lib/Bread/Board/Service/Deferred.pm lib/Bread/Board/Service/Deferred/Thunk.pm lib/Bread/Board/Service/Inferred.pm lib/Bread/Board/Service/WithClass.pm lib/Bread/Board/Service/WithConstructor.pm lib/Bread/Board/Service/WithDependencies.pm lib/Bread/Board/Service/WithParameters.pm lib/Bread/Board/SetterInjection.pm lib/Bread/Board/Traversable.pm lib/Bread/Board/Types.pm t/00-compile.t t/00-report-prereqs.dd t/00-report-prereqs.t t/001_constructor_injection.t t/002_setter_injection.t t/003_block_injection.t t/004_block_injection_w_out_class.t t/005_alias.t t/010_container.t t/011_container_path.t t/012_container_with_shared_deps.t t/020_sugar.t t/021_sugar.t t/022_sugar.t t/023_sugar.t t/024_sugar.t t/025_sugar_w_absolute_path.t t/026_sugar_remove.t t/027_sugar_w_include.t t/028_sugar_w_recursive_inc.t t/029_sugar_auto_wire_names.t t/030_lifecycle_singleton.t t/031_custom_lifecycles.t t/032_singleton_cycle.t t/040_circular_dependencies.t t/041_parameter_cache_handling.t t/042_parameter_cache_with_singleton.t t/043_parameter_leaks.t t/044_deferred_parameters.t t/045_parameters_in_dependency.t t/046_custom_parameter_service.t t/047_dependencies_override_parameters.t t/048_array_deps.t t/050_parameterized_containers.t t/051_more_parameterized_containers.t t/052_parameterized_in_hierarchy.t t/053_parameterized_clone.t t/054_parameterized_backref.t t/060_extend_w_sugar.t t/061_extends_w_sugar_and_inheritance.t t/062_service_class_w_sugar.t t/070_with_basic_typemap.t t/071_typemap_with_basic_infer.t t/072_typemap_with_more_infer.t t/073_typemap_with_role_infer.t t/074_typemap_w_recursive_infer.t t/075_complex_typemap_example.t t/076_more_complex_typemap.t t/077_more_complex_typemap_w_roles.t t/078_complex_typemap_w_error.t t/079_depending_on_type.t t/080_infer_subclasses.t t/100_clone_w_constructor_injection.t t/101_clone_w_setter_injection.t t/102_clone_w_block_injection.t t/110_clone_w_singleton.t t/150_deferred_parameters_fail.t t/151_sugar_no_container.t t/152_sugar_service_inheritance.t t/153_sugar_container_inheritance.t t/200_example_code.t t/201_log_dispatch_example.t t/202_form_sensible_example.t t/300_no_new.t t/301_sugar.t t/302_path_traversal_deprecation.t t/310_literal_keyword.t t/500-gh61.t t/lib/bad.bb t/lib/false.bb t/lib/logger.bb t/lib/my_app.bb xt/release/unused-vars.t Bread-Board-0.37/Changes0000644000175000017500000003165313505520232014323 0ustar yanickyanickRevision history for Bread-Board 0.37 2019-06-28 [ DOCUMENTATION ] - Fixed pod error as reported by CPANTS. (GH#67, Mohammad S Anwar) - remove extraneous character from synopsis - Remove extraneous character from synopsis (GH#66, Graham Knop) [ MISC ] - Fix some broken formatting in 'Changes'. (GH#65, Dave Rolsky) [ STATISTICS ] - code churn: 10 files changed, 137 insertions(+), 70 deletions(-) 0.36 2017-10-15 [ BUG FIXES ] - fix pod error. (Mohammad S Anwar, GH#63) [ MISC ] - Reverting the changes of #53, as it breaks a few packages that are using `also => 'Bread::Board'` in their import, and the benefit we get from freeing ourselves from Moose::Exporter isn't that huge. [ STATISTICS ] - code churn: 4 files changed, 40 insertions(+), 44 deletions(-) 0.35 2017-07-31 [ BUG FIXES ] - fixed Bread::Board::Dumper to dump parameterized containers - previously it would just ignore them entirely (#56, Dave Rolsky) - Moo classes were losing their method modifiers because of bad interaction with Class::MOP::class_of(). (GH#61) [ ENHANCEMENTS ] - Using Bread::Board no longer enables strict and warnings in the calling package. (#53, Dave Rolsky) - Bread::Board::Dumper now sorts services and sub containers - previously these came out in a random order each time (#56, Dave Rolsky) - allow a service or literal to be undefined. (#54, Dave Rolsky) [ STATISTICS ] - code churn: 13 files changed, 425 insertions(+), 237 deletions(-) 0.34 2016-03-28 - Add test messages to tests which did not have them, (#50, Alex Balhatchet) [ DOCUMENTATION ] - add missing module abstracts. (GH#49, Alex Balhatchet, GH#16, Sterling Hanenkamp) [ ENHANCEMENTS ] - SetterInjection can now accept any constructor. (#43, dakkar) [ STATISTICS ] - code churn: 37 files changed, 462 insertions(+), 395 deletions(-) 0.33 2015-04-26 - add 'literal' keyword (Yanick Champoux, #31) - added documentation for most of the classes (dakkar, sponsored by Net-a-Porter.com) [ STATISTICS ] - code churn: 35 files changed, 1016 insertions(+), 375 deletions(-) 0.32 2014-06-03 - fixes to arrayref dependencies (dakkar, #35) 0.31 2014-05-08 - allow specifying dependency values as arrayrefs, which will resolve to an arrayref containing the resolved service values (dakkar, #34) 0.30 2014-02-02 - fix deprecated use of Class::MOP::load_class (Caleb Cushing, #33) 0.29 2013-11-21 - Fix deprecated enum syntax 0.28 2013-08-30 - Allow overriding of services and containers. This is useful when using the common pattern of "sub BUILD { my $self = shift; container $self => as { ... } }" since the BUILD method in a subclass can modify existing services and containers that were defined in a superclass, rather than only being able to replace them (Florian Ragwitz, #26, #27). 0.27 2013-08-06 - allow the 'container $obj' sugar for parameterized containers (Florian Ragwitz, #25) 0.26 2013-08-01 - allow cloning containers with parameterized subcontainers (Florian Ragwitz, #22) - allow referencing parent services from parameterized subcontainers (Florian Ragwitz, #23) - deprecate special case in path traversal where a path component with the name of the current container was ignored (doy, #20) - fix setting the current container multiple times from outside of a container (doy) - make some error messages more helpful (Jason Galea, #14) - doc improvements (Sterling Hanenkamp, zdk, Philippe Bruhat, and Gabor Szabo; #15, #17, #19, #21) - remove the (unmaintained) Bread::Board::GraphViz 0.25 2011-10-20 - Bread::Board - the container sugar was misbehaving (since April 2010 apparently), so we have fixed it and clarified it 0.24 2011-10-15 - Bread::Board::Container - make sure to clone subcontainers, this is necessary for parameterized containers to retain their connections - Bread::Board::Traversable - add some errors, be a little more defensive 0.23 2011-10-14 - Bread::Board::Container::Parameterized - retain control of names of the generated containers, so if your the container being generated has a name, it keeps it. - this makes these containers more addressable when composed into higher level containers - this helps when subclassing parameterized containers - switch the generated containers to be created with builder instead of default - also helps when subclassing parameterized containers as well - t/046_custom_parameter_service.t - fixing the error about Moose deprecations 0.22 2011-10-03 - When inferring a service for a type, allow subclasses to fulfill discovered types. - Converted to Dist::Zilla 0.21 2011-09-06 - Bread::Board - Allow service() and alias() sugar functions to return the newly-created objects if the context container is not defined. (thanks to kip hampton) - added tests for this - this module is just an exporter, so it does not need to 'use Moose' (thanks to Tomas Doran) - fixed some spelling and grammar errors (thanks to ben hengst and Brad Bowman) - Bread::Board::Service::WithParameters - parameters attribute now has a builder instead of a default, so as to allow better tweaking in subclasses (thanks to Andre Walker) - added tests for this 0.20 2011-06-13 - Bread::Board::Lifecycle::Singleton - fix bug in singletons with circular refs (thanks to doy) - added tests for this (thanks to perigrin) 0.19 2011-06-01 - Bread::Board::GraphViz - added by jrockway, this allows you to visualise a Bread::Board system using GraphViz - note that this is optional and requires you to install optional dependencies - Bread::Board::Service::Alias - add the ability to alias services under another name (thanks to doy) - added tests for this - Bread::Board::Service::Inferred - improving edge cases (thanks to doy) - Bread::Board::Service::* - several code improvements (thanks to doy) - Bread::Board::Service - allow for custom Lifecycles by using the "+" prefix (thanks to jasonmay) - added tests for this 0.18 2011-04-13 - Bread::Board::Service::WithParameters - added has_parameter_defaults method to check if a parameter has default values - Bread::Board::Service::WithDependencies - added a check for has_parameter_defaults before we make a Thunk - added test for this (thanks to rafl) 0.17 2011-02-22 - Bread::Board::Service::Inferred - make recrusive inferrence work - add tests for this - Bread::Board::Manual::Concepts::Advanced - small doc update about subclassing and the name parameter, resolving RT#63124 (thanks to Evan Haas) - POSSIBLE BACK COMPAT BREAKAGE !! - Bread::Board::Traversal - make relative parent path traversal more sane, there should be no more need for excessive ../../ stuff in dependency service paths (thanks doy) - adjust tests accordingly - this should fix RT#64478 as well 0.16 2011-01-10 - Bread::Board::service sugar - adding the 'service_class' param for the service sugar function which allows you to pass in a custom service subclass - added tests for this (062_service_class_w_sugar.t) - Bread::Board::Dependency - added the service_params attribute here so that it is possible to pass in parameters when you depend on a service which requires them - added tests for this (045_parameters_in_dependency.t) - Bread::Board::Service::Inferred - when a typemapped service is created it is now named with the special 'type:' prefix. This allows you to depend on a typemapped service in a non-typemapped service - added tests for this (079_depending_on_type.t) - Bread::Board::Traversable - improving the error messages when a container/service is not found 0.15 2010-09-30 - Bread::Board::Service - removed the MooseX::Param dependency and implemented it internally so that we have more control - Bread::Board::Types - the Bread::Board::Service::Dependencies type now can also coerce ArrayRef[Str] and HashRef[Str] types correctly (doy) - Bread::Board::Service::WithDependencies - we now only create a ::Deferred::Thunk object if we have non-optional params - Bread::Board::Service::WithParameters - added the has_required_parameters method, to see if there are any non-optional parameters - added tests for both the above - NEW EXPERIMENTAL FEATURE !! - Bread::Board - added the typemap and infer keyword to help in the mapping of types and construction of inferred services - added tests for this - Bread::Board::Container - added the typemap feature and added the ->resolve( type => $type ) call - added tests for this - Bread::Board::Service::Inferred - added this and tests for it - Bread::Board::Manual::Concepts::Typemap - added this to help explain the typemap feature 0.14 2010-08-24 - Bread::Board::Container - added the ->resolve method to replace the ->fetch( $service )->get pattern that annoys mst so much. - adjusted all the tests to account for this change. - adjusted all the docs to now use this approach instead - now using Try::Tiny for all exception handling (except the Deferred service) - Bread::Board::Service::WithDependencies - if you want to depend on a parameterized service, now you can and it will return a Bread::Board::Service::Deferred::Thunk that you can call ->inflate on and pass in the parameters for it. - added tests for this - Bread::Board::Service::Deferred::Thunk - added this + tests for it 0.13 2010-04-23 - Bread::Board - making the include keyword handle compilation errors better (doy) - added test for this - Bread::Board::Container Bread::Board::Container::Parameterized - it is now possible to store parameterized containers within regular containers and have them behave properly - added tests for this - Bread::Board::Manual::Example::* - adding some examples of ways to use Bread::Board to the manual - added tests to confirm they work 0.12 2010-04-18 - Bread::Board - added the `include` keyword which will evaluate an external file within your Bread::Board configuration - added tests for this - added support for parameterized containers - added tests for this - the 'container' keyword will now accept an instance of Bread::Board::Container instead of the name, this makes subclassing easier - added tests for this - Bread::Board::Container::Parameterized - added this module and tests - Bread::Baord::Manual - moved, re-organizad and added too the docs that were previously in Bread::Board.pm 0.11 2010-03-25 - Much improved documentation. - Fixed inc/ to include all used Module-Install extensions. 0.10 2010-02-22 - Bread::Board - import strict and warnings into the caller upon import (Florian Ragwitz) - fixing the SYNOPSIS so that it will actually run (thanks to zby for spotting this) - Bread::Board::ConstructorInjection - Add a constructor_name parameter for classes using MooseX::Traits or other things which need an alternately named constructor. (Tomas Doran) 0.09 2009-07-29 - Add cloning support for containers and services - (thanks to jrockway for this) - adding tests for this - Bread::Board::ConstructorInjection - use meta->constructor_name instead of "new" if possible (jrockway) - Bread::Board::Service::WithParameters - fixing a leak where we would hold onto parameters that were passed into get() 0.08 2009-07-18 - updating dates on all files - Bread::Board::LifeCycle::Singleton::WithParameters - new module added to support the idea of a singleton lifecycle keyed on the parameters rather then just a per-instance item. - Bread::Board::Traversable - fixed the is_weak_ref mis-spelling 0.07 2009-02-18 - Work with new MooseX::Params::Validate - Specify MX::P::Validate version number in Makefile.PL 0.06 2008-11-03 - Forgot to update MANIFEST before uploading to CPAN. 0.05 2008-11-03 - Applied immutablity to classes where applicable, and vigorously unimport Moose keywords when they are no longer needed. This results in x 2 performance as far as defining a Bread::Board model (Daisuke Maki). - Bread::Board - Implemented unimport(), thus allowing you to remove keywords exported by Bread::Board (Daisuke Maki). - Bread::Board::Traversable - Unrolled recursive calls to loops, and removed Sub::Current dependency (Daisuke Maki) 0.04 2008-10-31 - Bread::Board Bread::Board::Traversable - fix root path handling (thanks to Daisuke Maki) - added tests for this - Bread::Board::Dumper - Simple utility for dumping containers (thanks to Daisuke Maki) - t/ - fixing the plans so that new versions of Test::More stop complaining 0.03 2008-01-08 - Bread::Board::Service::WithParameters - fixed the parameter validation to use a custom cache key, this is so that it plays nicely with the new MooseX::Params::Validate - added tests for this 0.02 2008-01-08 - forgot a dependency, whoops. 0.01 2008-01-07 - Out with the old (IOC) and in with the new (Bread::Board) Bread-Board-0.37/CODE_OF_CONDUCT.md0000644000175000017500000000624013505520232015621 0ustar yanickyanick# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at stevan@iinteractive.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org Bread-Board-0.37/LICENSE0000644000175000017500000004410313505520232014027 0ustar yanickyanickThis software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Bread-Board-0.37/README.mkdn0000644000175000017500000003653013505520232014637 0ustar yanickyanick# NAME Bread::Board - A solderless way to wire up your application components # VERSION version 0.37 # SYNOPSIS ```perl use Bread::Board; my $c = container 'MyApp' => as { service 'log_file_name' => "logfile.log"; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => [ 'log_file_name' ], ); container 'Database' => as { service 'dsn' => "dbi:SQLite:dbname=my-app.db"; service 'username' => "user234"; service 'password' => "****"; service 'dbh' => ( block => sub { my $s = shift; require DBI; DBI->connect( $s->param('dsn'), $s->param('username'), $s->param('password'), ) || die "Could not connect"; }, dependencies => [ 'dsn', 'username', 'password' ] ); }; service 'application' => ( class => 'MyApplication', dependencies => { logger => 'logger', dbh => 'Database/dbh', } ); }; no Bread::Board; # removes keywords # get an instance of MyApplication # from the container my $app = $c->resolve( service => 'application' ); # now user your MyApplication # as you normally would ... $app->run; ``` # DESCRIPTION Bread::Board is an inversion of control framework with a focus on dependency injection and lifecycle management. It's goal is to help you write more decoupled objects and components by removing the need for you to manually wire those objects/components together. Want to know more? See the [Bread::Board::Manual](https://metacpan.org/pod/Bread::Board::Manual). ``` +-----------------------------------------+ | A B C D E F G H I J | |-----------------------------------------| | o o | 1 o-o-o-o-o v o-o-o-o-o 1 | o o | | o o | 2 o-o-o-o-o o-o-o-o-o 2 | o o | | o o | 3 o-o-o-o-o o-o-o-o-o 3 | o o | | o o | 4 o-o-o-o-o o-o-o-o-o 4 | o o | | o o | 5 o-o-o-o-o o-o-o-o-o 5 | o o | | | 6 o-o-o-o-o o-o-o-o-o 6 | | | o o | 7 o-o-o-o-o o-o-o-o-o 7 | o o | | o o | 8 o-o-o-o-o o-o-o-o-o 8 | o o | | o o | 9 o-o-o-o-o o-o-o-o-o 9 | o o | | o o | 10 o-o-o-o-o o-o-o-o-o 10 | o o | | o o | 11 o-o-o-o-o o-o-o-o-o 11 | o o | | | 12 o-o-o-o-o o-o-o-o-o 12 | | | o o | 13 o-o-o-o-o o-o-o-o-o 13 | o o | | o o | 14 o-o-o-o-o o-o-o-o-o 14 | o o | | o o | 15 o-o-o-o-o o-o-o-o-o 15 | o o | | o o | 16 o-o-o-o-o o-o-o-o-o 16 | o o | | o o | 17 o-o-o-o-o o-o-o-o-o 17 | o o | | | 18 o-o-o-o-o o-o-o-o-o 18 | | | o o | 19 o-o-o-o-o o-o-o-o-o 19 | o o | | o o | 20 o-o-o-o-o o-o-o-o-o 20 | o o | | o o | 21 o-o-o-o-o o-o-o-o-o 21 | o o | | o o | 22 o-o-o-o-o o-o-o-o-o 22 | o o | | o o | 22 o-o-o-o-o o-o-o-o-o 22 | o o | | | 23 o-o-o-o-o o-o-o-o-o 23 | | | o o | 24 o-o-o-o-o o-o-o-o-o 24 | o o | | o o | 25 o-o-o-o-o o-o-o-o-o 25 | o o | | o o | 26 o-o-o-o-o o-o-o-o-o 26 | o o | | o o | 27 o-o-o-o-o o-o-o-o-o 27 | o o | | o o | 28 o-o-o-o-o ^ o-o-o-o-o 28 | o o | +-----------------------------------------+ ``` Loading this package will automatically load the rest of the packages needed by your Bread::Board configuration. # EXPORTED FUNCTIONS The functions of this package provide syntactic sugar to help you build your Bread::Board configuration. You can build such a configuration by constructing the objects manually instead, but your code may be more difficult to understand. ## `container` ### simple case ``` container $name, \&body; ``` This function constructs and returns an instance of [Bread::Board::Container](https://metacpan.org/pod/Bread::Board::Container). The (optional) `&body` block may be used to add services or sub-containers within the newly constructed container. Usually, the block is not passed directly, but passed using the `as` function. For example, ```perl container 'MyWebApp' => as { service my_dispatcher => ( class => 'MyWebApp::Dispatcher', ); }; ``` If `$name` starts with `'+'`, and the container is being declared inside another container, then this declaration will instead extend an existing container with the name `$name` (without the `'+'`). ### from an instance ``` container $container_instance, \&body ``` In many cases, subclassing [Bread::Board::Container](https://metacpan.org/pod/Bread::Board::Container) is the easiest route to getting access to this framework. You can do this and still get all the benefits of the syntactic sugar for configuring that class by passing an instance of your container subclass to `container`. You could, for example, configure your container inside the `BUILD` method of your class: ```perl package MyWebApp; use Moose; extends 'Bread::Board::Container'; sub BUILD { my $self = shift; container $self => as { service dbh => ( ... ); }; } ``` ### with parameters ``` container $name, \@parameters, \&body ``` A third way of using the `container` function is to build a parameterized container. These are useful as a way of providing a placeholder for parts of the configuration that may be provided later. You may not use an instance object in place of the `$name` in this case. For more detail on how you might use parameterized containers, see ["Parameterized Containers" in Bread::Board::Manual::Concepts::Advanced](https://metacpan.org/pod/Bread::Board::Manual::Concepts::Advanced#Parameterized-Containers). ## `as` ``` as { some_code() }; ``` This is just a replacement for the `sub` keyword that is easier to read when defining containers. ## `service` ``` service $name, $literal; service $name, %service_description; ``` Within the `as` blocks for your containers, you may construct services using the `service` function. This can construct several different kinds of services based upon how it is called. ### literal services To build a literal service (a [Bread::Board::Literal](https://metacpan.org/pod/Bread::Board::Literal) object), just specify a scalar value or reference you want to use as the literal value: ```perl # In case you need to adjust the gravitational constant of the Universe service gravitational_constant => 6.673E-11; ``` ### using injections To build a service using one of the injection services, just fill in all the details required to use that sort of injection: ```perl service search_service => ( class => 'MyApp::Search', block => sub { my $s = shift; MyApp::Search->new($s->param('url'), $s->param('type')); }, dependencies => { url => 'search_url', }, parameters => { type => { isa => 'Str', default => 'text' }, }, ); ``` The type of injection performed depends on the parameters used. You may use the `service_class` parameter to pick a specific injector class. For instance, this is useful if you need to use [Bread::Board::SetterInjection](https://metacpan.org/pod/Bread::Board::SetterInjection) or have defined a custom injection service. If you specify a `block`, block injection will be performed using [Bread::Board::BlockInjection](https://metacpan.org/pod/Bread::Board::BlockInjection). If neither of these is present, constructor injection will be used with [Bread::Board::ConstructorInjection](https://metacpan.org/pod/Bread::Board::ConstructorInjection) (and you must provide the `class` option). ### service dependencies The `dependencies` parameter takes a hashref of dependency names mapped to [Bread::Board::Dependency](https://metacpan.org/pod/Bread::Board::Dependency) objects, but there are several coercions and sugar functions available to make specifying dependencies as easy as possible. The simplest case is when the names of the services you're depending on are the same as the names that the service you're defining will be accessing them with. In this case, you can just specify an arrayref of service names: ```perl service foo => ( dependencies => [ 'bar', 'baz' ], # ... ); ``` If you need to use a different name, you can specify the dependencies as a hashref instead: ```perl service foo => ( dependencies => { dbh => 'foo_dbh', }, # ... ); ``` You can also specify parameters when depending on a parameterized service: ```perl service foo => ( dependencies => [ { bar => { bar_param => 1 } }, 'baz', ], # ... ); ``` Finally, services themselves can also be specified as dependencies, in which case they will just be resolved directly: ```perl service foo => ( dependencies => { dsn => Bread::Board::Literal->new( name => 'dsn', value => 'dbi:mysql:mydb', ), }, # ... ); ``` As a special case, an arrayref of dependencies will be interpreted as a service which returns an arrayref containing the resolved values of those dependencies: ```perl service foo => ( dependencies => { # items will resolve to [ $bar_service->get, $baz_service->get ] items => [ 'bar', Bread::Board::Literal->new(name => 'baz', value => 'BAZ'), ], }, # ... ); ``` ### inheriting and extending services If the `$name` starts with a `'+'`, the service definition will instead extend an existing service with the given `$name` (without the `'+'`). This works similarly to the `has '+foo'` syntax in Moose. It is most useful when defining a container class where the container is built up in `BUILD` methods, as each class in the inheritance hierarchy can modify services defined in superclasses. The `dependencies` and `parameters` options will be merged with the existing values, rather than overridden. Note that literal services can't be extended, because there's nothing to extend. You can still override them entirely by declaring the service name without a leading `'+'`. ## `literal` ``` literal($value); ``` Creates an anonymous [Bread::Board::Literal](https://metacpan.org/pod/Bread::Board::Literal) object with the given value. ```perl service 'dbh' => ( block => sub { my $s = shift; require DBI; DBI->connect( $s->param('dsn'), $s->param('username'), $s->param('password'), ) || die "Could not connect"; }, dependencies => { dsn => literal 'dbi:SQLite:somedb', username => literal 'foo', password => literal 'password', }, ); ``` ## `depends_on` ``` depends_on($service_path); ``` The `depends_on` function creates a [Bread::Board::Dependency](https://metacpan.org/pod/Bread::Board::Dependency) object for the named `$service_path` and returns it. ## `wire_names` ``` wire_names(@service_names); ``` This function is just a shortcut for passing a hash reference of dependencies into the service. It is not typically needed, since Bread::Board can usually understand what you mean - these declarations are all equivalent: ```perl service foo => ( class => 'Pity::TheFoo', dependencies => { foo => depends_on('foo'), bar => depends_on('bar'), baz => depends_on('baz'), }, ); service foo => ( class => 'Pity::TheFoo', dependencies => wire_names(qw( foo bar baz )), ); service foo => ( class => 'Pity::TheFoo', dependencies => { foo => 'foo', bar => 'bar', baz => 'baz', }, ); service foo => ( class => 'Pity::TheFoo', dependencies => [ qw(foo bar baz ) ], ); ``` ## `typemap` ``` typemap $type, $service; typemap $type, $service_path; ``` This creates a type mapping for the named type. Typically, it is paired with the `infer` call like so: ```perl typemap 'MyApp::Model::UserAccount' => infer; ``` For more details on what type mapping is and how it works, see [Bread::Board::Manual::Concepts::Typemap](https://metacpan.org/pod/Bread::Board::Manual::Concepts::Typemap). ## `infer` ``` infer; infer(%hints); ``` This is used with `typemap` to help create the typemap inference. It can be used with no arguments to do everything automatically. However, in some cases, you may want to pass a service instance as the argument or a hash of service arguments to change how the type map works. For example, if your type needs to be constructed using a setter injection, you can use an inference similar to this: ```perl typemap 'MyApp::Model::UserPassword' => infer( service_class => 'Bread::Board::SetterInjection', ); ``` For more details on what type mapping is and how it works, see [Bread::Board::Manual::Concepts::Typemap](https://metacpan.org/pod/Bread::Board::Manual::Concepts::Typemap). ## `include` ``` include $file; ``` This is a shortcut for loading a Bread::Board configuration from another file. ``` include "filename.pl"; ``` The above is pretty much identical to running: ``` do "filename.pl"; ``` However, you might find it more readable to use `include`. ## `alias` ``` alias $service_name, $service_path, %service_description; ``` This helper allows for the creation of [service aliases](https://metacpan.org/pod/Bread::Board::Service::Alias), which allows you to define a service in one place and then reuse that service with a different name somewhere else. This is sort of like a symbolic link for services. Aliases will be [resolved recursively](https://metacpan.org/pod/Bread::Board::Traversable#fetch), so an alias can alias an alias. For example, ```perl service file_logger => ( class => 'MyApp::Logger::File', ); alias my_logger => 'file_logger'; ``` # OTHER FUNCTIONS These are not exported, but might be helpful to you. ## `set_root_container` ``` set_root_container $container; ``` You may use this to set a top-level root container for all container definitions. For example, ```perl my $app = container MyApp => as { ... }; Bread::Board::set_root_container($app); my $config = container Config => as { ... }; ``` Here the `$config` container would be created as a sub-container of `$app`. # ACKNOWLEDGEMENTS Thanks to Daisuke Maki for his contributions and for really pushing the development of this module along. Chuck "sprongie" Adams, for testing/using early (pre-release) versions of this module, and some good suggestions for naming it. Matt "mst" Trout, for finally coming up with the best name for this module. Gianni "dakkar" Ceccarelli for writing lots of documentation, and Net-a-Porter.com for paying his salary while he was doing it. # ARTICLES [Bread::Board is the right tool for this job](http://domm.plix.at/perl/2013_04_bread_board_is_the_right_rool_for_this_job.html) Thomas Klausner showing a use-case for Bread::Board. # SEE ALSO - [Bread::Board::Declare](https://metacpan.org/pod/Bread::Board::Declare) This provides more powerful syntax for writing Bread::Board container classes. - [IOC](https://metacpan.org/pod/IOC) Bread::Board is basically my re-write of IOC. - [http://en.wikipedia.org/wiki/Breadboard](http://en.wikipedia.org/wiki/Breadboard) # AUTHOR Stevan Little # BUGS Please report any bugs or feature requests on the bugtracker website https://github.com/stevan/BreadBoard/issues When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. # COPYRIGHT AND LICENSE This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2011, 2009 by Infinity Interactive. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Bread-Board-0.37/SIGNATURE0000644000175000017500000002202613505520232014306 0ustar yanickyanickThis file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.79. To verify the content in this distribution, first make sure you have Module::Signature installed, then type: % cpansign -v It will check each file's integrity, as well as the signature's validity. If "==> Signature verified OK! <==" is not displayed, the distribution may already have been compromised, and you should not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 SHA1 7882559bbba283ac45290013abccc5c496e39e60 CODE_OF_CONDUCT.md SHA1 99687ee97adb7366b090052a9ad0ec2f53546f6b CONTRIBUTORS SHA1 ed276811111f0a6ec170857d511ea7fb2ff6ffe2 Changes SHA1 126b3991e8ebf99ecd0bd3934ee1a7765611ed6d INSTALL SHA1 cceccfa33c74ada8305d1c8d5384a4bc4d4ee483 LICENSE SHA1 17d3099d2bd43cba9143195b8da689c46073c125 MANIFEST SHA1 2e3e81c3faf2ba3066b58b1233092e6baaf0197a META.json SHA1 5085e94699661b72d9dd1bc6d510c2629804b91c META.yml SHA1 5453be8c45db127bdc43709105ff68379b9eb3d5 Makefile.PL SHA1 1fcf7f9a0ad31db6d5e069d065fd9e6263c308bf README.mkdn SHA1 cb0124cd9c5e96cfc6427e232abc73bc9d7515de cpanfile SHA1 ccc3175c2c919f000c6ab4c174a2a623014b76a2 doap.xml SHA1 d7e5a2edec446269d8e09c575c149ce8c9fa6c24 lib/Bread/Board.pm SHA1 8d6b6ee8000a1707bd9f430cd8f6f93a891185fa lib/Bread/Board/BlockInjection.pm SHA1 d2d1ed462a601a892f143d44f172646f0000e805 lib/Bread/Board/ConstructorInjection.pm SHA1 2be2832213c4956106bfbc599a52350ddde18a33 lib/Bread/Board/Container.pm SHA1 8dc8ee7ccb3c250cd6fbde35fa6720a620659f56 lib/Bread/Board/Container/FromParameterized.pm SHA1 c2fc38529343707355e641b60aca094dd2544bfc lib/Bread/Board/Container/Parameterized.pm SHA1 50e433b00b8e2bca002ad1e16216cb90d9317939 lib/Bread/Board/Dependency.pm SHA1 429d1412e7ae713a841d0e3248b283d195919b7c lib/Bread/Board/Dumper.pm SHA1 5b8c410134cf72705c90787dba8cd3bfee9ac36a lib/Bread/Board/LifeCycle.pm SHA1 eed1ebca34995c75223f71d89ace5ee37dec50f0 lib/Bread/Board/LifeCycle/Singleton.pm SHA1 6b0a4f0fd9ca41bc9ed7d6053bc1c94b3256cf3d lib/Bread/Board/LifeCycle/Singleton/WithParameters.pm SHA1 00fa93c74ffb4e0b09bce5d4cc196d37dba1108a lib/Bread/Board/Literal.pm SHA1 7561bff4d35f0c3a36f8cdfe20dbe1a1429cc8b3 lib/Bread/Board/Manual.pod SHA1 71e1a068cd006a707f7f8bd3123ad14ff8b75fc3 lib/Bread/Board/Manual/Concepts.pod SHA1 e1a7961a5a1860804277e93e64743bd83c8c2750 lib/Bread/Board/Manual/Concepts/Advanced.pod SHA1 6114183100e93f08620919c55ab2d5dadb894fc2 lib/Bread/Board/Manual/Concepts/Typemap.pod SHA1 f79f6970f559f6741cf303ada482d4ceed65bf09 lib/Bread/Board/Manual/Example.pod SHA1 3f2bce510493eed686a98b928e757cada8f77720 lib/Bread/Board/Manual/Example/FormSensible.pod SHA1 2986b9851dd4fabe055081edb29a2e347349f158 lib/Bread/Board/Manual/Example/LogDispatch.pod SHA1 bdb4578d5b0b4a486e4d4ebbac927163abd31cff lib/Bread/Board/Service.pm SHA1 05f69a659e2cb819221552cde6549be8965056db lib/Bread/Board/Service/Alias.pm SHA1 dc911ccd9275f58b47fee91aea96061364a8f0db lib/Bread/Board/Service/Deferred.pm SHA1 214743784a92cb588e6c408f4f6b5921e8144c93 lib/Bread/Board/Service/Deferred/Thunk.pm SHA1 43db87459d883b692cf901353c8b86c1d7d724da lib/Bread/Board/Service/Inferred.pm SHA1 508ab0189038a423b8fa580ed1210e30b59c5c24 lib/Bread/Board/Service/WithClass.pm SHA1 aefde131dfb098c565b777ce8d6236c00a3026db lib/Bread/Board/Service/WithConstructor.pm SHA1 edd7fb410d7db7953d5e34f39b25614884756227 lib/Bread/Board/Service/WithDependencies.pm SHA1 0496d1c01f25ba7460b4d1006fbc792783f9f4be lib/Bread/Board/Service/WithParameters.pm SHA1 3af51c47a03553526a051ae968865cb0d97dc2ea lib/Bread/Board/SetterInjection.pm SHA1 30cd7701e98fae1418bd78cb9adc6ed18f6e0370 lib/Bread/Board/Traversable.pm SHA1 89bd20d909643af48d4716a66e7f4dde8a32d024 lib/Bread/Board/Types.pm SHA1 750ff38befd1bf8d67708c96c68e76555a56aeaa t/00-compile.t SHA1 b827b18eeb9c867b27ec709379b2f4e6a72d8fd2 t/00-report-prereqs.dd SHA1 6348689eee69681210251892bd47cbfbb7507d7d t/00-report-prereqs.t SHA1 751103232b9b8d9eb5918981341b70790fa729bb t/001_constructor_injection.t SHA1 1e980d97aa4bdba035ab3c83fa9b7feeae51b0b5 t/002_setter_injection.t SHA1 7868c26adf3b85f959a8104f774793e4fb351a36 t/003_block_injection.t SHA1 b4265c471a295b29f2fc2195535a74b87ecf0551 t/004_block_injection_w_out_class.t SHA1 6ac17c39a66553d5afe7c53ea9ae9b8c08dc1012 t/005_alias.t SHA1 3f1171fe7ee0e8d6cd4f651e6637c7a8ee210c10 t/010_container.t SHA1 37695ff39a7466d273c6c95480578a23d6c5035b t/011_container_path.t SHA1 7ac20cccf542a528270a511e61595e6530a4dc67 t/012_container_with_shared_deps.t SHA1 bbdf1958d2c8fcc1608129695fb735b41199901d t/020_sugar.t SHA1 a770d2f329c496f727e727a789955ba2e7b4066c t/021_sugar.t SHA1 0115e49fd48956e1923f36d598a597f00f98b480 t/022_sugar.t SHA1 7383ec6072a7746b1980c2582d9bea50b9d18573 t/023_sugar.t SHA1 52f923f33dcaae6bac8a36857651f1f4a5632a91 t/024_sugar.t SHA1 55c0708edc83699089a91e3d2dd521385d19b2a6 t/025_sugar_w_absolute_path.t SHA1 2a540a326de81835d0c6dd9334c71c811d4d5d2e t/026_sugar_remove.t SHA1 64188a7ccbdf163cfcaf58a6b3eba8c62571f58d t/027_sugar_w_include.t SHA1 3b7004f0767d87977b8073df829e01e2138b660e t/028_sugar_w_recursive_inc.t SHA1 4293e122dbfa0dc9f0f77d9136923d6f2e9e7aaf t/029_sugar_auto_wire_names.t SHA1 f23222b8b99f677bdd51dd69e1c02830c9ae3ed1 t/030_lifecycle_singleton.t SHA1 922e35cd63174a3b61a8bbd52ed4dbadb7c3c5b8 t/031_custom_lifecycles.t SHA1 08c19a45d093aa604e58accc4d49e558490a8d44 t/032_singleton_cycle.t SHA1 5bd8711110602d8b70e17f418f768f81c7f4f1c6 t/040_circular_dependencies.t SHA1 bdcc19628732e781aa784540b6e9a64f4fa577ed t/041_parameter_cache_handling.t SHA1 1112ea7072af6e45446fbdef90f92122e3243db0 t/042_parameter_cache_with_singleton.t SHA1 8d0f9b9b3040f3ed3504d605d4987f7e9d821f99 t/043_parameter_leaks.t SHA1 1a322883da6dc77d4e8a7c435a51fd5cc3e0a0ff t/044_deferred_parameters.t SHA1 b12d120ae02005ba35e9d27624d13e86bfd177c4 t/045_parameters_in_dependency.t SHA1 9b034977fc02e9c0839dedae940869d49fdb1f07 t/046_custom_parameter_service.t SHA1 b070caf996694e7e7a30e2034eb58b075e4396c6 t/047_dependencies_override_parameters.t SHA1 c3fbef146dd0cb4e2b950f4b64a444d77c72ffb9 t/048_array_deps.t SHA1 a08571dd7fe6209957cc9b42c8caaf5be299301e t/050_parameterized_containers.t SHA1 e703a7feb5f9fc0378ca071cd6deacad98545287 t/051_more_parameterized_containers.t SHA1 e150d7901aae99d921b108c69ae76626d19282dc t/052_parameterized_in_hierarchy.t SHA1 aafa31e05d4566ed9ac7221bb29ae14b03900ad7 t/053_parameterized_clone.t SHA1 6c600e0ec344a06a4ba8bf0e298512aff42c8481 t/054_parameterized_backref.t SHA1 de90eac1dd7f192b72d8ffb220b644259ce895b0 t/060_extend_w_sugar.t SHA1 3fd84c5cee4396db778672bd5c77caa8053b8893 t/061_extends_w_sugar_and_inheritance.t SHA1 bbd8ff59c278a92b02a31ad8b9489d73de62328a t/062_service_class_w_sugar.t SHA1 12b0f79c551522f3e3fd6ac440d23948b1260b4b t/070_with_basic_typemap.t SHA1 72ae6d23b2bbbef492cea5b78af4ba717a3152b0 t/071_typemap_with_basic_infer.t SHA1 fcbd1bb111509c471420901f1399327ac2f35396 t/072_typemap_with_more_infer.t SHA1 24b1f72ed8e9aa3a59a23b1c486a7a118bb78a6d t/073_typemap_with_role_infer.t SHA1 2de1f3670a3be68cca59325d0b8a257a40561418 t/074_typemap_w_recursive_infer.t SHA1 14267b699d900ec9d6ac9a61e41f31d138780105 t/075_complex_typemap_example.t SHA1 e65e08ce2aa8e6d8127c46db7a9abef24b6880a8 t/076_more_complex_typemap.t SHA1 afda47d7b06806913a46de7f0c1a1121bc5a6fb8 t/077_more_complex_typemap_w_roles.t SHA1 429e80736f12e9a39564832e7e6064b944a2025f t/078_complex_typemap_w_error.t SHA1 01e0c90944205b48c72fe474822a388f393b77ee t/079_depending_on_type.t SHA1 1b5b6303ded514dfce966f1746876e84a9f7e861 t/080_infer_subclasses.t SHA1 58b82465814d8f6ebfa130cfea855af25cebcd96 t/100_clone_w_constructor_injection.t SHA1 32c6ce14d89439aefe7abf70530bf13308096cf4 t/101_clone_w_setter_injection.t SHA1 b8d57052d9872f91ac3ec3b26ff75575c561aa23 t/102_clone_w_block_injection.t SHA1 e636efbbd94211838ff25b9f2220b7f41d79d6dc t/110_clone_w_singleton.t SHA1 acceb11f1aa5176f06fc2321d18812bf2d022124 t/150_deferred_parameters_fail.t SHA1 ddb7acd4285f069e37424a30edac2a10968f7f69 t/151_sugar_no_container.t SHA1 856fcf40f418b9dbc7b77b086fec5873297bbd80 t/152_sugar_service_inheritance.t SHA1 845bb96d3876c4abe15e7a6844b54152c3056795 t/153_sugar_container_inheritance.t SHA1 32fd1ebfdaa14aa3effb5b649c19d188ecd18be8 t/200_example_code.t SHA1 32f8035e381bc3a4711ca30d472dee665f487084 t/201_log_dispatch_example.t SHA1 85f4ba29c828ae6cadedf60d2710a04ef38d6984 t/202_form_sensible_example.t SHA1 c36904adc649abfc2544c3f04fa8cd9a20e2f838 t/300_no_new.t SHA1 ed942f8f06034dc7a16b87b6e6dfb0bd598c7aaf t/301_sugar.t SHA1 56996a79c526cd7759b153275cc315b7df26dc70 t/302_path_traversal_deprecation.t SHA1 df1416e4b62108c5085ce11e8d1a9387a2490952 t/310_literal_keyword.t SHA1 d9e2c050ee0bbf915711fad435f2e3608d12b361 t/500-gh61.t SHA1 5b22f42755e60f9eae74f624b252f0b5f168f980 t/lib/bad.bb SHA1 cd82998b7b796280f4fa50bae8a77c2d6a4ad004 t/lib/false.bb SHA1 ec62b218160c4f116cb0d0966d6eef39eae9d701 t/lib/logger.bb SHA1 dd10457eebda57a6f9aa89ee9a3d1e67a7298cfc t/lib/my_app.bb SHA1 d1fe7d94b3edc7847eb187d4ee41f66e19cf8907 xt/release/unused-vars.t -----BEGIN PGP SIGNATURE----- iEYEARECAAYFAl0WoJoACgkQ34Hwf+GwC4z8swCgoQ9+QVlGBHsn+Zd6hS4ILBjJ uvgAn0pkCex4yuD9a/tW7xIm+6LBkDXW =k9Kt -----END PGP SIGNATURE----- Bread-Board-0.37/INSTALL0000644000175000017500000000166213505520232014056 0ustar yanickyanickThis is the Perl distribution Bread-Board. Installing Bread-Board is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm Bread::Board If you are installing into a system-wide directory, you may need to pass the "-S" flag to cpanm, which uses sudo to install the module: % cpanm -S Bread::Board ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan Bread::Board ## Manual installation As a last resort, you can manually install it. Download the tarball, untar it, then build it: % perl Makefile.PL % make && make test Then install it: % make install If you are installing into a system-wide directory, you may need to run: % sudo make install ## Documentation Bread-Board documentation is available as POD. You can run perldoc from a shell to read the documentation: % perldoc Bread::Board Bread-Board-0.37/CONTRIBUTORS0000644000175000017500000000135313505520232014702 0ustar yanickyanick # BREAD-BOARD CONTRIBUTORS # This is the (likely incomplete) list of people who have helped make this distribution what it is, either via code contributions, patches, bug reports, help with troubleshooting, etc. A huge 'thank you' to all of them. * Alex Balhatchet * André Walker * ben hengst * Brad Bowman * Caleb Cushing * Daisuke Maki * Dave Rolsky * Florian Ragwitz * Gabor Szabo * Gianni Ceccarelli * Graham Knop * Jason Galea * Jason May * Jay Hannah * Jesse Luehrs * Jonathan Rockway * Kip Hampton * Mohammad S Anwar * Neil Bowers * Philippe Bruhat (BooK) * Sterling Hanenkamp * Tomas Doran * Yanick Champoux * Yanick Champoux * zdk Bread-Board-0.37/META.json0000644000175000017500000001514513505520232014447 0ustar yanickyanick{ "abstract" : "A solderless way to wire up your application components", "author" : [ "Stevan Little " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Bread-Board", "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Test::More" : "0.96", "Test::Vars" : "0" } }, "runtime" : { "requires" : { "Carp" : "0", "Module::Runtime" : "0", "Moose" : "0", "Moose::Exporter" : "2.1200", "Moose::Role" : "0", "Moose::Util" : "0", "Moose::Util::TypeConstraints" : "0", "MooseX::Clone" : "0.05", "MooseX::Params::Validate" : "0.14", "Scalar::Util" : "0", "Try::Tiny" : "0", "overload" : "0", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "FindBin" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Test::Fatal" : "0", "Test::Moose" : "0", "Test::More" : "0", "Test::Requires" : "0", "perl" : "5.006" } } }, "provides" : { "Bread::Board" : { "file" : "lib/Bread/Board.pm", "version" : "0.37" }, "Bread::Board::BlockInjection" : { "file" : "lib/Bread/Board/BlockInjection.pm", "version" : "0.37" }, "Bread::Board::ConstructorInjection" : { "file" : "lib/Bread/Board/ConstructorInjection.pm", "version" : "0.37" }, "Bread::Board::Container" : { "file" : "lib/Bread/Board/Container.pm", "version" : "0.37" }, "Bread::Board::Container::FromParameterized" : { "file" : "lib/Bread/Board/Container/FromParameterized.pm", "version" : "0.37" }, "Bread::Board::Container::Parameterized" : { "file" : "lib/Bread/Board/Container/Parameterized.pm", "version" : "0.37" }, "Bread::Board::Dependency" : { "file" : "lib/Bread/Board/Dependency.pm", "version" : "0.37" }, "Bread::Board::Dumper" : { "file" : "lib/Bread/Board/Dumper.pm", "version" : "0.37" }, "Bread::Board::LifeCycle" : { "file" : "lib/Bread/Board/LifeCycle.pm", "version" : "0.37" }, "Bread::Board::LifeCycle::Singleton" : { "file" : "lib/Bread/Board/LifeCycle/Singleton.pm", "version" : "0.37" }, "Bread::Board::LifeCycle::Singleton::WithParameters" : { "file" : "lib/Bread/Board/LifeCycle/Singleton/WithParameters.pm", "version" : "0.37" }, "Bread::Board::Literal" : { "file" : "lib/Bread/Board/Literal.pm", "version" : "0.37" }, "Bread::Board::Service" : { "file" : "lib/Bread/Board/Service.pm", "version" : "0.37" }, "Bread::Board::Service::Alias" : { "file" : "lib/Bread/Board/Service/Alias.pm", "version" : "0.37" }, "Bread::Board::Service::Deferred" : { "file" : "lib/Bread/Board/Service/Deferred.pm", "version" : "0.37" }, "Bread::Board::Service::Deferred::Thunk" : { "file" : "lib/Bread/Board/Service/Deferred/Thunk.pm", "version" : "0.37" }, "Bread::Board::Service::Inferred" : { "file" : "lib/Bread/Board/Service/Inferred.pm", "version" : "0.37" }, "Bread::Board::Service::WithClass" : { "file" : "lib/Bread/Board/Service/WithClass.pm", "version" : "0.37" }, "Bread::Board::Service::WithConstructor" : { "file" : "lib/Bread/Board/Service/WithConstructor.pm", "version" : "0.37" }, "Bread::Board::Service::WithDependencies" : { "file" : "lib/Bread/Board/Service/WithDependencies.pm", "version" : "0.37" }, "Bread::Board::Service::WithParameters" : { "file" : "lib/Bread/Board/Service/WithParameters.pm", "version" : "0.37" }, "Bread::Board::SetterInjection" : { "file" : "lib/Bread/Board/SetterInjection.pm", "version" : "0.37" }, "Bread::Board::Traversable" : { "file" : "lib/Bread/Board/Traversable.pm", "version" : "0.37" }, "Bread::Board::Types" : { "file" : "lib/Bread/Board/Types.pm", "version" : "0.37" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/stevan/BreadBoard/issues" }, "homepage" : "https://github.com/stevan/BreadBoard", "repository" : { "type" : "git", "url" : "https://github.com/stevan/BreadBoard.git", "web" : "https://github.com/stevan/BreadBoard" } }, "version" : "0.37", "x_authority" : "cpan:STEVAN", "x_contributor_covenant" : { "version" : 0.01 }, "x_contributors" : [ "Alex Balhatchet ", "Andr\u00e9 Walker ", "ben hengst ", "Brad Bowman ", "Caleb Cushing ", "Daisuke Maki ", "Dave Rolsky ", "Florian Ragwitz ", "Gabor Szabo ", "Gianni Ceccarelli ", "Graham Knop ", "Jason Galea ", "Jason May ", "Jay Hannah ", "Jesse Luehrs ", "Jonathan Rockway ", "Kip Hampton ", "Mohammad S Anwar ", "Neil Bowers ", "Philippe Bruhat (BooK) ", "Sterling Hanenkamp ", "Tomas Doran ", "Yanick Champoux ", "Yanick Champoux ", "zdk " ], "x_generated_by_perl" : "v5.20.2", "x_serialization_backend" : "Cpanel::JSON::XS version 3.0115" } Bread-Board-0.37/META.yml0000644000175000017500000001111313505520232014266 0ustar yanickyanick--- abstract: 'A solderless way to wire up your application components' author: - 'Stevan Little ' build_requires: ExtUtils::MakeMaker: '0' File::Spec: '0' FindBin: '0' IO::Handle: '0' IPC::Open3: '0' Test::Fatal: '0' Test::Moose: '0' Test::More: '0' Test::Requires: '0' perl: '5.006' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Bread-Board provides: Bread::Board: file: lib/Bread/Board.pm version: '0.37' Bread::Board::BlockInjection: file: lib/Bread/Board/BlockInjection.pm version: '0.37' Bread::Board::ConstructorInjection: file: lib/Bread/Board/ConstructorInjection.pm version: '0.37' Bread::Board::Container: file: lib/Bread/Board/Container.pm version: '0.37' Bread::Board::Container::FromParameterized: file: lib/Bread/Board/Container/FromParameterized.pm version: '0.37' Bread::Board::Container::Parameterized: file: lib/Bread/Board/Container/Parameterized.pm version: '0.37' Bread::Board::Dependency: file: lib/Bread/Board/Dependency.pm version: '0.37' Bread::Board::Dumper: file: lib/Bread/Board/Dumper.pm version: '0.37' Bread::Board::LifeCycle: file: lib/Bread/Board/LifeCycle.pm version: '0.37' Bread::Board::LifeCycle::Singleton: file: lib/Bread/Board/LifeCycle/Singleton.pm version: '0.37' Bread::Board::LifeCycle::Singleton::WithParameters: file: lib/Bread/Board/LifeCycle/Singleton/WithParameters.pm version: '0.37' Bread::Board::Literal: file: lib/Bread/Board/Literal.pm version: '0.37' Bread::Board::Service: file: lib/Bread/Board/Service.pm version: '0.37' Bread::Board::Service::Alias: file: lib/Bread/Board/Service/Alias.pm version: '0.37' Bread::Board::Service::Deferred: file: lib/Bread/Board/Service/Deferred.pm version: '0.37' Bread::Board::Service::Deferred::Thunk: file: lib/Bread/Board/Service/Deferred/Thunk.pm version: '0.37' Bread::Board::Service::Inferred: file: lib/Bread/Board/Service/Inferred.pm version: '0.37' Bread::Board::Service::WithClass: file: lib/Bread/Board/Service/WithClass.pm version: '0.37' Bread::Board::Service::WithConstructor: file: lib/Bread/Board/Service/WithConstructor.pm version: '0.37' Bread::Board::Service::WithDependencies: file: lib/Bread/Board/Service/WithDependencies.pm version: '0.37' Bread::Board::Service::WithParameters: file: lib/Bread/Board/Service/WithParameters.pm version: '0.37' Bread::Board::SetterInjection: file: lib/Bread/Board/SetterInjection.pm version: '0.37' Bread::Board::Traversable: file: lib/Bread/Board/Traversable.pm version: '0.37' Bread::Board::Types: file: lib/Bread/Board/Types.pm version: '0.37' requires: Carp: '0' Module::Runtime: '0' Moose: '0' Moose::Exporter: '2.1200' Moose::Role: '0' Moose::Util: '0' Moose::Util::TypeConstraints: '0' MooseX::Clone: '0.05' MooseX::Params::Validate: '0.14' Scalar::Util: '0' Try::Tiny: '0' overload: '0' strict: '0' warnings: '0' resources: bugtracker: https://github.com/stevan/BreadBoard/issues homepage: https://github.com/stevan/BreadBoard repository: https://github.com/stevan/BreadBoard.git version: '0.37' x_authority: cpan:STEVAN x_contributor_covenant: version: 0.01 x_contributors: - 'Alex Balhatchet ' - 'André Walker ' - 'ben hengst ' - 'Brad Bowman ' - 'Caleb Cushing ' - 'Daisuke Maki ' - 'Dave Rolsky ' - 'Florian Ragwitz ' - 'Gabor Szabo ' - 'Gianni Ceccarelli ' - 'Graham Knop ' - 'Jason Galea ' - 'Jason May ' - 'Jay Hannah ' - 'Jesse Luehrs ' - 'Jonathan Rockway ' - 'Kip Hampton ' - 'Mohammad S Anwar ' - 'Neil Bowers ' - 'Philippe Bruhat (BooK) ' - 'Sterling Hanenkamp ' - 'Tomas Doran ' - 'Yanick Champoux ' - 'Yanick Champoux ' - 'zdk ' x_generated_by_perl: v5.20.2 x_serialization_backend: 'YAML::Tiny version 1.67' Bread-Board-0.37/cpanfile0000644000175000017500000000201713505520232014524 0ustar yanickyanickrequires "Carp" => "0"; requires "Module::Runtime" => "0"; requires "Moose" => "0"; requires "Moose::Exporter" => "2.1200"; requires "Moose::Role" => "0"; requires "Moose::Util" => "0"; requires "Moose::Util::TypeConstraints" => "0"; requires "MooseX::Clone" => "0.05"; requires "MooseX::Params::Validate" => "0.14"; requires "Scalar::Util" => "0"; requires "Try::Tiny" => "0"; requires "overload" => "0"; requires "strict" => "0"; requires "warnings" => "0"; on 'test' => sub { requires "ExtUtils::MakeMaker" => "0"; requires "File::Spec" => "0"; requires "FindBin" => "0"; requires "IO::Handle" => "0"; requires "IPC::Open3" => "0"; requires "Test::Fatal" => "0"; requires "Test::Moose" => "0"; requires "Test::More" => "0"; requires "Test::Requires" => "0"; requires "perl" => "5.006"; }; on 'test' => sub { recommends "CPAN::Meta" => "2.120900"; }; on 'configure' => sub { requires "ExtUtils::MakeMaker" => "0"; }; on 'develop' => sub { requires "Test::More" => "0.96"; requires "Test::Vars" => "0"; }; Bread-Board-0.37/t/0000775000175000017500000000000013505520232013265 5ustar yanickyanickBread-Board-0.37/t/046_custom_parameter_service.t0000644000175000017500000000617713505520232021146 0ustar yanickyanick#!perl use warnings; use strict; use Test::More; use Bread::Board; our $at_underscore; our $params; { package Foo; use Moose; has myattr => ( isa => 'Int', is => 'rw', ); has foo => ( is => 'rw', isa => 'Str', required => 1, ); } { package MyCustomWithParametersService; use Moose::Role; with 'Bread::Board::Service::WithParameters' => { -excludes => '_build_parameters' }; sub _build_parameters { { foo => { isa => 'Str', required => 1, } } } no Moose::Role; } { package MyCustomBlockInjection; use Moose; extends 'Bread::Board::BlockInjection'; with 'MyCustomWithParametersService', 'Bread::Board::Service::WithDependencies'; around get => sub { my $orig = shift; my $self = shift; $at_underscore = \@_; $params = $self->params; return $self->$orig(@_); }; __PACKAGE__->meta->make_immutable; no Moose; } { package MyCustomConstructorInjection; use Moose; extends 'Bread::Board::ConstructorInjection'; with 'Bread::Board::Service::WithClass', 'MyCustomWithParametersService', 'Bread::Board::Service::WithDependencies'; around get => sub { my $orig = shift; my $self = shift; $at_underscore = \@_; $params = $self->params; return $self->$orig(@_); }; __PACKAGE__->meta->make_immutable; no Moose; } my $c = Bread::Board::Container->new( name => 'TestApp' ); $c->add_service( MyCustomConstructorInjection->new( class => 'Foo', name => 'foo_ci', dependencies => { myattr => Bread::Board::Literal->new( name => 'true', value => 1 ) } ) ); $c->add_service( MyCustomBlockInjection->new( block => sub { my $s = shift; my $foo = $s->param('foo_ci'); $foo->myattr(2); return $foo; }, name => 'foo_bi', dependencies => { foo_ci => Bread::Board::Dependency->new( service_path => 'foo_ci', service_params => { foo => 'baz' }, ) }, ) ); eval { $c->resolve(service => 'foo_ci') }; like($@, qr/'foo' missing/, q/Can't resolve foo_ci without mandatory attribute/); ok(my $foo_ci = $c->resolve(service => 'foo_ci', parameters => { foo => 'bar' }), 'got the constructor injection right'); isa_ok($foo_ci, 'Foo'); is_deeply($params, { myattr => 1, foo => 'bar' }, 'params ok'); is_deeply($at_underscore, [ 'foo', 'bar' ], '@_ ok'); $foo_ci->myattr(2); $foo_ci->foo('baz'); eval { $c->resolve(service => 'foo_bi') }; like($@, qr/'foo' missing/, q/Can't resolve foo_bi without mandatory attribute/); ok(my $foo_bi = $c->resolve(service => 'foo_bi', parameters => { foo => 'baz' }), 'got the block injection right'); isa_ok($foo_bi, 'Foo'); is_deeply($params, { foo_ci => $foo_ci, foo => 'baz' }, 'params ok'); is_deeply($at_underscore, [ 'foo', 'baz' ], '@_ ok'); done_testing; Bread-Board-0.37/t/074_typemap_w_recursive_infer.t0000644000175000017500000000153713505520232021327 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package My::Foo; use Moose; has 'bar' => ( is => 'ro', isa => 'My::Bar', required => 1 ); package My::Bar; use Moose; has 'foo' => ( is => 'ro', isa => 'My::Foo', required => 1 ); } { my $c = container 'MyTestContainer' => as { typemap 'My::Foo' => infer; }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); isa_ok($foo->bar, 'My::Bar'); } { my $bar = $c->resolve( service => 'type:My::Bar' ); isa_ok($bar, 'My::Bar'); isa_ok($bar->foo, 'My::Foo'); } } done_testing; Bread-Board-0.37/t/054_parameterized_backref.t0000644000175000017500000000165413505520232020357 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; my $c = container Foo => as { service fez => 123; container Bar => ['Baz'] => as { service moo => ( block => sub { my ($s) = @_; $s->param('kooh') + $s->param('fez'); }, dependencies => { kooh => depends_on('Baz/boo'), fez => depends_on('../fez'), }, ); }; container Bif => as { service boo => 42; }; }; is $c->fetch('Bar')->create(Baz => $c->fetch('Bif'))->resolve(service => 'moo'), 165, 'container works as expected'; my $clone; is exception { $clone = $c->clone }, undef, 'cloning the container does not throw an exception'; is $clone->fetch('Bar')->create(Baz => $clone->fetch('Bif'))->resolve(service => 'moo'), 165, 'can do parameterized backref from clone'; done_testing; Bread-Board-0.37/t/040_circular_dependencies.t0000644000175000017500000000634713505520232020357 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; =pod This test checks the basic cyclical dependency handling. It is not quite as sophisticated as the IOC one, but it is good enough. Honestly, in all the years of using IOC, I never needed to use cyclical deps. =cut { package Class::A; use Moose; has 'B' => (is => 'ro', isa => 'Class::B'); package Class::B; use Moose; has 'A' => (is => 'ro', isa => 'Class::A'); package Class::C; use Moose; has 'D' => (is => 'ro', isa => 'Class::D'); package Class::D; use Moose; has 'E' => (is => 'ro', isa => 'Class::E'); package Class::E; use Moose; has 'F' => (is => 'ro', isa => 'Class::F'); package Class::F; use Moose; has 'C' => (is => 'ro', isa => 'Class::C'); } # +---+ # +--| A |<-+ # | +---+ | # | +---+ | # +->| B |--+ # +---+ { my $c = container 'Test' => as { service 'A' => ( class => 'Class::A', lifecycle => 'Singleton', dependencies => [ depends_on('B') ] ); service 'B' => ( class => 'Class::B', lifecycle => 'Singleton', dependencies => [ depends_on('A') ] ); }; isa_ok($c, 'Bread::Board::Container'); ok($c->has_service('A'), '... got the A service'); ok($c->has_service('B'), '... got the B service'); my $b = $c->resolve( service => 'B' ); isa_ok($b, 'Class::B'); my $a = $c->resolve( service => 'A'); isa_ok($a, 'Class::A'); isa_ok($b->A, 'Class::A'); isa_ok($a->B, 'Class::B'); is($a->B, $b, '... our Bs match'); is($b->A, "$a", '... our As match'); } # +---+ # +--| C |<-+ # | +---+ | # +-V-+ +---+ # | D | | F | # +---+ +-^-+ # | +---+ | # +->| E |--+ # +---+ { my $container = container 'Test' => as { service 'C' => ( class => 'Class::C', lifecycle => 'Singleton', dependencies => [ depends_on('D') ] ); service 'D' => ( class => 'Class::D', lifecycle => 'Singleton', dependencies => [ depends_on('E') ] ); service 'E' => ( class => 'Class::E', lifecycle => 'Singleton', dependencies => [ depends_on('F') ] ); service 'F' => ( class => 'Class::F', lifecycle => 'Singleton', dependencies => [ depends_on('C') ] ); }; isa_ok($container, 'Bread::Board::Container'); ok($container->has_service($_), '... got the ' . $_ . ' service') for qw/C D E F/; my $c = $container->resolve( service => 'C' ); isa_ok($c, 'Class::C'); my $d = $container->resolve( service => 'D' ); isa_ok($d, 'Class::D'); my $e = $container->resolve( service => 'E' ); isa_ok($e, 'Class::E'); my $f = $container->resolve( service => 'F' ); isa_ok($f, 'Class::F'); isa_ok($c->D, 'Class::D'); isa_ok($d->E, 'Class::E'); isa_ok($e->F, 'Class::F'); isa_ok($f->C, 'Class::C'); is($f->C, $c, '... our Cs match'); is($c->D->E->F, $f, '... our Fs match'); } done_testing; Bread-Board-0.37/t/061_extends_w_sugar_and_inheritance.t0000644000175000017500000000564113505520232022440 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package DBH; use Moose; has 'dsn' => (is => 'ro', isa => 'Str'); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); has 'dbh' => (is => 'ro', isa => 'DBH', required => 1); } { package My::App; use Moose; use Bread::Board; extends 'Bread::Board::Container'; has 'log_file_name' => ( is => 'ro', isa => 'Str', default => 'logfile.log', ); sub BUILD { my $self = shift; container $self => as { service 'log_file' => $self->log_file_name; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => { log_file => depends_on('log_file'), } ); service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); }; } package My::App::Extended; use Moose; use Bread::Board; extends 'My::App'; has 'dsn' => ( is => 'ro', isa => 'Str', required => 1, ); sub BUILD { my $self = shift; container $self => as { service 'db_conn' => ( class => 'DBH', dependencies => [ (service 'dsn' => $self->dsn) ] ); service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), dbh => depends_on('db_conn'), } ); }; } } my $c = My::App::Extended->new( name => 'MyApp', dsn => 'dbi:mysql:test' ); isa_ok($c, 'My::App::Extended'); isa_ok($c, 'My::App'); isa_ok($c, 'Bread::Board::Container'); # test the first one my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $dbh = $c->resolve( service => 'db_conn' ); isa_ok($dbh, 'DBH'); is($dbh->dsn, 'dbi:mysql:test', '... got the right dsn'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); isa_ok($app->dbh, 'DBH'); isnt($app->dbh, $dbh, '... got the right db_conn (not a singleton)'); done_testing; Bread-Board-0.37/t/lib/0000775000175000017500000000000013505520232014033 5ustar yanickyanickBread-Board-0.37/t/lib/my_app.bb0000644000175000017500000000053513505520232015626 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use FindBin; use Bread::Board; container 'MyApp' => as { service 'log_file' => "logfile.log"; include "$FindBin::Bin/lib/logger.bb"; service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); };Bread-Board-0.37/t/lib/false.bb0000644000175000017500000000000313505520232015421 0ustar yanickyanick0; Bread-Board-0.37/t/lib/bad.bb0000644000175000017500000000003513505520232015062 0ustar yanickyanickfunction_doesnt_exist 'foo'; Bread-Board-0.37/t/lib/logger.bb0000644000175000017500000000034113505520232015613 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Bread::Board; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => { log_file => depends_on('log_file'), } );Bread-Board-0.37/t/047_dependencies_override_parameters.t0000644000175000017500000000150613505520232022614 0ustar yanickyanick#!/usr/bin/env perl use strict; use warnings; use Test::More; use Bread::Board; my $c = container Foo => as { service a => 'A'; service foo => ( block => sub { $_[0]->param('a'); }, dependencies => ['a'], parameters => { # XXX should optional need to be specified here? or should an # existing dependency of the same name imply that? seems to # parallel the default/required situation in moose, where default # overrides required, but not sure a => { optional => 1 }, }, ); }; is($c->resolve(service => 'foo'), 'A', 'can resolve foo service with its default dependency'); is($c->resolve(service => 'foo', parameters => { a => 'B' }), 'B', 'can resolve foo service with a parameter overriding the default'); done_testing; Bread-Board-0.37/t/300_no_new.t0000644000175000017500000000022313505520232015314 0ustar yanickyanickuse strict; use warnings; use Test::More; use Bread::Board (); ok !Bread::Board->can("new"), 'Bread::Board has no new() method'; done_testing; Bread-Board-0.37/t/151_sugar_no_container.t0000644000175000017500000000347513505520232017726 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } my $file_service = service 'log_file' => "logfile.log"; does_ok($file_service, 'Bread::Board::Service'); my $logger_service = service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => { log_file => depends_on('log_file'), } ); does_ok($logger_service, 'Bread::Board::Service'); my $app_service = service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); does_ok($app_service, 'Bread::Board::Service'); my $bunyan_service = alias 'paul_bunyan' => 'logger'; does_ok($bunyan_service, 'Bread::Board::Service'); isa_ok($bunyan_service, 'Bread::Board::Service::Alias'); my $c = container 'MyApp'; isa_ok($c, 'Bread::Board::Container'); foreach ( $file_service, $logger_service, $app_service, $bunyan_service) { $c->add_service($_); } my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); my $bunyan = $c->resolve( service => 'paul_bunyan' ); isa_ok($bunyan, 'FileLogger'); is($bunyan, $logger, 'standalone alias works.'); done_testing; Bread-Board-0.37/t/005_alias.t0000644000175000017500000001601513505520232015130 0ustar yanickyanick#!/usr/bin/env perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { package Some::Class; use Moose; has foo => ( is => 'ro', isa => 'Str', ); } { my $c = container 'MyApp' => as { service 'foo' => 'FOO'; service 'bar' => ( block => sub { 'BAR' }, ); service 'baz' => ( class => 'Some::Class', ); alias 'foo_alias' => 'foo'; alias 'bar_alias' => 'bar'; alias 'baz_alias' => 'baz'; }; is($c->resolve(service => 'foo_alias'), 'FOO', "literal aliases work"); is($c->resolve(service => 'bar_alias'), 'BAR', "block aliases work"); isa_ok($c->resolve(service => 'baz_alias'), 'Some::Class'); is($c->fetch('foo_alias')->name, 'foo', "fetch on aliases returns the underlying service"); } { my $c = container 'MyApp' => as { service 'foo' => 'FOO'; service 'bar' => ( block => sub { my $s = shift; return $s->param('foo') . 'BAR'; }, dependencies => ['foo'], ); service 'baz' => ( class => 'Some::Class', dependencies => ['foo'], ); alias 'bar_alias' => 'bar'; alias 'baz_alias' => 'baz'; }; is($c->resolve(service => 'bar_alias'), 'FOOBAR', "block aliases with deps work"); is($c->resolve(service => 'baz_alias')->foo, 'FOO', "constructor aliases with deps work"); } { my $c = container 'MyApp' => as { service 'real_foo' => 'FOO'; service 'bar' => ( block => sub { my $s = shift; return $s->param('foo') . 'BAR'; }, dependencies => ['foo'], ); service 'baz' => ( class => 'Some::Class', dependencies => ['foo'], ); alias 'foo' => 'real_foo'; }; is($c->resolve(service => 'bar'), 'FOOBAR', "blocks can dep on aliases"); is($c->resolve(service => 'baz')->foo, 'FOO', "constructor injections can dep on aliases"); } { my $c = container 'MyApp' => as { service 'foo' => ( block => sub { my $s = shift; return 'FOO' . $s->param('sub_bar'); }, dependencies => ['sub_bar'], ); alias 'sub_bar' => 'SubApp/bar1'; container 'SubApp' => as { service 'bar1' => 'BAR'; service 'bar2' => ( block => sub { my $s = shift; return 'BAR' . $s->param('parent_foo') . $s->param('root_foo') . $s->param('sub_baz'); }, dependencies => ['parent_foo', 'root_foo', 'sub_baz'], ); alias 'parent_foo' => '../foo'; alias 'root_foo' => '/foo'; alias 'sub_baz' => 'SubSubApp/baz1'; container 'SubSubApp' => as { service 'baz1' => 'BAZ'; service 'baz2' => ( block => sub { my $s = shift; return 'BAZ' . $s->param('parent_bar') . $s->param('parent_foo') . $s->param('root_foo'); }, dependencies => ['parent_bar', 'parent_foo', 'root_foo'], ); alias 'parent_bar' => '../bar1'; alias 'parent_foo' => '../../foo'; alias 'root_foo' => '/foo'; }; }; }; is($c->resolve(service => 'foo'), 'FOOBAR', "aliases to nested containers work"); is($c->resolve(service => 'sub_bar'), 'BAR', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/bar1'), 'BAR', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/bar2'), 'BARFOOBARFOOBARBAZ', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/parent_foo'), 'FOOBAR', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/root_foo'), 'FOOBAR', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/sub_baz'), 'BAZ', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/SubSubApp/baz1'), 'BAZ', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/SubSubApp/baz2'), 'BAZBARFOOBARFOOBAR', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/SubSubApp/parent_bar'), 'BAR', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/SubSubApp/parent_foo'), 'FOOBAR', "aliases to nested containers work"); is($c->resolve(service => 'SubApp/SubSubApp/root_foo'), 'FOOBAR', "aliases to nested containers work"); } { my $c = container 'MyApp' => as { service 'foo' => 'FOO'; alias 'foo1' => 'foo'; alias 'foo2' => 'foo1'; }; is($c->resolve(service => 'foo2'), 'FOO', "multi-level aliases work"); is($c->fetch('foo2')->name, 'foo', "multi-level fetching works"); } { my $c; is(exception { $c = container 'MyApp' => as { alias 'foo' => 'doesnt_exist'; alias 'a' => 'a'; alias 'b' => 'c'; alias 'c' => 'b'; alias 'd' => 'e'; alias 'e' => 'f'; alias 'f' => 'd'; }; }, undef, "bad aliases don't die on creation"); like(exception { $c->resolve(service => 'foo'); }, qr/^While resolving alias foo: Could not find container or service for doesnt_exist in MyApp/, "error when aliasing to something that doesn't exist"); like(exception { $c->resolve(service => 'a'); }, qr/^Cycle detected in aliases/, "error with self-referencing aliases"); like(exception { $c->resolve(service => 'b'); }, qr/^Cycle detected in aliases/, "error with circular aliases"); like(exception { $c->resolve(service => 'd'); }, qr/^Cycle detected in aliases/, "error with circular aliases with larger cycles"); like(exception { $c->fetch('a'); }, qr/^Cycle detected in aliases/, "error with self-referencing aliases"); like(exception { $c->fetch('b'); }, qr/^Cycle detected in aliases/, "error with circular aliases"); like(exception { $c->fetch('d'); }, qr/^Cycle detected in aliases/, "error with circular aliases with larger cycles"); } { my $c = container 'MyApp' => as { service 'foo' => ( class => 'Some::Class', lifecycle => 'Singleton', ); alias 'foo_alias' => 'foo'; }; is($c->resolve(service => 'foo'), $c->resolve(service => 'foo'), "same object, since it's a singleton"); is($c->resolve(service => 'foo_alias'), $c->resolve(service => 'foo_alias'), "same object, since it's a singleton"); } done_testing; Bread-Board-0.37/t/004_block_injection_w_out_class.t0000644000175000017500000000146413505520232021576 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Scalar::Util 'blessed'; use Bread::Board::BlockInjection; use Bread::Board::Literal; my $s = Bread::Board::BlockInjection->new( name => 'NoClass', block => sub { my $s = shift; return +{ foo => $s->param('foo') } }, dependencies => { foo => Bread::Board::Literal->new( name => 'foo', value => 'FOO' ) } ); isa_ok($s, 'Bread::Board::BlockInjection'); does_ok($s, 'Bread::Board::Service::WithDependencies'); does_ok($s, 'Bread::Board::Service::WithParameters'); does_ok($s, 'Bread::Board::Service'); my $x = $s->get; ok( !blessed($x), '... the result of the block injection is not blessed'); is_deeply($x, { foo => 'FOO' }, '... block injections can return unblessed values'); done_testing; Bread-Board-0.37/t/079_depending_on_type.t0000644000175000017500000000144313505520232017543 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package Stapler; use Moose; package Desk; use Moose; has 'stapler' => ( is => 'ro', isa => 'Stapler', required => 1 ); package Employee; use Moose; has 'desk' => ( is => 'ro', isa => 'Desk' ); } my $c = container 'TypeDependencyTest' => as { typemap 'Desk' => infer; service 'Employee' => ( class => 'Employee', dependencies => { desk => 'type:Desk' } ); }; my $desk = $c->resolve( type => 'Desk' ); isa_ok($desk, 'Desk'); isa_ok($desk->stapler, 'Stapler'); my $employee = $c->resolve( service => 'Employee' ); isa_ok($employee, 'Employee'); isa_ok($employee->desk, 'Desk'); isa_ok($employee->desk->stapler, 'Stapler'); done_testing; Bread-Board-0.37/t/052_parameterized_in_hierarchy.t0000644000175000017500000000257413505520232021426 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { package My::Database::Handle; use Moose; has ['dsn', 'username', 'password'] => ( is => 'ro', isa => 'Str', required => 1 ); } my $utils = container 'Utils' => as { container 'Database' => [ 'DBConnInfo' ] => as { service 'handle' => ( class => 'My::Database::Handle', dependencies => { dsn => depends_on('DBConnInfo/dsn'), username => depends_on('DBConnInfo/username'), password => depends_on('DBConnInfo/password'), } ); }; }; isa_ok($utils, 'Bread::Board::Container'); my $db_conn_info = container 'DatabaseConnection' => as { service 'dsn' => 'dbi:mysql:foo'; service 'username' => 'bar'; service 'password' => '***'; }; isa_ok($db_conn_info, 'Bread::Board::Container'); my $db = $utils->fetch('Database'); isa_ok($db, 'Bread::Board::Container::Parameterized'); isnt(exception { $utils->fetch('Database')->fetch('handle'); }, undef, '... cannot fetch on a parameterized container'); isnt(exception { $utils->fetch('Database/handle'); }, undef, '... cannot fetch within a parameterized container'); my $dbh = $db->create( DBConnInfo => $db_conn_info )->resolve( service => 'handle' ); isa_ok($dbh, 'My::Database::Handle'); done_testing; Bread-Board-0.37/t/041_parameter_cache_handling.t0000644000175000017500000000201613505520232021002 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { package Foo; use Moose; has 'bar' => (is => 'ro', isa => 'Int', required => 1); package Bar; use Moose; has 'foo' => (is => 'ro', isa => 'Int', required => 1); } my $c = container 'MyApp' => as { service 'foo' => ( class => 'Foo', parameters => { bar => { isa => 'Int' } } ); service 'bar' => ( class => 'Bar', parameters => { foo => { isa => 'Int' } } ); }; my $foo; is(exception { $foo = $c->resolve( service => 'foo', parameters => { bar => 10 } ); }, undef, '... got the service correctly'); isa_ok($foo, 'Foo'); is($foo->bar, 10, '... got the right parameter value'); my $bar; is(exception { $bar = $c->resolve( service => 'bar', parameters => { foo => 20 } ); }, undef, '... got the service correctly'); isa_ok($bar, 'Bar'); is($bar->foo, 20, '... got the right parameter value'); done_testing; Bread-Board-0.37/t/100_clone_w_constructor_injection.t0000644000175000017500000000413313505520232022166 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Scalar::Util qw(refaddr); use Bread::Board; { package Test::Class; use Moose; has 'dep' => ( is => 'ro', isa => 'Int' ); } my $board = Bread::Board::Container->new( name => 'app' ); isa_ok($board, 'Bread::Board::Container'); $board->add_service( Bread::Board::ConstructorInjection->new( name => 'test', class => 'Test::Class', dependencies => { dep => Bread::Board::Dependency->new(service_path => '/dep'), }, ) ); ok($board->has_service('test'), '... got the test service'); isa_ok($board->get_service('test'), 'Bread::Board::ConstructorInjection'); # clone ... my $board2 = $board->clone; isa_ok($board2, 'Bread::Board::Container'); isnt($board, $board2, '... they are not the same instance'); ok($board2->has_service('test'), '... got the test service'); isa_ok($board2->get_service('test'), 'Bread::Board::ConstructorInjection'); isnt($board->get_service('test'), $board2->get_service('test'), '... not the same test services'); # add dep services ... $board->add_service( Bread::Board::Literal->new(name => 'dep', value => 1) ); ok($board->has_service('dep'), '... got the dep service'); isa_ok($board->get_service('dep'), 'Bread::Board::Literal'); ok(!$board2->has_service('dep'), '... board2 does not have the dep service'); $board2->add_service( Bread::Board::Literal->new(name => 'dep', value => 2) ); ok($board2->has_service('dep'), '... got the dep service'); isa_ok($board2->get_service('dep'), 'Bread::Board::Literal'); isnt($board->get_service('dep'), $board2->get_service('dep'), '... not the same dep services'); # test them ... is($board->fetch('/dep')->get(), 1, '... got correct dep'); is($board->fetch('/test')->get()->dep, 1, '... test uses dep'); is(refaddr $board->fetch('/test')->parent, refaddr $board, '... got the right board'); is($board2->fetch('/dep')->get(), 2, '... got correct dep'); is($board2->fetch('/test')->get()->dep, 2, '... test uses dep'); is(refaddr $board2->fetch('/test')->parent, refaddr $board2, '... got the right board'); done_testing; Bread-Board-0.37/t/028_sugar_w_recursive_inc.t0000644000175000017500000000155613505520232020437 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use FindBin; use Test::More; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } my $c = include "$FindBin::Bin/lib/my_app.bb"; my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); done_testing; Bread-Board-0.37/t/301_sugar.t0000644000175000017500000000504413505520232015157 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; my $exception = exception { container (bless {}, 'NameObject') }; like( $exception, qr/^an object used as a container/, "exception begins with: an object used as a container" ); ok ( (container 'MyApp' => as { service 'service_name', 'service_type' => 'Block', 'block' => sub{} } ), 'set service with service type name' ); my $c = container 'Application'; isa_ok($c, 'Bread::Board::Container'); ok ( (container $c), 'set container with object' ); $exception = exception{ container $c, 'thing1', 'thing2' }; like( $exception, qr/^container\(\$object, \.\.\.\) is not supported/, 'exception begins with: container($object, ...) is not supported' ); $exception = exception{ container 'MyApp' => as { service 'service_name', 'thing1', 'thing2', 'trouble' } }; like( $exception, qr/^A service is defined by/, 'exception begins with: A service is defined by' ); { package MyNonService; use Moose; } $exception = exception{ container 'MyApp' => as { service 'service_name', 'service_class' => 'MyNonService', } }; like( $exception, qr/^The service class must do the Bread::Board::Service role/, 'exception begins with: The service class must do the Bread::Board::Service role' ); $exception = exception{ typemap ('Type') }; like( $exception, qr/^typemap takes a single argument/, "exception begins with: typemap takes a single argument" ); $exception = exception{ typemap ('Type', MyNonService->new) }; like( $exception, qr/isn't a service/, "exception contains: isn't a service" ); { my $parameterized_container = container 'Foo' => ['Bar'] => as { service foo => ( block => sub { shift->param('bar') }, dependencies => { bar => 'Bar/bar' }, ); }; is exception { container $parameterized_container => as { service moo => ( block => sub { shift->param('foo') }, dependencies => [depends_on('foo')], ); }; }, undef, 'contaner $parameterized_container => as {} succeeds'; is $parameterized_container->create(Bar => (container Bar => as { service bar => 42; }))->resolve(service => 'moo'), 42, 'container $parameterized_container => as {} modifies underlying container'; } { my $c = container Foo => as { container 'Bar'; }; isa_ok $c->fetch('Bar'), 'Bread::Board::Container'; } done_testing; Bread-Board-0.37/t/500-gh61.t0000644000175000017500000000310613505520232014517 0ustar yanickyanickuse Test::Requires 'Moo'; { package Foo; use Moo; has str => ( is => 'rw', default => '' ); sub BUILD { my ($self, $args) = @_; $self->str($self->str); } around str => sub { my ($orig, $self, $val) = @_; return $orig->($self) unless defined $val; $orig->( $self, 'prefix_'.$val); }; } # BB was using Class::MOP::class_of to determine # the constructor, and that plays havoc with Moo, # it seems { package Bar; use Moo; extends 'Foo'; } { package Baz; use Moose; extends 'Foo'; } package main; use strict; use warnings; use Test::More; use Bread::Board; my $c = container 'MyApp' => as { map { service lc $_ => ( class => $_, parameters => { str => { optional => 1 } } ) } qw/ Foo Bar Baz / }; subtest $_, \&test_class, $_ for qw/ Foo Bar Baz/; done_testing; sub test_class { my $class = shift; my $plain = $class->new({ str => 'foo_plain' }); is $plain->str => 'prefix_foo_plain'; my $bb = $c->resolve( service => lc $class, parameters => { str => 'foo_bb' } ); is( $class->new( str => 'foo_plain' )->str => 'prefix_foo_plain', 'plain after resolve' ); is $bb->str => 'prefix_foo_bb'; is $plain->str => 'prefix_foo_plain', 'plain after str'; $bb->str('foo_bb_setter'); is $bb->str => 'prefix_foo_bb_setter'; my $plain_after_bb = $class->new({ str => 'foo_plain_after_bb' }); is $plain_after_bb->str => 'prefix_foo_plain_after_bb'; is( Foo->new( str => 'foo_plain' )->str => 'prefix_foo_plain', 'Foo untouched' ); } Bread-Board-0.37/t/032_singleton_cycle.t0000644000175000017500000000167413505520232017225 0ustar yanickyanick#!/usr/bin/env perl use strict; use Test::More; use Bread::Board; my $seen; { package Bot; use Moose; has plugin => ( isa => 'Plugin', is => 'ro', required => 1 ); } { package Plugin; use Moose; has bot => ( isa => 'Bot', is => 'ro', weak_ref => 1, required => 1 ); } my $c = container 'Config' => as { service plugin => ( class => 'Plugin', lifecycle => 'Singleton', dependencies => ['bot'], ); service bot => ( class => 'Bot', block => sub { my ($s) = @_; $seen++; Bot->new(plugin => $s->param('plugin')); }, lifecycle => 'Singleton', dependencies => ['plugin'], ); }; ok($c->resolve(service => 'bot'), 'can resolve bot service'); is($seen, 1, '... and it is seen only once'); done_testing; Bread-Board-0.37/t/043_parameter_leaks.t0000644000175000017500000000171513505520232017201 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { package Foo; use Moose; has 'bar' => (is => 'ro', isa => 'Bar', required => 1); package Bar; use Moose; our $BAR_DEMOLISH_COUNT = 0; sub DESTROY { $BAR_DEMOLISH_COUNT++; } } my $c = container 'MyApp' => as { service 'foo' => ( class => 'Foo', parameters => { bar => { isa => 'Bar' } } ); }; { my $bar = Bar->new; { my $foo; is(exception { $foo = $c->resolve( service => 'foo', parameters => { bar => $bar } ); }, undef, '... got the service correctly'); isa_ok($foo, 'Foo'); is($foo->bar, $bar, '... got the right parameter value'); } is($Bar::BAR_DEMOLISH_COUNT, 0, '... it should be one'); # $bar should be demolished here ... } is($Bar::BAR_DEMOLISH_COUNT, 1, '... it should be one'); done_testing; Bread-Board-0.37/t/201_log_dispatch_example.t0000644000175000017500000000271513505520232020212 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Test::Requires 'Log::Dispatch', 'Log::Dispatch::File', 'Log::Dispatch::Screen'; use Bread::Board; my $c = container 'Logging' => as { service 'Logger' => ( block => sub { my $s = shift; my $c = $s->parent; my $outputs = $c->get_sub_container('Outputs'); my $log = Log::Dispatch->new; foreach my $name ( $outputs->get_service_list ) { $log->add( $outputs->get_service( $name )->get ); } $log; } ); container 'Outputs' => as { service 'File' => ( block => sub { Log::Dispatch::File->new( name => 'file', min_level => 'debug', filename => 'logfile' ) } ); service 'Screen' => ( block => sub { Log::Dispatch::Screen->new( name => 'screen', min_level => 'warning', ) } ); }; }; my $logger = $c->resolve( service => 'Logger' ); isa_ok($logger, 'Log::Dispatch'); my $screen = $logger->output('screen'); isa_ok($screen, 'Log::Dispatch::Screen'); my $file = $logger->output('file'); isa_ok($file, 'Log::Dispatch::File'); unlink 'logfile'; done_testing; Bread-Board-0.37/t/150_deferred_parameters_fail.t0000644000175000017500000000242513505520232021036 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { package Model; use Moose; has [qw(dsn extra_args)] => ( is => 'ro', required => 1, ); } { package UserStore; use Moose; has model => ( is => 'ro', isa => 'Model', required => 1, ); } my $c = container 'MyApp' => as { service model_dsn => 'foo:bar'; service model => ( class => 'Model', lifecycle => 'Singleton', parameters => { extra_args => { default => { create => 1, user => 'foo', password => 'bar', }, }, }, dependencies => { dsn => depends_on('model_dsn'), }, ); service user_store => ( class => 'UserStore', lifecycle => 'Singleton', dependencies => { model => depends_on('/model'), }, ); }; my $store; is(exception { $store = $c->fetch('/user_store')->get; }, undef, '... deferred parameters that have defaults should pass through too'); is($store->model->dsn, 'foo:bar', '... got the right default values'); done_testing; Bread-Board-0.37/t/048_array_deps.t0000644000175000017500000000712713505520232016203 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board::ConstructorInjection; use Bread::Board::Literal; use Bread::Board::Container; use Bread::Board::Dependency; { package Item; use Moose; has my_name => (is => 'ro'); package ListOfItems; use Moose; sub as_string { join ',',map {$_->my_name} @{shift->items} } has 'items' => (is => 'ro', isa => 'ArrayRef'); } { my $s = Bread::Board::ConstructorInjection->new( name => 'list_of_items', class => 'ListOfItems', dependencies => { items => [ map { Bread::Board::ConstructorInjection->new( name => $_, class => 'Item', dependencies => { my_name => Bread::Board::Literal->new( name => 'item_name', value => $_, ), }, ) } qw(one two three) ], }, ); my $output = $s->get->as_string; is( $output, 'one,two,three', 'no container worked' ); } { my $c = Bread::Board::Container->new( name => 'list_container', services => [ (map { Bread::Board::ConstructorInjection->new( name => "item_$_", class => 'Item', dependencies => { my_name => Bread::Board::Literal->new( name => 'item_name', value => $_, ), }, ) } qw(one two three)), Bread::Board::ConstructorInjection->new( name => 'list_of_items', class => 'ListOfItems', dependencies => { items => [ map { "item_$_" } qw(one two three) ], }, ), ], ); my $output = $c->fetch('list_of_items')->get->as_string; is( $output, 'one,two,three', 'container with no ambiguous path names worked' ); } { my $c = Bread::Board::Container->new( name => 'list_container', sub_containers => [ map { Bread::Board::Container->new( name => "$_", services => [ Bread::Board::ConstructorInjection->new( name => "item", class => 'Item', dependencies => { my_name => Bread::Board::Literal->new( name => 'item_name', value => $_, ), }, ) ], ) } qw(one two three), ], services => [ Bread::Board::ConstructorInjection->new( name => 'list_of_items', class => 'ListOfItems', dependencies => { # all of these have a service_name of "item", the # dependency coercion must give them distinct # names items => [ map { "/$_/item" } qw(one two three) ], }, ), ], ); my $output = $c->fetch('list_of_items')->get->as_string; is( $output, 'one,two,three', 'multiple containers with ambiguous names worked' ); } done_testing; Bread-Board-0.37/t/062_service_class_w_sugar.t0000644000175000017500000000261213505520232020414 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package My::LoggerService; use Moose; extends 'Bread::Board::ConstructorInjection'; with 'Bread::Board::LifeCycle::Singleton'; has '+class' => (default => 'FileLogger'); package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } my $c = container 'MyApp' => as { service 'log_file' => "logfile.log"; service 'logger' => ( service_class => 'My::LoggerService', dependencies => { log_file => depends_on('log_file'), } ); service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); }; my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); done_testing; Bread-Board-0.37/t/060_extend_w_sugar.t0000644000175000017500000000515413505520232017060 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } { package My::App; use Moose; use Bread::Board; extends 'Bread::Board::Container'; has 'log_file_name' => ( is => 'ro', isa => 'Str', default => 'logfile.log', ); sub BUILD { my $self = shift; container $self => as { service 'log_file' => $self->log_file_name; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => { log_file => depends_on('log_file'), } ); service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); }; } } my $c1 = My::App->new( name => 'MyApp1' ); isa_ok($c1, 'My::App'); isa_ok($c1, 'Bread::Board::Container'); my $c2 = My::App->new( name => 'MyApp2', log_file_name => 'another_logfile.log' ); isa_ok($c2, 'My::App'); isa_ok($c2, 'Bread::Board::Container'); # test the first one my $logger1 = $c1->resolve( service => 'logger' ); isa_ok($logger1, 'FileLogger'); is($logger1->log_file, 'logfile.log', '... got the right logfile dep'); is($c1->fetch('logger/log_file')->service, $c1->fetch('log_file'), '... got the right value'); is($c1->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app1 = $c1->resolve( service => 'application' ); isa_ok($app1, 'MyApplication'); isa_ok($app1->logger, 'FileLogger'); is($app1->logger, $logger1, '... got the right logger (singleton)'); # test the second one my $logger2 = $c2->resolve( service => 'logger' ); isa_ok($logger2, 'FileLogger'); is($logger2->log_file, 'another_logfile.log', '... got the right logfile dep'); is($c2->fetch('logger/log_file')->service, $c2->fetch('log_file'), '... got the right value'); is($c2->fetch('logger/log_file')->get, 'another_logfile.log', '... got the right value'); my $app2 = $c2->resolve( service => 'application' ); isa_ok($app2, 'MyApplication'); isa_ok($app2->logger, 'FileLogger'); is($app2->logger, $logger2, '... got the right logger (singleton)'); # make sure they share nothing isnt( $logger1, $logger2, '... these are not the same' ); isnt( $app1, $app2, '... these are not the same' ); done_testing; Bread-Board-0.37/t/110_clone_w_singleton.t0000644000175000017500000000357513505520232017553 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Scalar::Util qw(refaddr); use Bread::Board; { package Test::Class; use Moose; has 'dep' => ( is => 'ro', isa => 'Int' ); } my $board = Bread::Board::Container->new( name => 'app' ); isa_ok($board, 'Bread::Board::Container'); $board->add_service( Bread::Board::ConstructorInjection->new( lifecycle => 'Singleton', name => 'test', class => 'Test::Class', dependencies => { dep => Bread::Board::Dependency->new(service_path => '/dep'), }, ) ); ok($board->has_service('test'), '... got the test service'); isa_ok($board->get_service('test'), 'Bread::Board::ConstructorInjection'); $board->add_service( Bread::Board::Literal->new(name => 'dep', value => 1) ); ok($board->has_service('dep'), '... got the dep service'); isa_ok($board->get_service('dep'), 'Bread::Board::Literal'); ## check the singleton-ness is($board->fetch('/test')->get, $board->fetch('/test')->get, '... got the singleton'); # clone ... my $board2 = $board->clone; isa_ok($board2, 'Bread::Board::Container'); isnt($board, $board2, '... they are not the same instance'); ok($board2->has_service('test'), '... got the test service'); isa_ok($board2->get_service('test'), 'Bread::Board::ConstructorInjection'); ok($board2->has_service('dep'), '... got the dep service'); isa_ok($board2->get_service('dep'), 'Bread::Board::Literal'); isnt($board->get_service('test'), $board2->get_service('test'), '... not the same test services'); isnt($board->get_service('dep'), $board2->get_service('dep'), '... not the same dep services'); ## check the singleton-ness is($board2->fetch('/test')->get, $board2->fetch('/test')->get, '... got the singleton'); ## check the singleton-less-ness isnt($board->fetch('/test')->get, $board2->fetch('/test')->get, '... singleton are not shared'); done_testing; Bread-Board-0.37/t/050_parameterized_containers.t0000644000175000017500000000435713505520232021126 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { package My::Simple::Logger; use Moose; package My::Database::Logger; use Moose; has ['dsn', 'username', 'password'] => ( is => 'ro', isa => 'Str', required => 1 ); package My::Application; use Moose; has 'log_handle' => ( is => 'ro', isa => 'Object', required => 1 ); } my $simple_logger = container 'SimpleLogger' => as { service 'handle' => ( class => 'My::Simple::Logger' ); }; isa_ok($simple_logger, 'Bread::Board::Container'); my $db_conn_info = container 'DatabaseConnection' => as { service 'dsn' => 'dbi:mysql:foo'; service 'username' => 'bar'; service 'password' => '***'; }; isa_ok($db_conn_info, 'Bread::Board::Container'); my $db_logger = container 'DatabaseLogger' => [ 'DBConnInfo' ] => as { service 'handle' => ( class => 'My::Database::Logger', dependencies => { dsn => depends_on('DBConnInfo/dsn'), username => depends_on('DBConnInfo/username'), password => depends_on('DBConnInfo/password'), } ); }; isa_ok($db_logger, 'Bread::Board::Container::Parameterized'); isnt(exception { $db_logger->fetch('handle') }, undef, '... cannot call fetch on a parameterized container'); my $app = container 'Application' => [ 'Logger' ] => as { service 'app' => ( class => 'My::Application', dependencies => { log_handle => depends_on('Logger/handle') } ); }; isa_ok($app, 'Bread::Board::Container::Parameterized'); isnt(exception { $app->fetch('handle') }, undef, '... cannot call fetch on a parameterized container'); isnt(exception { $app->resolve( service => 'handle') }, undef, '... cannot call resolve on a parameterized container'); my $simple_app = $app->create( Logger => $simple_logger ); isa_ok($simple_app, 'Bread::Board::Container'); isa_ok($simple_app->resolve( service => 'app' )->log_handle, 'My::Simple::Logger'); my $db_app = $app->create( Logger => $db_logger->create( DBConnInfo => $db_conn_info ) ); isa_ok($db_app, 'Bread::Board::Container'); isa_ok($db_app->resolve( service => 'app' )->log_handle, 'My::Database::Logger'); done_testing; Bread-Board-0.37/t/00-report-prereqs.t0000644000175000017500000001342613505520232016665 0ustar yanickyanick#!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.027 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if $mod eq 'perl'; next if grep { $_ eq $mod } @exclude; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass; # vim: ts=4 sts=4 sw=4 et: Bread-Board-0.37/t/00-report-prereqs.dd0000644000175000017500000000432013505520232017002 0ustar yanickyanickdo { my $x = { 'configure' => { 'requires' => { 'ExtUtils::MakeMaker' => '0' } }, 'develop' => { 'requires' => { 'Test::More' => '0.96', 'Test::Vars' => '0' } }, 'runtime' => { 'requires' => { 'Carp' => '0', 'Module::Runtime' => '0', 'Moose' => '0', 'Moose::Exporter' => '2.1200', 'Moose::Role' => '0', 'Moose::Util' => '0', 'Moose::Util::TypeConstraints' => '0', 'MooseX::Clone' => '0.05', 'MooseX::Params::Validate' => '0.14', 'Scalar::Util' => '0', 'Try::Tiny' => '0', 'overload' => '0', 'strict' => '0', 'warnings' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'ExtUtils::MakeMaker' => '0', 'File::Spec' => '0', 'FindBin' => '0', 'IO::Handle' => '0', 'IPC::Open3' => '0', 'Test::Fatal' => '0', 'Test::Moose' => '0', 'Test::More' => '0', 'Test::Requires' => '0', 'perl' => '5.006' } } }; $x; }Bread-Board-0.37/t/023_sugar.t0000644000175000017500000000231513505520232015156 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } my $c = container 'MyApp' => as { service 'log_file' => "logfile.log"; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => [ depends_on('log_file'), ] ); service 'application' => ( class => 'MyApplication', dependencies => [ depends_on('logger'), ] ); }; my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); done_testing; Bread-Board-0.37/t/073_typemap_with_role_infer.t0000644000175000017500000000277013505520232020765 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package Bar::Role; use Moose::Role; package My::Bar; use Moose; with 'Bar::Role'; package My::Foo; use Moose; has 'bar' => ( is => 'ro', does => 'Bar::Role', required => 1, ); } { my $c = container 'MyTestContainer' => as { typemap 'Bar::Role' => infer( class => 'My::Bar' ); typemap 'My::Foo' => infer; }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); does_ok( $c->get_type_mapping_for('My::Foo'), 'Bread::Board::Service' ); ok($c->has_type_mapping_for('Bar::Role'), '... we do have a type mapping for Bar::Role'); does_ok( $c->get_type_mapping_for('Bar::Role'), 'Bread::Board::Service' ); is( $c->get_type_mapping_for('My::Foo')->get_dependency('bar')->service, $c->get_type_mapping_for('Bar::Role'), '... the Bar::Role dependency for My::Foo is the same as in the type map' ); { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); isa_ok($foo->bar, 'My::Bar'); does_ok($foo->bar, 'Bar::Role'); } } { eval { container 'MyTestContainer' => as { typemap 'My::Foo' => infer; } }; my $e = $@; like($e, qr/We can only infer Moose classes\, Bar\:\:Role is a role/, '... needs the param'); } done_testing; Bread-Board-0.37/t/045_parameters_in_dependency.t0000644000175000017500000000312313505520232021066 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package Serializer; use Moose; has 'format' => ( is => 'ro', isa => 'Str' ); package Application; use Moose; has 'json' => ( is => 'ro', isa => 'Serializer' ); } { my $c = container 'Test' => as { service 'Serializer' => ( class => 'Serializer', parameters => { 'format' => { isa => 'Str' } } ); service 'App' => ( class => 'Application', dependencies => { json => Bread::Board::Dependency->new( service_path => 'Serializer', service_params => { 'format' => 'JSON' } ) } ); }; my $app = $c->resolve( service => 'App' ); isa_ok($app, 'Application'); isa_ok($app->json, 'Serializer'); is($app->json->format, 'JSON', '... got the right format'); } { my $c = container 'Test' => as { service 'Serializer' => ( class => 'Serializer', parameters => { 'format' => { isa => 'Str' } } ); service 'App' => ( class => 'Application', dependencies => { json => { 'Serializer' => { 'format' => 'JSON' } } } ); }; my $app = $c->resolve( service => 'App' ); isa_ok($app, 'Application'); isa_ok($app->json, 'Serializer'); is($app->json->format, 'JSON', '... got the right format'); } done_testing; Bread-Board-0.37/t/031_custom_lifecycles.t0000644000175000017500000000067213505520232017554 0ustar yanickyanick#!/usr/bin/env perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board::ConstructorInjection; { package MyLifeCycle; use Moose::Role; with 'Bread::Board::LifeCycle::Singleton'; } { package MyClass; use Moose; } my $s = Bread::Board::ConstructorInjection->new( lifecycle => '+MyLifeCycle', name => 'foo', class => 'MyClass', ); does_ok($s, 'MyLifeCycle'); done_testing; Bread-Board-0.37/t/024_sugar.t0000644000175000017500000000523513505520232015163 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package DBI; use Moose; has 'dsn' => (is => 'ro', isa => 'Str'); has 'username' => (is => 'ro', isa => 'Str'); has 'password' => (is => 'ro', isa => 'Str'); sub connect { my ($class, $dsn, $username, $password) = @_; $class->new(dsn => $dsn, username => $username, password => $password); } package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); has 'dbh' => (is => 'ro', isa => 'DBI', required => 1); } my $c = container 'MyApp' => as { service 'log_file' => "logfile.log"; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => [ depends_on('log_file'), ] ); container 'Database' => as { service 'dsn' => "dbi:sqlite:dbname=my-app.db"; service 'username' => "user"; service 'password' => "pass"; service 'host' => undef; service 'dbh' => ( block => sub { my $s = shift; DBI->connect( $s->param('dsn'), $s->param('username'), $s->param('password'), ) || die "Could not connect"; }, dependencies => wire_names(qw[dsn username password]) ); }; service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), dbh => depends_on('Database/dbh'), } ); }; my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); is($c->resolve( service => 'Database/host'), undef, '... service where value is undef'); my $dbh = $c->resolve( service => 'Database/dbh' ); isa_ok($dbh, 'DBI'); is($dbh->dsn, "dbi:sqlite:dbname=my-app.db", '... got the right dsn'); is($dbh->username, "user", '... got the right username'); is($dbh->password, "pass", '... got the right password'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); isa_ok($app->dbh, 'DBI'); isnt($app->dbh, $dbh, '... got a different dbh'); done_testing; Bread-Board-0.37/t/076_more_complex_typemap.t0000644000175000017500000000762613505520232020307 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package Stapler; use Moose; package Desk; use Moose; package Chair; use Moose; package Cubicle; use Moose; has 'desk' => ( is => 'ro', isa => 'Desk', required => 1, ); has 'chair' => ( is => 'ro', isa => 'Chair', required => 1, ); package KeyCard; use Moose; use Moose::Util::TypeConstraints; subtype 'KeyCardUUID' => as 'Str'; has 'uuid' => ( is => 'ro', isa => 'KeyCardUUID', required => 1, ); package Employee; use Moose; has [ 'first_name', 'last_name' ] => ( is => 'ro', isa => 'Str', required => 1, ); has 'stapler' => ( is => 'ro', isa => 'Stapler', predicate => 'has_stapler' ); has 'keycard' => ( is => 'ro', isa => 'KeyCard', required => 1, ); has 'work_area' => ( is => 'ro', isa => 'Cubicle', required => 1, ); } my $UUID = 0; my $c = container 'Initech' => as { service 'keycard_uuid_generator' => ( block => sub { ++$UUID } ); typemap 'KeyCardUUID' => 'keycard_uuid_generator'; typemap 'Employee' => infer; }; my $micheal = $c->resolve( type => 'Employee', parameters => { first_name => 'Micheal', last_name => 'Bolton' } ); my $samir = $c->resolve( type => 'Employee', parameters => { first_name => 'Samir', last_name => 'Nagheenanajar' } ); isa_ok($micheal, 'Employee'); is($micheal->first_name, 'Micheal', '... got the right first name'); is($micheal->last_name, 'Bolton', '... got the right last name'); isa_ok($micheal->work_area, 'Cubicle'); isa_ok($micheal->work_area->desk, 'Desk'); isa_ok($micheal->work_area->chair, 'Chair'); ok(!$micheal->has_stapler, '... Micheal doesnt have a stapler'); isa_ok($samir, 'Employee'); is($samir->first_name, 'Samir', '... got the right first name'); is($samir->last_name, 'Nagheenanajar', '... got the right last name'); isa_ok($samir->work_area, 'Cubicle'); isa_ok($samir->work_area->desk, 'Desk'); isa_ok($samir->work_area->chair, 'Chair'); ok(!$samir->has_stapler, '... Samir doesnt have a stapler'); isnt($micheal, $samir, '... two different employees'); isnt($micheal->work_area, $samir->work_area, '... two different work_areas'); isnt($micheal->work_area->chair, $samir->work_area->chair, '... two different work_area chairs'); isnt($micheal->work_area->desk, $samir->work_area->desk, '... two different work_area desks'); isnt($micheal->keycard, $samir->keycard, '... two different keycards'); isnt($micheal->keycard->uuid, $samir->keycard->uuid, '... two different keycard uuids'); my $milton = $c->resolve( type => 'Employee', parameters => { first_name => 'Milton', last_name => 'Waddams', stapler => Stapler->new } ); isa_ok($milton, 'Employee'); is($milton->first_name, 'Milton', '... got the right first name'); is($milton->last_name, 'Waddams', '... got the right last name'); isa_ok($milton->work_area, 'Cubicle'); isa_ok($milton->work_area->desk, 'Desk'); isa_ok($milton->work_area->chair, 'Chair'); ok($milton->has_stapler, '... Milton does have a stapler'); foreach ( $micheal, $samir ) { isnt($milton, $_, '... two different employees'); isnt($milton->work_area, $_->work_area, '... two different work_areas'); isnt($milton->work_area->chair, $_->work_area->chair, '... two different work_area chairs'); isnt($milton->work_area->desk, $_->work_area->desk, '... two different work_area desks'); isnt($milton->keycard, $_->keycard, '... two different keycards'); isnt($milton->keycard->uuid, $_->keycard->uuid, '... two different keycard uuids'); } done_testing; Bread-Board-0.37/t/021_sugar.t0000644000175000017500000000242413505520232015155 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } sub loggers { service 'log_file' => "logfile.log"; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => { log_file => depends_on('log_file'), } ); } my $c = container 'MyApp' => as { loggers(); # reuse baby !!! service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); }; my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); done_testing; Bread-Board-0.37/t/078_complex_typemap_w_error.t0000644000175000017500000000225713505520232021021 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Test::Moose; use Bread::Board; { package Desk; use Moose; # this cannot be handled # so this will cause the # inference to die has 'stapler' => ( is => 'ro', isa => 'Any', required => 1, ); package Chair; use Moose; package Cubicle; use Moose; has 'desk' => ( is => 'ro', isa => 'Desk', required => 1, ); has 'chair' => ( is => 'ro', isa => 'Chair', required => 1, ); package Employee; use Moose; has [ 'first_name', 'last_name' ] => ( is => 'ro', isa => 'Str', required => 1, ); has 'work_area' => ( is => 'ro', isa => 'Cubicle', required => 1, ); } like(exception { container 'Initech' => as { typemap 'Employee' => infer; }; }, qr/Only class types\, role types\, or subtypes of Object can be inferred\. I don\'t know what to do with type \(Any\)/, '... cannot infer a non typemapped item below the first level'); done_testing; Bread-Board-0.37/t/00-compile.t0000644000175000017500000000372213505520232015321 0ustar yanickyanickuse 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.052 use Test::More; plan tests => 24 + ($ENV{AUTHOR_TESTING} ? 1 : 0); my @module_files = ( 'Bread/Board.pm', 'Bread/Board/BlockInjection.pm', 'Bread/Board/ConstructorInjection.pm', 'Bread/Board/Container.pm', 'Bread/Board/Container/FromParameterized.pm', 'Bread/Board/Container/Parameterized.pm', 'Bread/Board/Dependency.pm', 'Bread/Board/Dumper.pm', 'Bread/Board/LifeCycle.pm', 'Bread/Board/LifeCycle/Singleton.pm', 'Bread/Board/LifeCycle/Singleton/WithParameters.pm', 'Bread/Board/Literal.pm', 'Bread/Board/Service.pm', 'Bread/Board/Service/Alias.pm', 'Bread/Board/Service/Deferred.pm', 'Bread/Board/Service/Deferred/Thunk.pm', 'Bread/Board/Service/Inferred.pm', 'Bread/Board/Service/WithClass.pm', 'Bread/Board/Service/WithConstructor.pm', 'Bread/Board/Service/WithDependencies.pm', 'Bread/Board/Service/WithParameters.pm', 'Bread/Board/SetterInjection.pm', 'Bread/Board/Traversable.pm', 'Bread/Board/Types.pm' ); # no fake home requested my $inc_switch = -d 'blib' ? '-Mblib' : '-Ilib'; use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L my $stderr = IO::Handle->new; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, $inc_switch, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ) if $ENV{AUTHOR_TESTING}; Bread-Board-0.37/t/044_deferred_parameters.t0000644000175000017500000000230413505520232020041 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package User; use Moose; has 'name' => ( is => 'ro', isa => 'Str' ); package Page; use Moose; has 'user' => ( is => 'ro', isa => 'User' ); } my $c = container 'Views' => as { service 'User' => ( block => sub { my $s = shift; '

' . $s->param('user')->name . '

'; }, parameters => { user => { isa => 'User' } } ); service 'Page' => ( block => sub { my $s = shift; '' . '' . $s->param('user_view')->inflate( user => $s->param('page')->user ) . '' . ''; }, dependencies => { user_view => depends_on('User') }, parameters => { page => { isa => 'Page' } } ); }; my $view = $c->fetch('Page')->get( page => Page->new( user => User->new( name => 'Stevan' ) ) ); is( $view, '

Stevan

', '... got the correct result' ); done_testing; Bread-Board-0.37/t/153_sugar_container_inheritance.t0000644000175000017500000000634313505520232021602 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { my $c = container Foo => as { container Bar => as { service baz => 21; }; container Moo => ['Bar'] => as { service kooh => ( block => sub { my ($s) = @_; $s->param('baz') * 2; }, dependencies => { baz => depends_on('Bar/baz'), }, ); }; }; container $c => as { container '+Bar' => as { service bif => 123; }; container '+Moo' => as { service boo => ( block => sub { my ($s) = @_; $s->param('a') + $s->param('b'); }, dependencies => { a => depends_on('kooh'), b => depends_on('Bar/bif'), }, ); }; }; is $c->resolve(service => 'Bar/baz'), 21, 'can resolve Bar/Baz from container'; is $c->resolve(service => 'Bar/bif'), 123, 'can resolve Bar/bif from container'; my $p = $c->fetch('Moo')->create(Bar => $c->fetch('Bar')); is $p->resolve(service => 'kooh'), 42, 'can resolve kooh from parameterized container'; is $p->resolve(service => 'boo'), 165, 'can resolve boo from parameterized container'; like exception { container '+Foo' => as {}; }, qr/^Inheriting containers isn't possible outside of the context of a container/, 'exception thrown when trying to inherit +Foo outside of container context'; like exception { container $c => as { container '+Buf' => as {}; }; }, qr/^Could not find container or service for Buf in Foo/, 'exception thrown when trying to inherit +Buf and it does not exist'; like exception { container $c => as { container '+Buf' => ['Moo'] => as {}; }; }, qr/^Declaring container parameters when inheriting is not supported/, 'exception thrown when trying to declare container parameters when inheriting'; } { { package Thing; use Moose; has bar => (is => 'ro', required => 1); no Moose; } { package TestThing; use Moose; extends 'Thing'; no Moose; } my $c = container Foo => as { service bar => 42; container Moo => as { container Kooh => as { service boo => ( class => 'Thing', dependencies => { bar => '../../bar', }, ); }; }; }; isa_ok $c->resolve(service => 'Moo/Kooh/boo'), 'Thing', 'can resolve Moo/Kooh/boo and get Thing'; is $c->resolve(service => 'Moo/Kooh/boo')->bar, 42, '... and can call bar method on it'; container $c => as { container '+Moo/Kooh' => as { service '+boo' => (class => 'TestThing'); }; }; isa_ok $c->resolve(service => 'Moo/Kooh/boo'), 'TestThing', 'can resolve Moo/Kooh/boo and get TestThing'; is $c->resolve(service => 'Moo/Kooh/boo')->bar, 42, '... and can call bar method on it'; } done_testing; Bread-Board-0.37/t/202_form_sensible_example.t0000644000175000017500000000726013505520232020402 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; BEGIN { plan skip_all => 'Form::Sensible is broken at the moment' } use Test::Requires { 'Form::Sensible' => '0.11220' }; use Bread::Board; { package My::Model; use Moose; sub get_all_access_levels { return ( { id => 'standard', name => 'Standard User' }, { id => 'admin', name => 'Administrator' }, { id => 'super', name => 'Super User' }, ) } } my $FormBuilder = container 'FormBuilder' => [ 'Fields' ] => as { service 'Form' => ( class => 'Form::Sensible', block => sub { my $s = shift; my $c = $s->parent; my $fields = $c->get_sub_container('Fields'); my $form = Form::Sensible::Form->new( name => $s->param('name') ); foreach my $name ( $fields->get_service_list ) { $form->add_field( $fields->get_service( $name )->get ); } if ( my $state = $s->param('state') ) { $form->set_values( $state ); } $form; }, parameters => { name => { isa => 'Str' }, state => { isa => 'HashRef', optional => 1 }, } ); }; my $Fields = container 'Fields' => [ 'Model' ] => as { service 'Username' => ( class => 'Form::Sensible::Field::Text', block => sub { Form::Sensible::Field::Text->new( name => 'username', validation => { regex => qr/^[0-9a-z]*$/ } ); } ); service 'Password' => ( class => 'Form::Sensible::Field::Text', block => sub { Form::Sensible::Field::Text->new( name => 'password', render_hints => { 'HTML' => { field_type => 'password' } } ); } ); service 'Submit' => ( class => 'Form::Sensible::Field::Trigger', block => sub { Form::Sensible::Field::Trigger->new( name => 'submit' ); } ); service 'AccessLevel' => ( class => 'Form::Sensible::Field::Select', block => sub { my $s = shift; my $select = Form::Sensible::Field::Select->new( name => 'access_level', ); foreach my $access_level ( $s->param('schema')->get_all_access_levels ) { $select->add_option( $access_level->{id}, $access_level->{name} ); } $select; }, dependencies => { schema => depends_on('Model/schema') , }, ); }; # this would actually wrap the # $c->model('DBIC') or something # in order to get the DBIC schema # object my $Model = container 'Model' => as { service 'schema' => My::Model->new }; # perhaps create this in a early part # of a catalyst dispatch chain my $Form = $FormBuilder->create( Fields => $Fields->create( Model => $Model ) ); # then in the actual action code # you would create the form instance # and pass the state (which is # basically $c->req->parameters) my $f = $Form->resolve( service => 'Form', parameters => { name => 'test', state => { username => 'stevan', password => '****', access_level => [ 'admin' ] } } ); isa_ok($f, 'Form::Sensible::Form'); my $result = $f->validate; ok( $result->is_valid, '... our form validated' ); done_testing; Bread-Board-0.37/t/012_container_with_shared_deps.t0000644000175000017500000000305313505520232021411 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board::Container; use Bread::Board::ConstructorInjection; use Bread::Board::BlockInjection; use Bread::Board::Literal; { package DBH; use Moose; has ['dsn', 'user', 'pass'] => (is => 'ro', required => 1); } my $c = Bread::Board::Container->new( name => 'Model', services => [ Bread::Board::ConstructorInjection->new( name => 'schema', class => 'My::App::Schema', dependencies => { dsn => Bread::Board::Literal->new(name => 'dsn', value => ''), user => Bread::Board::Literal->new(name => 'user', value => ''), pass => Bread::Board::Literal->new(name => 'pass', value => ''), }, ), Bread::Board::BlockInjection->new( name => 'dbh', block => sub { my $s = shift; DBH->new( dsn => $s->param('dsn'), user => $s->param('user'), pass => $s->param('pass'), ) }, dependencies => { dsn => Bread::Board::Dependency->new(service_path => 'schema/dsn'), user => Bread::Board::Dependency->new(service_path => 'schema/user'), pass => Bread::Board::Dependency->new(service_path => 'schema/pass'), }, ) ] ); my $s = $c->fetch('dbh'); does_ok($s, 'Bread::Board::Service'); my $dbh = $s->get; isa_ok($dbh, 'DBH'); done_testing; Bread-Board-0.37/t/010_container.t0000644000175000017500000000613013505520232016012 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board::Container; use Bread::Board::ConstructorInjection; use Bread::Board::Literal; my $c = Bread::Board::Container->new(name => '/'); isa_ok($c, 'Bread::Board::Container'); $c->add_sub_container( Bread::Board::Container->new( name => 'Application', sub_containers => [ Bread::Board::Container->new( name => 'Model', services => [ Bread::Board::Literal->new(name => 'dsn', value => ''), Bread::Board::ConstructorInjection->new( name => 'schema', class => 'My::App::Schema', dependencies => { dsn => Bread::Board::Dependency->new(service_path => '../dsn'), user => Bread::Board::Literal->new(name => 'user', value => ''), pass => Bread::Board::Literal->new(name => 'pass', value => ''), }, ) ] ), Bread::Board::Container->new( name => 'View', services => [ Bread::Board::ConstructorInjection->new( name => 'TT', class => 'My::App::View::TT', dependencies => { tt_include_path => Bread::Board::Literal->new(name => 'include_path', value => []), }, ) ] ), Bread::Board::Container->new(name => 'Controller'), ] ) ); my $app = $c->get_sub_container('Application'); isa_ok($app, 'Bread::Board::Container'); is($app->name, 'Application', '... got the right container'); { my $controller = $app->get_sub_container('Controller'); isa_ok($controller, 'Bread::Board::Container'); is($controller->name, 'Controller', '... got the right container'); is($controller->parent, $app, '... app is the parent of the controller'); ok(!$controller->has_services, '... the controller has no services'); } { my $view = $app->get_sub_container('View'); isa_ok($view, 'Bread::Board::Container'); is($view->name, 'View', '... got the right container'); is($view->parent, $app, '... app is the parent of the view'); ok($view->has_services, '... the veiw has services'); my $service = $view->get_service('TT'); does_ok($service, 'Bread::Board::Service'); is($service->parent, $view, '... the parent of the service is the view'); } { my $model = $app->get_sub_container('Model'); isa_ok($model, 'Bread::Board::Container'); is($model->name, 'Model', '... got the right container'); is($model->parent, $app, '... app is the parent of the model'); ok($model->has_services, '... the model has services'); my $service = $model->get_service('schema'); does_ok($service, 'Bread::Board::Service'); is($service->parent, $model, '... the parent of the service is the model'); } done_testing; Bread-Board-0.37/t/101_clone_w_setter_injection.t0000644000175000017500000000411413505520232021107 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Scalar::Util qw(refaddr); use Bread::Board; { package Test::Class; use Moose; has 'dep' => ( is => 'rw', isa => 'Int' ); } my $board = Bread::Board::Container->new( name => 'app' ); isa_ok($board, 'Bread::Board::Container'); $board->add_service( Bread::Board::SetterInjection->new( name => 'test', class => 'Test::Class', dependencies => { dep => Bread::Board::Dependency->new(service_path => '/dep'), }, ) ); ok($board->has_service('test'), '... got the test service'); isa_ok($board->get_service('test'), 'Bread::Board::SetterInjection'); # clone ... my $board2 = $board->clone; isa_ok($board2, 'Bread::Board::Container'); isnt($board, $board2, '... they are not the same instance'); ok($board2->has_service('test'), '... got the test service'); isa_ok($board2->get_service('test'), 'Bread::Board::SetterInjection'); isnt($board->get_service('test'), $board2->get_service('test'), '... not the same test services'); # add dep services ... $board->add_service( Bread::Board::Literal->new(name => 'dep', value => 1) ); ok($board->has_service('dep'), '... got the dep service'); isa_ok($board->get_service('dep'), 'Bread::Board::Literal'); ok(!$board2->has_service('dep'), '... board2 does not have the dep service'); $board2->add_service( Bread::Board::Literal->new(name => 'dep', value => 2) ); ok($board2->has_service('dep'), '... got the dep service'); isa_ok($board2->get_service('dep'), 'Bread::Board::Literal'); isnt($board->get_service('dep'), $board2->get_service('dep'), '... not the same dep services'); # test them ... is($board->fetch('/dep')->get(), 1, '... got correct dep'); is($board->fetch('/test')->get()->dep, 1, '... test uses dep'); is(refaddr $board->fetch('/test')->parent, refaddr $board, '... got the right board'); is($board2->fetch('/dep')->get(), 2, '... got correct dep'); is($board2->fetch('/test')->get()->dep, 2, '... test uses dep'); is(refaddr $board2->fetch('/test')->parent, refaddr $board2, '... got the right board'); done_testing; Bread-Board-0.37/t/027_sugar_w_include.t0000644000175000017500000000324713505520232017220 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use FindBin; use Test::More; use Test::Fatal; use Bread::Board; like(exception { local $SIG{__WARN__} = sub { }; include "$FindBin::Bin/lib/bad.bb" }, qr/Couldn't compile.*bad\.bb.*syntax error.*function_doesnt_exist/, "we get appropriate errors for invalid files"); like(exception { include "$FindBin::Bin/lib/doesnt_exist.bb" }, qr/Couldn't open.*doesnt_exist\.bb.*for reading/, "we get appropriate errors for files that don't exist"); like(exception { include "$FindBin::Bin/lib/false.bb" }, qr/false\.bb.*doesn't return a true value/, "we get appropriate errors for files that evaluate to false"); { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } my $c = container 'MyApp' => as { service 'log_file' => "logfile.log"; include "$FindBin::Bin/lib/logger.bb"; service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); }; my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); done_testing; Bread-Board-0.37/t/042_parameter_cache_with_singleton.t0000644000175000017500000000501613505520232022257 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; use Bread::Board::LifeCycle::Singleton::WithParameters; { package Foo; use Moose; has 'bar' => (is => 'ro', isa => 'Int', required => 1); has 'baz' => (is => 'ro', isa => 'Str', required => 1); } my $c = container 'MyApp' => as { service 'foo' => ( lifecycle => 'Singleton::WithParameters', class => 'Foo', parameters => { bar => { isa => 'Int' }, baz => { isa => 'Str' }, } ); }; my $foo; is(exception { $foo = $c->resolve( service => 'foo', parameters => { bar => 10, baz => 'BAZ' } ); }, undef, '... got the service correctly'); isa_ok($foo, 'Foo'); is($foo->bar, 10, '... got the right parameter value'); is($foo->baz, 'BAZ', '... got the right parameter value'); # this is the same instance ... my $foo2; is(exception { $foo2 = $c->resolve( service => 'foo', parameters => { bar => 10, baz => 'BAZ' } ); }, undef, '... got the service correctly'); isa_ok($foo2, 'Foo'); is($foo2->bar, 10, '... got the right parameter value'); is($foo2->baz, 'BAZ', '... got the right parameter value'); # this will be different instance ... my $foo3; is(exception { $foo3 = $c->resolve( service => 'foo', parameters => { bar => 20, baz => 'BAZ' } ); }, undef, '... got the service correctly'); isa_ok($foo3, 'Foo'); is($foo3->bar, 20, '... got the right parameter value'); is($foo3->baz, 'BAZ', '... got the right parameter value'); # this is the same instance ... my $foo4; is(exception { $foo4 = $c->resolve( service => 'foo', parameters => { bar => 10, baz => 'BAZ' } ); }, undef, '... got the service correctly'); isa_ok($foo4, 'Foo'); is($foo4->bar, 10, '... got the right parameter value'); is($foo4->baz, 'BAZ', '... got the right parameter value'); # this will be different instance ... my $foo5; is(exception { $foo5 = $c->resolve( service => 'foo', parameters => { bar => 10, baz => 'Baz' }); }, undef, '... got the service correctly'); isa_ok($foo5, 'Foo'); is($foo5->bar, 10, '... got the right parameter value'); is($foo5->baz, 'Baz', '... got the right parameter value'); # confirm our assumptions ... is($foo, $foo2, '... they are the same instances (same params)'); isnt($foo, $foo3, '... they are not the same instances (diff params)'); is($foo, $foo4, '... they are the same instances (same params)'); isnt($foo, $foo5, '... they are the same instances (same params)'); isnt($foo3, $foo5, '... they are the same instances (same params)'); done_testing; Bread-Board-0.37/t/001_constructor_injection.t0000644000175000017500000000606113505520232020462 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Test::Fatal; use Bread::Board::ConstructorInjection; use Bread::Board::Literal; { package Needle; use Moose; package Mexican::Black::Tar; use Moose; package Addict; use Moose; sub shoot_up_good { shift->new(@_, overdose => 1) } has 'needle' => (is => 'ro'); has 'spoon' => (is => 'ro'); has 'stash' => (is => 'ro'); has 'overdose' => (is => 'ro', isa => 'Bool', default => 0); } my $s = Bread::Board::ConstructorInjection->new( name => 'William', class => 'Addict', dependencies => { needle => Bread::Board::ConstructorInjection->new(name => 'spike', class => 'Needle'), spoon => Bread::Board::Literal->new(name => 'works', value => 'Spoon!'), }, parameters => { stash => { isa => 'Mexican::Black::Tar' } } ); isa_ok($s, 'Bread::Board::ConstructorInjection'); does_ok($s, 'Bread::Board::Service::WithClass'); does_ok($s, 'Bread::Board::Service::WithDependencies'); does_ok($s, 'Bread::Board::Service::WithParameters'); does_ok($s, 'Bread::Board::Service'); { my $i = $s->get(stash => Mexican::Black::Tar->new); isa_ok($i, 'Addict'); isa_ok($i->needle, 'Needle'); is($i->spoon, 'Spoon!', '... got our literal service'); isa_ok($i->stash, 'Mexican::Black::Tar'); ok ! $i->overdose, 'Normal constructor'; { my $i2 = $s->get(stash => Mexican::Black::Tar->new); isnt($i, $i2, '... calling it again returns an new object'); } } $s->constructor_name('shoot_up_good'); { my $i = $s->get(stash => Mexican::Black::Tar->new); isa_ok($i, 'Addict'); ok $i->overdose, 'Alternate constructor called'; } is($s->name, 'William', '... got the right name'); is($s->class, 'Addict', '... got the right class'); my $deps = $s->dependencies; is_deeply([ sort keys %$deps ], [qw/needle spoon/], '... got the right dependency keys'); my $needle = $s->get_dependency('needle'); isa_ok($needle, 'Bread::Board::Dependency'); isa_ok($needle->service, 'Bread::Board::ConstructorInjection'); is($needle->service->name, 'spike', '... got the right name'); is($needle->service->class, 'Needle', '... got the right class'); my $spoon = $s->get_dependency('spoon'); isa_ok($spoon, 'Bread::Board::Dependency'); isa_ok($spoon->service, 'Bread::Board::Literal'); is($spoon->service->name, 'works', '... got the right name'); is($spoon->service->value, 'Spoon!', '... got the right literal value'); my $params = $s->parameters; is_deeply([ sort keys %$params ], [qw/stash/], '... got the right paramter keys'); is_deeply($params->{stash}, { isa => 'Mexican::Black::Tar' }, '... got the right parameter spec'); # test some errors isnt(exception { $s->get; }, undef, '... you must supply the required parameters'); isnt(exception { $s->get(stash => []); }, undef, '... you must supply the required parameters as correct types'); isnt(exception { $s->get(stash => Mexican::Black::Tar->new, foo => 10); }, undef, '... you must supply the required parameters (and no more)'); done_testing; Bread-Board-0.37/t/302_path_traversal_deprecation.t0000644000175000017500000000102113505520232021422 0ustar yanickyanick#!/usr/bin/env perl use strict; use warnings; use Test::More; use Bread::Board; my $c = container 'Foo' => as { service bar => 'baz'; }; { my $warning; local $SIG{__WARN__} = sub { $warning = $_[0] }; my $baz = $c->resolve(service => '/Foo/bar'); is($baz, 'baz', 'resolving service path in deprecated way still works'); like($warning, qr/Traversing into the current container \(Foo\) is deprecated; you should remove the Foo component from the path/, '... but gives a nice warning'); } done_testing; Bread-Board-0.37/t/030_lifecycle_singleton.t0000644000175000017500000000625613505520232020064 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board::ConstructorInjection; use Bread::Board::LifeCycle::Singleton; use Bread::Board::Literal; { package Needle; use Moose; package Mexican::Black::Tar; use Moose; package Addict; use Moose; has 'needle' => (is => 'ro'); has 'spoon' => (is => 'ro'); has 'stash' => (is => 'ro'); } my $s = Bread::Board::ConstructorInjection->new( lifecycle => 'Singleton', name => 'William', class => 'Addict', dependencies => { needle => Bread::Board::ConstructorInjection->new(name => 'spike', class => 'Needle'), spoon => Bread::Board::Literal->new(name => 'works', value => 'Spoon!'), }, parameters => { stash => { isa => 'Mexican::Black::Tar' } } ); isa_ok($s, 'Bread::Board::ConstructorInjection'); does_ok($s, 'Bread::Board::Service::WithClass'); does_ok($s, 'Bread::Board::Service::WithDependencies'); does_ok($s, 'Bread::Board::Service::WithParameters'); does_ok($s, 'Bread::Board::Service'); does_ok($s, 'Bread::Board::LifeCycle::Singleton'); is($s->lifecycle, 'Singleton', '... got the right lifecycle'); ok(!$s->has_instance, '... we dont have an instance yet'); my $i = $s->get(stash => Mexican::Black::Tar->new); ok($s->has_instance, '... we do have an instance now'); isa_ok($i, 'Addict'); isa_ok($i->needle, 'Needle'); is($i->spoon, 'Spoon!', '... got our literal service'); isa_ok($i->stash, 'Mexican::Black::Tar'); { my $i2 = $s->get(stash => Mexican::Black::Tar->new); is($i, $i2, '... calling it again returns the same object'); } $s->flush_instance; { my $i2 = $s->get(stash => Mexican::Black::Tar->new); isnt($i, $i2, '... calling it again returns an new object'); { my $i2a = $s->get(stash => Mexican::Black::Tar->new); is($i2, $i2a, '... calling it again returns the same object'); } $s->lifecycle('Null'); ok(!$s->can('flush_instance'), '... we can no longer call flush_instance'); ok(!$s->can('instance'), '... we can no longer call instance'); ok(!$s->can('has_instance'), '... we can no longer call has_instance'); is($s->lifecycle, 'Null', '... got the right lifecycle'); { my $i2a = $s->get(stash => Mexican::Black::Tar->new); isnt($i2, $i2a, '... calling it again returns a new object'); { my $i2a1 = $s->get(stash => Mexican::Black::Tar->new); isnt($i2, $i2a1, '... calling it again returns a new object as before'); isnt($i2a, $i2a1, '... calling it again returns a new object'); } } } $s->lifecycle('Singleton'); ok($s->can('flush_instance'), '... we can no longer call flush_instance'); ok($s->can('instance'), '... we can no longer call instance'); ok($s->can('has_instance'), '... we can no longer call has_instance'); is($s->lifecycle, 'Singleton', '... got the right lifecycle'); { my $i2 = $s->get(stash => Mexican::Black::Tar->new); isnt($i, $i2, '... calling it again returns the same object'); { my $i2a = $s->get(stash => Mexican::Black::Tar->new); is($i2, $i2a, '... calling it again returns the same object'); } } done_testing; Bread-Board-0.37/t/102_clone_w_block_injection.t0000644000175000017500000000426513505520232020703 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Scalar::Util qw(refaddr); use Bread::Board; { package Test::Class; use Moose; has 'dep' => ( is => 'rw', isa => 'Int' ); } my $board = Bread::Board::Container->new( name => 'app' ); isa_ok($board, 'Bread::Board::Container'); $board->add_service( Bread::Board::BlockInjection->new( name => 'test', class => 'Test::Class', block => sub { my $s = shift; Test::Class->new(%{ $s->params }); }, dependencies => { dep => Bread::Board::Dependency->new(service_path => '/dep'), }, ) ); ok($board->has_service('test'), '... got the test service'); isa_ok($board->get_service('test'), 'Bread::Board::BlockInjection'); # clone ... my $board2 = $board->clone; isa_ok($board2, 'Bread::Board::Container'); isnt($board, $board2, '... they are not the same instance'); ok($board2->has_service('test'), '... got the test service'); isa_ok($board2->get_service('test'), 'Bread::Board::BlockInjection'); isnt($board->get_service('test'), $board2->get_service('test'), '... not the same test services'); # add dep services ... $board->add_service( Bread::Board::Literal->new(name => 'dep', value => 1) ); ok($board->has_service('dep'), '... got the dep service'); isa_ok($board->get_service('dep'), 'Bread::Board::Literal'); ok(!$board2->has_service('dep'), '... board2 does not have the dep service'); $board2->add_service( Bread::Board::Literal->new(name => 'dep', value => 2) ); ok($board2->has_service('dep'), '... got the dep service'); isa_ok($board2->get_service('dep'), 'Bread::Board::Literal'); isnt($board->get_service('dep'), $board2->get_service('dep'), '... not the same dep services'); # test them ... is($board->fetch('/dep')->get(), 1, '... got correct dep'); is($board->fetch('/test')->get()->dep, 1, '... test uses dep'); is(refaddr $board->fetch('/test')->parent, refaddr $board, '... got the right board'); is($board2->fetch('/dep')->get(), 2, '... got correct dep'); is($board2->fetch('/test')->get()->dep, 2, '... test uses dep'); is(refaddr $board2->fetch('/test')->parent, refaddr $board2, '... got the right board'); done_testing; Bread-Board-0.37/t/026_sugar_remove.t0000644000175000017500000000341313505520232016536 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; is(exception { container 'MyApp' => sub { "dummy" } }, undef, 'container sugar does not throw exception'); is(exception { as { "Dummy" } }, undef, 'as sugar does not throw exception'); is(exception { container 'MyApp' => as { service 'service1' => 'foo' }; }, undef, 'as service sugar does not throw exception'); is(exception { container 'MyApp' => as { service 'service1' => 'foo'; service 'service2' => ( block => sub { "dummy" }, dependencies => wire_names 'service1' ); } }, undef, 'container, service and wire_names sugar does not throw exception'); is(exception { container 'MyApp' => as { service 'service1' => 'foo'; service 'service2' => ( block => sub { "dummy" }, dependencies => { service1 => depends_on 'service1' } ); } }, undef, 'container, service, and depends_on sugar does not throw exception'); no Bread::Board; like(exception { container() }, qr/^Undefined subroutine &main::container called/, 'container function does not exist without Bread::Board'); like(exception { as() }, qr/^Undefined subroutine &main::as called/, 'as function does not exist without Bread::Board'); like(exception { service() }, qr/^Undefined subroutine &main::service called/, 'service function does not exist without Bread::Board'); like(exception { depends_on() }, qr/^Undefined subroutine &main::depends_on called/, 'depends_on function does not exist without Bread::Board'); like(exception { wire_names() }, qr/^Undefined subroutine &main::wire_names called/, 'wire_names function does not exist without Bread::Board'); done_testing; Bread-Board-0.37/t/075_complex_typemap_example.t0000644000175000017500000000401013505520232020757 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package Logger::Role; use Moose::Role; requires 'log'; package My::Logger; use Moose; with 'Logger::Role'; has 'level' => ( is => 'ro', isa => 'Str', default => 'warn' ); sub log {} package My::DBI; use Moose; has 'dsn' => (is => 'ro', isa => 'Str'); sub connect { my ($class, $dsn) = @_; $class->new( dsn => $dsn ); } package My::Application; use Moose; has 'logger' => (is => 'ro', does => 'Logger::Role', required => 1); has 'dbh' => (is => 'ro', isa => 'My::DBI', required => 1); } my $c = container 'Automat' => as { service 'dsn' => 'dbi:sqlite:test'; service 'dbh' => ( block => sub { my $s = shift; My::DBI->connect( $s->param( 'dsn' ) ); }, dependencies => [ depends_on('dsn') ] ); # map a type to a service implementation ... typemap 'My::DBI' => 'dbh'; # ask the container to infer a service, # but give it some hints .... typemap 'Logger::Role' => infer( class => 'My::Logger' ); # ask the container to infer the # entire service ... typemap 'My::Application' => infer; }; # check the inference from the top level Application ... { my $app = $c->resolve( type => 'My::Application' ); isa_ok($app, 'My::Application'); isa_ok($app->logger, 'My::Logger'); does_ok($app->logger, 'Logger::Role'); is($app->logger->level, 'warn', '... got the default level'); isa_ok($app->dbh, 'My::DBI'); is($app->dbh->dsn, 'dbi:sqlite:test', '... got the right DSN too'); } # check the inference from the logger object # and its optional 'level' parameter { my $logger = $c->resolve( type => 'Logger::Role', parameters => { level => 'debug' } ); isa_ok($logger, 'My::Logger'); does_ok($logger, 'Logger::Role'); is($logger->level, 'debug', '... got the custom level'); } done_testing; Bread-Board-0.37/t/002_setter_injection.t0000644000175000017500000000575013505520232017410 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Test::Fatal; use Bread::Board::SetterInjection; use Bread::Board::Literal; { package Needle; use Moose; package Mexican::Black::Tar; use Moose; package Addict; use Moose; sub shoot_up_good { shift->new(@_, overdose => 1) } has 'needle' => (is => 'rw'); has 'spoon' => (is => 'rw'); has 'stash' => (is => 'rw'); has 'overdose' => (is => 'ro', isa => 'Bool', default => 0); } my $s = Bread::Board::SetterInjection->new( name => 'William', class => 'Addict', dependencies => { needle => Bread::Board::SetterInjection->new(name => 'spike', class => 'Needle'), spoon => Bread::Board::Literal->new(name => 'works', value => 'Spoon!'), }, parameters => { stash => { isa => 'Mexican::Black::Tar' } } ); isa_ok($s, 'Bread::Board::SetterInjection'); does_ok($s, 'Bread::Board::Service::WithClass'); does_ok($s, 'Bread::Board::Service::WithDependencies'); does_ok($s, 'Bread::Board::Service::WithParameters'); does_ok($s, 'Bread::Board::Service'); { my $i = $s->get(stash => Mexican::Black::Tar->new); isa_ok($i, 'Addict'); isa_ok($i->needle, 'Needle'); is($i->spoon, 'Spoon!', '... got our literal service'); isa_ok($i->stash, 'Mexican::Black::Tar'); { my $i2 = $s->get(stash => Mexican::Black::Tar->new); isnt($i, $i2, '... calling it again returns an new object'); } } $s->constructor_name('shoot_up_good'); { my $i = $s->get(stash => Mexican::Black::Tar->new); isa_ok($i, 'Addict'); ok $i->overdose, 'Alternate constructor called'; } is($s->name, 'William', '... got the right name'); is($s->class, 'Addict', '... got the right class'); my $deps = $s->dependencies; is_deeply([ sort keys %$deps ], [qw/needle spoon/], '... got the right dependency keys'); my $needle = $s->get_dependency('needle'); isa_ok($needle, 'Bread::Board::Dependency'); isa_ok($needle->service, 'Bread::Board::SetterInjection'); is($needle->service->name, 'spike', '... got the right name'); is($needle->service->class, 'Needle', '... got the right class'); my $spoon = $s->get_dependency('spoon'); isa_ok($spoon, 'Bread::Board::Dependency'); isa_ok($spoon->service, 'Bread::Board::Literal'); is($spoon->service->name, 'works', '... got the right name'); is($spoon->service->value, 'Spoon!', '... got the right literal value'); my $params = $s->parameters; is_deeply([ sort keys %$params ], [qw/stash/], '... got the right paramter keys'); is_deeply($params->{stash}, { isa => 'Mexican::Black::Tar' }, '... got the right parameter spec'); ## some errors isnt(exception { $s->get }, undef, '... you must supply the required parameters'); isnt(exception { $s->get(stash => []) }, undef, '... you must supply the required parameters as correct types'); isnt(exception { $s->get(stash => Mexican::Black::Tar->new, foo => 10) }, undef, '... you must supply the required parameters (and no more)'); done_testing; Bread-Board-0.37/t/072_typemap_with_more_infer.t0000644000175000017500000000336613505520232020767 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package My::Bar; use Moose; package My::Foo; use Moose; has 'bar' => ( is => 'ro', isa => 'My::Bar', required => 1, ); } { my $c = container 'MyTestContainer' => as { typemap 'My::Bar' => infer; typemap 'My::Foo' => infer; }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); does_ok( $c->get_type_mapping_for('My::Foo'), 'Bread::Board::Service' ); ok($c->has_type_mapping_for('My::Bar'), '... we do not have a type mapping for My::Bar'); does_ok( $c->get_type_mapping_for('My::Bar'), 'Bread::Board::Service' ); is( $c->get_type_mapping_for('My::Foo')->get_dependency('bar')->service, $c->get_type_mapping_for('My::Bar'), '... the My::Bar dependency for My::Foo is the same as in the type map' ); { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); isa_ok($foo->bar, 'My::Bar'); } } # don't give infer enough information # and make it figure it out for itself # including inferring the embedded object { my $c = container 'MyTestContainer' => as { typemap 'My::Foo' => infer; }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); does_ok( $c->get_type_mapping_for('My::Foo'), 'Bread::Board::Service' ); ok(!$c->has_type_mapping_for('My::Bar'), '... we do not have a type mapping for My::Bar'); { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); isa_ok($foo->bar, 'My::Bar'); } } done_testing; Bread-Board-0.37/t/200_example_code.t0000644000175000017500000000471313505520232016463 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; use Bread::Board::Types; # roles use Bread::Board::Service; use Bread::Board::Service::WithClass; use Bread::Board::Service::WithDependencies; use Bread::Board::Service::WithParameters; # services use Bread::Board::ConstructorInjection; use Bread::Board::SetterInjection; use Bread::Board::BlockInjection; use Bread::Board::Literal; use Bread::Board::Container; use Bread::Board::Dependency; use Bread::Board::Traversable; use Bread::Board::LifeCycle::Singleton; { package MyLogger; use Moose; package MyDBI; use Moose; has 'dsn' => (is => 'ro', isa => 'Str'); has 'username' => (is => 'ro', isa => 'Str'); has 'password' => (is => 'ro', isa => 'Str'); sub connect { my ($class, $dsn, $username, $password) = @_; $class->new(dsn => $dsn, username => $username, password => $password); } package MyAuthenticator; use Moose; has 'dbh' => (is => 'ro', isa => 'MyDBI', required => 1); has 'logger' => (is => 'ro', isa => 'MyLogger', required => 1); } my $c; is(exception { $c = Bread::Board::Container->new( name => 'Application' ); $c->add_service( Bread::Board::BlockInjection->new( name => 'logger', block => sub { MyLogger->new() } ) ); $c->add_service( Bread::Board::BlockInjection->new( name => 'db_conn', block => sub { MyDBI->connect('dbi:mysql:test', '', '') } ) ); $c->add_service( Bread::Board::BlockInjection->new( name => 'authenticator', block => sub { my $service = shift; MyAuthenticator->new( dbh => $service->param('db_conn'), logger => $service->param('logger') ); }, dependencies => { db_conn => Bread::Board::Dependency->new(service_path => 'db_conn'), logger => Bread::Board::Dependency->new(service_path => 'logger'), } ) ); }, undef, '... container compiled successfully'); my $authenticator; is(exception { $authenticator = $c->resolve( service => 'authenticator' ) }, undef, '... and the container compiled correctly'); isa_ok($authenticator, 'MyAuthenticator'); isa_ok($authenticator->dbh, 'MyDBI'); isa_ok($authenticator->logger, 'MyLogger'); done_testing; Bread-Board-0.37/t/080_infer_subclasses.t0000644000175000017500000000235513505520232017376 0ustar yanickyanick#!/usr/bin/env perl use strict; use warnings; use Test::More; use Bread::Board; { package NonMoose; sub new { bless { data => $_[0] }, shift } } { package Foo; use Moose; has non_moose => ( is => 'ro', isa => 'NonMoose', required => 1, ); } { package Bar; use Moose; has foo => ( is => 'ro', isa => 'Foo', required => 1, ); } { my $c = container Stuff => as { service non_moose => NonMoose->new("foo"); service foo => ( class => 'Foo', dependencies => ['non_moose'], ); typemap 'Foo' => 'foo'; typemap 'Bar' => infer; }; my $bar = $c->resolve(type => 'Bar'); isa_ok($bar->foo->non_moose, 'NonMoose'); } { package Foo::Sub; use Moose; extends 'Foo'; } { my $c = container Stuff => as { service non_moose => NonMoose->new("foo"); service foo => ( class => 'Foo::Sub', dependencies => ['non_moose'], ); typemap 'Foo::Sub' => 'foo'; typemap 'Bar' => infer; }; my $bar = $c->resolve(type => 'Bar'); isa_ok($bar->foo->non_moose, 'NonMoose'); } done_testing; Bread-Board-0.37/t/011_container_path.t0000644000175000017500000000465213505520232017036 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board::Container; use Bread::Board::ConstructorInjection; use Bread::Board::Literal; my $c = Bread::Board::Container->new( name => 'Application', sub_containers => [ Bread::Board::Container->new( name => 'Model', services => [ Bread::Board::Literal->new(name => 'dsn', value => ''), Bread::Board::ConstructorInjection->new( name => 'schema', class => 'My::App::Schema', dependencies => { dsn => Bread::Board::Dependency->new(service_path => 'dsn'), user => Bread::Board::Literal->new(name => 'user', value => ''), pass => Bread::Board::Literal->new(name => 'pass', value => ''), }, ) ] ), Bread::Board::Container->new( name => 'View', services => [ Bread::Board::ConstructorInjection->new( name => 'TT', class => 'My::App::View::TT', dependencies => { tt_include_path => Bread::Board::Literal->new(name => 'include_path', value => []), }, ) ] ), Bread::Board::Container->new(name => 'Controller'), ] ); #use Bread::Board::Dumper; #diag(Bread::Board::Dumper->new->dump($c)); my $model = $c->fetch('Model'); isa_ok($model, 'Bread::Board::Container'); is($model->name, 'Model', '... got the right model'); { my $model2 = $c->fetch('/Model'); isa_ok($model2, 'Bread::Board::Container'); is($model, $model2, '... they are the same thing'); } my $dsn = $model->fetch('schema/dsn'); isa_ok($dsn, 'Bread::Board::Dependency'); is($dsn->service_path, 'dsn', '... got the right name'); { my $dsn2 = $c->fetch('/Model/schema/dsn'); isa_ok($dsn2, 'Bread::Board::Dependency'); is($dsn, $dsn2, '... they are the same thing'); } my $root = $model->fetch('../'); isa_ok($root, 'Bread::Board::Container'); is($root, $c, '... got the same container'); is($model, $model->fetch('../Model'), '... navigated back to myself'); is($dsn, $model->fetch('../Model/schema/dsn'), '... navigated to dsn'); is($model, $dsn->fetch('../Model'), '... got the model from the dsn'); done_testing; Bread-Board-0.37/t/025_sugar_w_absolute_path.t0000644000175000017500000000314013505520232020415 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; my $c = container 'Application' => as { container 'Model' => as { service 'dsn' => ''; service 'schema' => ( class => 'My::App::Schema', dependencies => { dsn => depends_on('dsn'), user => depends_on('user'), pass => depends_on('pass') } ); }; container 'View' => as { service 'TT' => ( class => 'My::App::View::TT', dependencies => { tt_include_path => depends_on('include_path') } ) }; container 'Controller'; }; my $model = $c->fetch('Model'); isa_ok($model, 'Bread::Board::Container'); is($model->name, 'Model', '... got the right model'); { my $model2 = $c->fetch('/Model'); isa_ok($model2, 'Bread::Board::Container'); is($model, $model2, '... they are the same thing'); } my $dsn = $model->fetch('schema/dsn'); isa_ok($dsn, 'Bread::Board::Dependency'); is($dsn->service_path, 'dsn', '... got the right name'); { my $dsn2 = $c->fetch('/Model/schema/dsn'); isa_ok($dsn2, 'Bread::Board::Dependency'); is($dsn, $dsn2, '... they are the same thing'); } my $root = $model->fetch('..'); isa_ok($root, 'Bread::Board::Container'); is($root, $c, '... got the same container'); is($model, $model->fetch('../Model'), '... navigated back to myself'); is($dsn, $model->fetch('../Model/schema/dsn'), '... navigated to dsn'); is($model, $dsn->fetch('../Model'), '... got the model from the dsn'); done_testing; Bread-Board-0.37/t/071_typemap_with_basic_infer.t0000644000175000017500000000443713505520232021105 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { package Foo::Role; use Moose::Role; package My::Foo; use Moose; with 'Foo::Role'; } # give infer() enough information to create # the service all by itself ... { my $c = container 'MyTestContainer' => as { typemap 'Foo::Role' => infer( class => 'My::Foo' ); }; ok($c->has_type_mapping_for('Foo::Role'), '... have a type mapping for Foo::Role'); does_ok( $c->get_type_mapping_for('Foo::Role'), 'Bread::Board::Service' ); { my $foo = $c->resolve( type => 'Foo::Role' ); isa_ok($foo, 'My::Foo'); } } # don't give infer enough information # and make it figure it out for itself { my $c = container 'MyTestContainer' => as { typemap 'My::Foo' => infer; }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); does_ok( $c->get_type_mapping_for('My::Foo'), 'Bread::Board::Service' ); { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); } } { my $c = container 'MyTestContainer' => as { typemap 'My::Foo' => infer( dependencies => { thing => service('thing' => 'THING') } ); }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); my $s = $c->get_type_mapping_for('My::Foo'); does_ok($s, 'Bread::Board::Service'); ok($s->has_dependency('thing'), "service_args were passed along"); { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); } } { package My::ConstructorInjection; use Moose; extends 'Bread::Board::ConstructorInjection'; } { my $c = container 'MyTestContainer' => as { typemap 'My::Foo' => infer( My::ConstructorInjection->new( name => 'foo', class => 'My::Foo', ) ); }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); my $s = $c->get_type_mapping_for('My::Foo'); does_ok($s, 'Bread::Board::Service'); isa_ok($s, 'My::ConstructorInjection'); { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); } } done_testing; Bread-Board-0.37/t/051_more_parameterized_containers.t0000644000175000017500000000732313505520232022145 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package My::Form; use Moose; has 'fields' => ( is => 'ro', isa => 'ArrayRef[My::Form::Field]', required => 1 ); has 'state' => ( is => 'ro', isa => 'HashRef', required => 1, ); package My::Form::Field; use Moose; has 'name' => ( is => 'ro', isa => 'Str', required => 1, ); package My::Form::Field::Text; use Moose; extends 'My::Form::Field'; has 'validations' => ( is => 'ro', isa => 'RegexpRef', required => 1, ); package My::Form::Field::Select; use Moose; extends 'My::Form::Field'; has 'options' => ( is => 'ro', isa => 'ArrayRef[HashRef]', required => 1, ); package My::Model; use Moose; sub get_all_states { return [ { value => 'CT', name => 'Connecticut' }, { value => 'CO', name => 'Colorado' }, { value => 'CA', name => 'California' }, ] } } my $FormBuilder = container 'Form' => [ 'Fields' ] => as { service 'form' => ( class => 'My::Form', block => sub { my $s = shift; my $c = $s->parent->get_sub_container('Fields'); return My::Form->new( state => $s->param('state'), fields => [ map { $c->fetch( $_ )->get; } reverse sort $c->get_service_list ] ); }, parameters => { state => { isa => 'HashRef' } } ); }; isa_ok($FormBuilder, 'Bread::Board::Container::Parameterized'); my $fields = container 'Fields' => [ 'Model' ] => as { service 'username' => ( class => 'My::Form::Field::Text', parameters => { name => { isa => 'Str', default => 'username' }, validations => { isa => 'RegexpRef', default => qr/^[a-zA-Z0-9_]*$/ }, } ); service 'states' => ( class => 'My::Form::Field::Select', block => sub { my $s = shift; My::Form::Field::Select->new( name => 'states', options => $s->param('schema')->get_all_states, ); }, dependencies => { schema => depends_on('Model/schema') , }, ); }; isa_ok($fields, 'Bread::Board::Container::Parameterized'); my $model = container 'Model' => as { service 'schema' => My::Model->new; }; isa_ok($model, 'Bread::Board::Container'); my $form = $FormBuilder->create( Fields => $fields->create( Model => $model ) ); isa_ok($form, 'Bread::Board::Container'); my $f = $form->resolve( service => 'form', parameters => { state => { username => 'stevan', state => 'CT' } } ); isa_ok($f, 'My::Form'); is_deeply( $f->state, { username => 'stevan', state => 'CT' }, '... got the right state' ); my $username = $f->fields->[0]; isa_ok($username, 'My::Form::Field::Text'); isa_ok($username, 'My::Form::Field'); is($username->name, 'username', '... got the right name'); ok(ref $username->validations eq 'Regexp', '... got the right validation'); my $states = $f->fields->[1]; isa_ok($states, 'My::Form::Field::Select'); isa_ok($states, 'My::Form::Field'); is($states->name, 'states', '... got the right name'); is_deeply( $states->options, [ { value => 'CT', name => 'Connecticut' }, { value => 'CO', name => 'Colorado' }, { value => 'CA', name => 'California' }, ], '... got the right option list' ); done_testing; Bread-Board-0.37/t/070_with_basic_typemap.t0000644000175000017500000000424413505520232017715 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package My::Foo; use Moose; } { # typemap directly getting a service object ... my $c = container 'MyTestContainer' => as { typemap 'My::Foo' => (service 'my_foo' => (class => 'My::Foo')); }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); is( $c->get_type_mapping_for('My::Foo'), $c->fetch('my_foo'), '... the type mapping for My::Foo is the my_foo service' ); { my $foo = $c->resolve( service => 'my_foo' ); isa_ok($foo, 'My::Foo'); } { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); } } { # typemap mapping to a service object name ... my $c = container 'MyTestContainer' => as { service 'my_foo' => (class => 'My::Foo'); typemap 'My::Foo' => 'my_foo'; }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); is( $c->get_type_mapping_for('My::Foo'), $c->fetch('my_foo'), '... the type mapping for My::Foo is the my_foo service' ); { my $foo = $c->resolve( service => 'my_foo' ); isa_ok($foo, 'My::Foo'); } { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); } } { # typemap mapping to a service object name # that is a path to a sub-container service my $c = container 'MyTestContainer' => as { container 'MyTestSubContainer' => as { service 'my_foo' => (class => 'My::Foo'); }; typemap 'My::Foo' => 'MyTestSubContainer/my_foo'; }; ok($c->has_type_mapping_for('My::Foo'), '... have a type mapping for My::Foo'); is( $c->get_type_mapping_for('My::Foo'), $c->fetch('MyTestSubContainer/my_foo'), '... the type mapping for My::Foo is the MyTestSubContainer/my_foo service' ); { my $foo = $c->resolve( service => 'MyTestSubContainer/my_foo' ); isa_ok($foo, 'My::Foo'); } { my $foo = $c->resolve( type => 'My::Foo' ); isa_ok($foo, 'My::Foo'); } } done_testing; Bread-Board-0.37/t/152_sugar_service_inheritance.t0000644000175000017500000000772213505520232021261 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { package Thing; use Moose; has foo => (is => 'ro', required => 1); has moo => (is => 'ro', required => 1); no Moose; __PACKAGE__->meta->make_immutable; } { package TestThing; use Moose; extends 'Thing'; has bar => (is => 'ro', required => 1); has kooh => (is => 'ro', required => 1); no Moose; __PACKAGE__->meta->make_immutable; } { my $c = container 'MyApp' => as { service foo => 42; service thing => ( class => 'Thing', dependencies => [depends_on('foo')], parameters => { moo => { isa => 'Int' }, }, ); }; { my $t = $c->resolve( service => 'thing', parameters => { moo => 123, }, ); isa_ok $t, 'Thing'; is $t->foo, 42, '... and has a foo literal'; is $t->moo, 123, '... and has a moo literal'; } container $c => as { service bar => 23; service '+thing' => ( class => 'TestThing', dependencies => [depends_on('bar')], parameters => ['kooh'], ); }; { my $t = $c->resolve( service => 'thing', parameters => { moo => 123, kooh => 456, }, ); isa_ok $t, 'TestThing'; is $t->foo, 42, '... and has a foo literal'; is $t->moo, 123, '... and has moo literal'; is $t->bar, 23, '... and has a bar literal'; is $t->kooh, 456, '... and has a kooh literal'; } } { my $parameterized = container MyApp => ['Config'] => as { service foo => (block => sub { 42 }); }; container $parameterized => as { service '+foo' => (block => sub { 23 }); }; my $c = $parameterized->create(Config => container Config => as {}); is $c->resolve(service => 'foo'), 23, 'Can resolve foo from parameterized container'; } like exception { service '+foo' => 42; }, qr/^Service inheritance doesn't make sense for literal services/, 'exception thrown when trying to do service inheritance from literal service'; like exception { container Foo => as { container foo => as {}; service '+foo' => (block => sub { 42 }); }; }, qr/^Trying to inherit from service 'foo', but found a Bread::Board::Container/, 'exception thrown when trying to inherit from a container'; like exception { container Foo => as { service foo => 42; service '+foo' => (block => sub { 123 }); }; }, qr/^Trying to inherit from a literal service/, 'exception thrown when trying to inherit from literal service'; { package Bread::Board::FooInjection; use Moose; extends 'Bread::Board::Literal'; no Moose; } like exception { container Foo => as { service foo => (block => sub { 123 }); service '+foo' => (service_class => 'Bread::Board::FooInjection'); }; }, qr/^Changing a service's class is not possible when inheriting/, 'exception thrown when trying to change a service class when inheriting'; like exception { container Foo => as { service foo => (block => sub { 123 }); service '+foo' => (service_type => 'Foo'); }; }, qr/^Changing a service's class is not possible when inheriting/, 'exception thrown when trying to change a service type when inheriting'; { package Foo; use Moose; no Moose; } like exception { container Foo => as { service foo => (block => sub { 123 }); service '+foo' => (class => 'Foo'); }; }, qr/^/, 'exception thrown when trying to change a service class for "+foo"'; like exception { container Foo => as { service foo => (class => 'Foo'); service '+foo' => (block => sub { 123 }); }; }, qr/^/, 'exception thrown when trying to change a service class for "foo"'; done_testing; Bread-Board-0.37/t/029_sugar_auto_wire_names.t0000644000175000017500000000464113505520232020431 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package DBI; use Moose; has 'dsn' => (is => 'ro', isa => 'Str'); has 'username' => (is => 'ro', isa => 'Str'); has 'password' => (is => 'ro', isa => 'Str'); sub connect { my ($class, $dsn, $username, $password) = @_; $class->new(dsn => $dsn, username => $username, password => $password); } package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); has 'dbh' => (is => 'ro', isa => 'DBI', required => 1); } my $c = container 'MyApp' => as { service 'log_file' => "logfile.log"; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => ['log_file'] ); container 'Database' => as { service 'dsn' => "dbi:sqlite:dbname=my-app.db"; service 'username' => "user"; service 'password' => "pass"; service 'dbh' => ( block => sub { my $s = shift; DBI->connect( $s->param('dsn'), $s->param('username'), $s->param('password'), ) || die "Could not connect"; }, dependencies => [qw[dsn username password]] ); }; service 'application' => ( class => 'MyApplication', dependencies => ['logger', 'Database/dbh'] ); }; my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $dbh = $c->resolve( service => 'Database/dbh' ); isa_ok($dbh, 'DBI'); is($dbh->dsn, "dbi:sqlite:dbname=my-app.db", '... got the right dsn'); is($dbh->username, "user", '... got the right username'); is($dbh->password, "pass", '... got the right password'); my $app = $c->resolve( service => 'application'); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); isa_ok($app->dbh, 'DBI'); isnt($app->dbh, $dbh, '... got a different dbh'); done_testing; Bread-Board-0.37/t/020_sugar.t0000644000175000017500000000234313505520232015154 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } my $c = container 'MyApp' => as { service 'log_file' => "logfile.log"; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => { log_file => depends_on('log_file'), } ); service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); }; my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); done_testing; Bread-Board-0.37/t/077_more_complex_typemap_w_roles.t0000644000175000017500000000765413505520232022043 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Bread::Board; { # Abstract items ... package Desk; use Moose::Role; package Chair; use Moose::Role; package WorkArea; use Moose::Role; has 'desk' => ( is => 'ro', does => 'Desk', required => 1, ); has 'chair' => ( is => 'ro', does => 'Chair', required => 1, ); } # crappy stuff { package CheapMetalDesk; use Moose; with 'Desk'; package CheapOfficeChair; use Moose; with 'Chair'; package Cubicle; use Moose; with 'WorkArea'; } # good stuff { package NiceWoodenDesk; use Moose; with 'Desk'; package AeronChair; use Moose; with 'Chair'; package Office; use Moose; with 'WorkArea'; } { package Employee; use Moose; has [ 'first_name', 'last_name' ] => ( is => 'ro', isa => 'Str', required => 1, ); has 'work_area' => ( is => 'ro', does => 'WorkArea', required => 1, ); package Manager; use Moose; extends 'Employee'; has '+work_area' => ( isa => 'Office' ); } my $c = container 'Initech' => as { # Employees ... typemap 'Desk' => infer( class => 'CheapMetalDesk' ); typemap 'Chair' => infer( class => 'CheapOfficeChair' ); typemap 'WorkArea' => infer( class => 'Cubicle' ); # Managers ... service 'managers_desk' => (class => 'NiceWoodenDesk'); service 'managers_chair' => (class => 'AeronChair'); typemap 'Office' => infer( dependencies => { desk => depends_on('managers_desk'), chair => depends_on('managers_chair') } ); typemap 'Employee' => infer; typemap 'Manager' => infer; }; my $micheal = $c->resolve( type => 'Employee', parameters => { first_name => 'Micheal', last_name => 'Bolton' } ); my $samir = $c->resolve( type => 'Employee', parameters => { first_name => 'Samir', last_name => 'Nagheenanajar' } ); isa_ok($micheal, 'Employee'); is($micheal->first_name, 'Micheal', '... got the right first name'); is($micheal->last_name, 'Bolton', '... got the right last name'); does_ok($micheal->work_area, 'WorkArea'); isa_ok($micheal->work_area, 'Cubicle'); does_ok($micheal->work_area->desk, 'Desk'); isa_ok($micheal->work_area->desk, 'CheapMetalDesk'); does_ok($micheal->work_area->chair, 'Chair'); isa_ok($micheal->work_area->chair, 'CheapOfficeChair'); isa_ok($samir, 'Employee'); is($samir->first_name, 'Samir', '... got the right first name'); is($samir->last_name, 'Nagheenanajar', '... got the right last name'); does_ok($samir->work_area, 'WorkArea'); isa_ok($samir->work_area, 'Cubicle'); does_ok($samir->work_area->desk, 'Desk'); isa_ok($samir->work_area->desk, 'CheapMetalDesk'); does_ok($samir->work_area->chair, 'Chair'); isa_ok($samir->work_area->chair, 'CheapOfficeChair'); isnt($micheal, $samir, '... two different employees'); isnt($micheal->work_area, $samir->work_area, '... two different cubicles'); isnt($micheal->work_area->chair, $samir->work_area->chair, '... two different cubicle chairs'); isnt($micheal->work_area->desk, $samir->work_area->desk, '... two different cubicle desks'); # managers my $lundberg = $c->resolve( type => 'Manager', parameters => { first_name => 'Bill', last_name => 'Lundberg' } ); isa_ok($lundberg, 'Manager'); is($lundberg->first_name, 'Bill', '... got the right first name'); is($lundberg->last_name, 'Lundberg', '... got the right last name'); does_ok($lundberg->work_area, 'WorkArea'); isa_ok($lundberg->work_area, 'Office'); does_ok($lundberg->work_area->desk, 'Desk'); isa_ok($lundberg->work_area->desk, 'NiceWoodenDesk'); does_ok($lundberg->work_area->chair, 'Chair'); isa_ok($lundberg->work_area->chair, 'AeronChair'); done_testing; Bread-Board-0.37/t/003_block_injection.t0000644000175000017500000000540713505520232017174 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Moose; use Test::Fatal; use Bread::Board::BlockInjection; use Bread::Board::SetterInjection; use Bread::Board::Literal; { package Needle; use Moose; package Mexican::Black::Tar; use Moose; package Addict; use Moose; has 'needle' => (is => 'rw'); has 'spoon' => (is => 'rw'); has 'stash' => (is => 'rw'); } my $s = Bread::Board::BlockInjection->new( name => 'William', class => 'Addict', block => sub { my $s = shift; $s->class->new(%{ $s->params }); }, dependencies => { needle => Bread::Board::SetterInjection->new(name => 'spike', class => 'Needle'), spoon => Bread::Board::Literal->new(name => 'works', value => 'Spoon!'), }, parameters => { stash => { isa => 'Mexican::Black::Tar' } } ); isa_ok($s, 'Bread::Board::BlockInjection'); does_ok($s, 'Bread::Board::Service::WithDependencies'); does_ok($s, 'Bread::Board::Service::WithParameters'); does_ok($s, 'Bread::Board::Service'); { my $i = $s->get(stash => Mexican::Black::Tar->new); isa_ok($i, 'Addict'); isa_ok($i->needle, 'Needle'); is($i->spoon, 'Spoon!', '... got our literal service'); isa_ok($i->stash, 'Mexican::Black::Tar'); { my $i2 = $s->get(stash => Mexican::Black::Tar->new); isnt($i, $i2, '... calling it again returns an new object'); } } is($s->name, 'William', '... got the right name'); is($s->class, 'Addict', '... got the right class'); my $deps = $s->dependencies; is_deeply([ sort keys %$deps ], [qw/needle spoon/], '... got the right dependency keys'); my $needle = $s->get_dependency('needle'); isa_ok($needle, 'Bread::Board::Dependency'); isa_ok($needle->service, 'Bread::Board::SetterInjection'); is($needle->service->name, 'spike', '... got the right name'); is($needle->service->class, 'Needle', '... got the right class'); my $spoon = $s->get_dependency('spoon'); isa_ok($spoon, 'Bread::Board::Dependency'); isa_ok($spoon->service, 'Bread::Board::Literal'); is($spoon->service->name, 'works', '... got the right name'); is($spoon->service->value, 'Spoon!', '... got the right literal value'); my $params = $s->parameters; is_deeply([ sort keys %$params ], [qw/stash/], '... got the right paramter keys'); is_deeply($params->{stash}, { isa => 'Mexican::Black::Tar' }, '... got the right parameter spec'); ## check some errors isnt(exception { $s->get; }, undef, '... you must supply the required parameters'); isnt(exception { $s->get(stash => []); }, undef, '... you must supply the required parameters as correct types'); isnt(exception { $s->get(stash => Mexican::Black::Tar->new, foo => 10); }, undef, '... you must supply the required parameters (and no more)'); done_testing; Bread-Board-0.37/t/022_sugar.t0000644000175000017500000000343313505520232015157 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; { package FileLogger; use Moose; has 'log_file' => (is => 'ro', required => 1); package MyApplication; use Moose; has 'logger' => (is => 'ro', isa => 'FileLogger', required => 1); } sub loggers { service 'log_file' => "logfile.log"; service 'logger' => ( class => 'FileLogger', lifecycle => 'Singleton', dependencies => { log_file => depends_on('log_file'), } ); } my $c = container 'MyApp'; Bread::Board::set_root_container($c); is exception { Bread::Board::set_root_container($c) }, undef, "setting the root container multiple times works"; is exception { Bread::Board::set_root_container(undef) }, undef, "setting the root container to undef works"; container $c => as { like exception { Bread::Board::set_root_container(undef) }, qr/Can't set the root container when we're already in a container/, "can't set the root container from inside a container"; }; Bread::Board::set_root_container($c); loggers(); # reuse baby !!! service 'application' => ( class => 'MyApplication', dependencies => { logger => depends_on('logger'), } ); my $logger = $c->resolve( service => 'logger' ); isa_ok($logger, 'FileLogger'); is($logger->log_file, 'logfile.log', '... got the right logfile dep'); is($c->fetch('logger/log_file')->service, $c->fetch('log_file'), '... got the right value'); is($c->fetch('logger/log_file')->get, 'logfile.log', '... got the right value'); my $app = $c->resolve( service => 'application' ); isa_ok($app, 'MyApplication'); isa_ok($app->logger, 'FileLogger'); is($app->logger, $logger, '... got the right logger (singleton)'); done_testing; Bread-Board-0.37/t/053_parameterized_clone.t0000644000175000017500000000151013505520232020050 0ustar yanickyanick#!/usr/bin/perl use strict; use warnings; use Test::More; use Test::Fatal; use Bread::Board; my $c = container Foo => as { container Bar => ['Baz'] => as { service moo => ( block => sub { my ($s) = @_; $s->param('kooh'); }, dependencies => { kooh => depends_on('Baz/boo'), }, ); }; container Bif => as { service boo => 42; }; }; is $c->fetch('Bar')->create(Baz => $c->fetch('Bif'))->resolve(service => 'moo'), 42, 'container works as expected'; my $clone; is exception { $clone = $c->clone }, undef, 'cloning the container does not throw an exception'; is $clone->fetch('Bar')->create(Baz => $clone->fetch('Bif'))->resolve(service => 'moo'), 42, 'clone behaves like the original'; done_testing; Bread-Board-0.37/t/310_literal_keyword.t0000644000175000017500000000071713505520232017240 0ustar yanickyanickuse strict; use warnings; use Test::More tests => 1; use Bread::Board; my $c = container 'Main' => as { service with_literal => ( block => sub { $_[0]->param('foo') . join '', @{ $_[0]->param('bar') } }, dependencies => { foo => literal( 'fantastic' ), bar => literal( [ 1..5 ] ), }, ); }; is $c->resolve( service => 'with_literal' ) => 'fantastic12345', 'got expected service string from literals'; Bread-Board-0.37/doap.xml0000644000175000017500000003245713505520232014500 0ustar yanickyanick Bread-Board A solderless way to wire up your application components Stevan Little Alex Balhatchet André Walker ben hengst Brad Bowman Caleb Cushing Daisuke Maki Dave Rolsky Florian Ragwitz Gabor Szabo Gianni Ceccarelli Graham Knop Jason Galea Jason May Jay Hannah Jesse Luehrs Jonathan Rockway Kip Hampton Mohammad S Anwar Neil Bowers Philippe Bruhat BooK Sterling Hanenkamp Tomas Doran Yanick Champoux Yanick Champoux zdk 0.01 2008-01-07 0.02 2008-01-08 0.03 2008-01-08 0.04 2008-10-31 0.05 2008-11-03 0.06 2008-11-03 0.07 2009-02-18 0.08 2009-07-18 0.09 2009-07-29 0.10 2010-02-22 0.11 2010-03-25 0.12 2010-04-18 0.13 2010-04-23 0.14 2010-08-24 0.15 2010-09-30 0.16 2011-01-10 0.17 2011-02-22 0.18 2011-04-13 0.19 2011-06-01 0.20 2011-06-13 0.21 2011-09-06 0.22 2011-10-03 0.23 2011-10-14 0.24 2011-10-15 0.25 2011-10-20 0.26 2013-08-01 0.27 2013-08-06 0.28 2013-08-30 0.29 2013-11-21 0.30 2014-02-02 0.31 2014-05-08 0.32 2014-06-03 0.33 2015-04-26 0.34 2016-03-28 0.35 2017-07-31 0.36 2017-10-15 Perl