view lib/IMPL/Config/Container.pm @ 414:ec6f2d389d1e ref20150831

working on IMPL::Config::Bag
author cin
date Fri, 02 Oct 2015 06:56:24 +0300
parents af8d359ee4cc
children 3d24b10dd0d5
line wrap: on
line source

package IMPL::Config::Container;
use strict;

use IMPL::Exception();
use IMPL::lang qw(:base);
use IMPL::declare {
	require => {
		Descriptor        => 'IMPL::Config::Descriptor',
		ValueDescriptor   => 'IMPL::Config::ValueDescriptor',
		ActivationContext => 'IMPL::Config::ActivationContext',
		Hierarchy         => 'IMPL::Config::Hierarchy',
		Bag               => 'IMPL::Config::Bag'
	},
	base => [
		'IMPL::Object' => undef
	],
	props => [
		roles     => 'r',
		services  => 'r',
		instances => 'r',
		parent    => 'r'
	]
};

my $nextRoleId = 1;

sub CTOR {
	my ( $this, $parent ) = @_;

	$this->instances( {} );

	if ($parent) {
		$this->roles( Hierarchy->new( $parent->roles ) );
		$this->services( Bag->new( $parent->services ) );
		$this->parent($parent);
	}
	else {
		$this->roles( Hierarchy->new() );
		$this->services( Bag->new() );
	}
}

sub Register {
	my ( $this, $role, $service ) = @_;

	die IMPL::InvalidArgumentException->new(
		role => 'The argument is required' )
	  unless $role;
	die IMPL::InvalidArgumentException->new('service')
	  unless is( $service, Descriptor );

	if ( isarray($role) ) {
		my $tempRole = "unnamed-" . $nextRoleId++;
		$this->role->AddRole( $tempRole, $role );
		$role = $tempRole;
	}

	$service = ValueDescriptor->new( value => $service )
	  unless is( $service, Descriptor );

	$this->services->Register( $this->roles->GetLinearRoleHash($role), $service );
}

sub Resolve {
	my ( $this, $role, %opts ) = @_;

	my $descriptor = $this->services->Resolve($role);

	return $descriptor->Activate( ActivationContext->new($this) )
	  if $descirptor;

	return $opts{default} if exists $opts{default};
}

sub ResolveAll {
	my ( $this, $role, %opts ) = @_;

	my $all = $this->services->ResolveAll($role);

	my $context;

	my @result;

	foreach my $service (@$all) {
		$context = ActivationContext->new($this)
		  unless $context || $opts{shared};

		push @result, $service->Activate($context);
	}

	return \@result;
}

1;

__END__

=pod

=head1 NAME

C<IMPL::Config::Container> - dependency injection container

=head1 SYNOPSIS

=head2 METHODS

=head3 GetService($serviceId)

=over

=item * $serviceId

A string indetifier of the service, it can be in two forms: class name or service name,
for the class name it should be prefixed with C<@>, for example: C<@Foo::Bar>.

=back

The activation container maintains two maps, one for classes and the other for names.
The first one is useful when we searching for an implementation the second one when
we need a particular service. 

=head3 RegisterService($descriptor)

=cut