package Bread::Board::Container; BEGIN { $Bread::Board::Container::AUTHORITY = 'cpan:STEVAN'; } BEGIN { $Bread::Board::Container::VERSION = '0.25'; } use Moose; use Moose::Util::TypeConstraints 'find_type_constraint'; use MooseX::Params::Validate; 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; 1; =pod =head1 NAME Bread::Board::Container =head1 VERSION version 0.25 =head1 DESCRIPTION =head1 METHODS =over 4 =item B =item B =item B =item B =item B =item B =item B =item B =item B =item B =item B =item B =item B =item B =item B $service_name, ?type => $type, ?parameters => { ... } )> =item B =item B =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 COPYRIGHT AND LICENSE This software is copyright (c) 2011 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 __END__