diff lib/IMPL/Config/ServicesBag.pm @ 407:c6e90e02dd17 ref20150831

renamed Lib->lib
author cin
date Fri, 04 Sep 2015 19:40:23 +0300
parents
children ee36115f6a34
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/IMPL/Config/ServicesBag.pm	Fri Sep 04 19:40:23 2015 +0300
@@ -0,0 +1,158 @@
+package IMPL::Config::ServicesBag;
+
+require v5.9.5;
+
+use mro;
+
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+	base => [
+		'IMPL::Object' => undef
+	],
+	props => [
+		_prototype => PROP_RW,
+		_nameMap   => PROP_RW,
+		_typeMap   => PROP_RW,
+		_props     => PROP_RW,
+	]
+};
+
+sub CTOR {
+	my ( $this, $prototype ) = @_;
+
+	$this->_prototype($prototype) if $prototype;
+	$this->_nameMap( {} );
+	$this->_typeMap( {} );
+}
+
+sub GetDescriptorByName {
+	my ( $this, $name ) = @_;
+
+	my $d = $this->_nameMap->{$name};
+	return $d if $d and $d->{valid};
+
+	my $parent = $this->_prototype;
+
+	if ( $parent and $d = $parent->GetDescriptorByName($name) ) {
+		return $this->_nameMap->{$name} = $d;
+	}
+
+	return undef;
+}
+
+sub GetDescriptorByType {
+	my ( $this, $type ) = @_;
+
+	my $d = $this->_typeMap->{$type};
+	return $d if $d and $d->{valid};
+
+	my $parent = $this->_prototype;
+	if ( $parent and $d = $parent->GetDescriptorByType($type) ) {
+		return $this->_typeMap->{$type} = $d;
+	}
+
+	return undef;
+}
+
+sub RegisterValue {
+	my ( $this, $value, $name, $type ) = @_;
+
+	my $d = { owner => $this, value => $value, valid => 1 };
+
+	if ($type) {
+		my $map = $this->_typeMap;
+		my $isa = mro::get_linear_isa($type);
+		$d->{isa} = $isa;
+
+		# the service record which is superseded by the current one
+		my $replaces = $this->GetDescriptorByType($type);
+
+		foreach my $t (@$isa) {
+			if ( my $prev = $this->GetDescriptorByType($t) ) {
+
+				# keep previous registrations if they are valid
+				next if not $replaces or $prev != $replaces;
+			}
+
+			$map->{$t} = $d;
+		}
+
+		if ($replaces) {
+
+			# invalidate cache
+			$replaces->{owner}->UpdateDescriptor($replaces);
+		}
+	}
+
+	if ($name) {
+		my $prev = $this->_nameMap->{$name};
+		$d->{name} = $name;
+		$this->_nameMap->{$name} = $d;
+		$prev->{owner}->UpdateDescriptor($prev) if $prev;
+	}
+
+	return $d;
+}
+
+sub UpdateDescriptor {
+	my ( $this, $d ) = @_;
+
+	my $d2 = {};
+
+	# copy descriptor
+	while ( my ( $k, $v ) = each %$d ) {
+		$d2->{$k} = $v;
+	}
+
+	# update named entries
+	my $name = $d->{name};
+	if ( $name and $this->_nameMap->{$name} == $d ) {
+		$this->_nameMap->{$name} = $d2;
+	}
+
+	# update type entries
+	if ( my $isa = $d->{isa} ) {
+		my $map = $this->_typeMap;
+		foreach my $t (@$isa) {
+			next unless $map->{$t} == $d;
+			$map->{$t} = $d2;
+		}
+	}
+
+	$d->{valid} = 0;
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Коллекция сервисов построена на прототиптровании экземпляров, т.е. при создании
+новой коллекции может указваться базовая коллекция в которой будет происходить
+поиск сервисов в случае их отсутсвия в основной. Для оптимизации данного процесса
+сервисы кешируются, чтобы избежать можестрвенных операций поиска по иерархии
+коллекций, для этого каждый сервис описывается дескриптором:
+
+=over
+
+=item * isa массив типов сервиса, если он регистрировался как сервис
+
+=item * value значение
+
+=item * valid признак того, что дескриптор действителен
+
+=item * owner коллекция, которая создала данный дескриптор
+
+=back
+
+Если запрашиваемый десриптор не найден это является ошибкой, поэтому негативные
+ответы не кешируются
+
+=cut