changeset 372:e12c14177848

refactoring web resources model, implementing new simplified model
author cin
date Tue, 24 Dec 2013 20:01:55 +0400
parents d5c8b955bf8d
children 3ca44e23fd1f
files Lib/IMPL/Web/Application/CustomResource.pm Lib/IMPL/Web/Application/Resource.pm Lib/IMPL/Web/Application/ResourceBase.pm Lib/IMPL/Web/Handler/View.pm
diffstat 4 files changed, 416 insertions(+), 415 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/Web/Application/CustomResource.pm	Fri Dec 13 16:49:47 2013 +0400
+++ b/Lib/IMPL/Web/Application/CustomResource.pm	Tue Dec 24 20:01:55 2013 +0400
@@ -2,123 +2,184 @@
 use strict;
 
 use IMPL::Const qw(:prop);
-
+use IMPL::lang qw(:hash :base);
 use IMPL::declare {
-    require => {
-        NotAllowedException => 'IMPL::Web::NotAllowedException',
-        HttpResponse => 'IMPL::Web::HttpResponse'
-    },
-    base => [
-        'IMPL::Web::Application::Resource' => '@_'
-    ],
-    props => [
-        accessCheck => PROP_RW,
-        resources   => PROP_RO,
-        verbs       => PROP_RO,
-        namedResources => PROP_RO,
-        regexResources => PROP_RO
-    ]
+	require => {
+		Exception           => 'IMPL::Exception',
+		OperationException  => '-IMPL::InvalidOperationException',
+		NotFoundException   => 'IMPL::Web::NotFoundException',
+		HttpResponse        => 'IMPL::Web::HttpResponse',
+		Loader              => 'IMPL::Code::Loader'
+	  },
+	  base  => [ 'IMPL::Web::Application::ResourceBase' => '@_' ],
+	  props => [
+		accessCheck    => PROP_RW,
+		resources      => PROP_RO,
+		verbs          => PROP_RO,
+		namedResources => PROP_RO,
+		regexResources => PROP_RO
+	  ]
 };
 
+use constant { CustomResource => __PACKAGE__ };
+
 our %RESOURCE_BINDINGS = (
-    GET => 'HttpGet',
-    POST => 'HttpPost',
-    PUT => 'HttpPut',
-    DELETE => 'HttpDelete',
-    HEAD => 'HttpHead',
-    OPTIONS => 'HttpOptions',
-    TRACE => 'HttpTrace'
+	GET     => 'HttpGet',
+	POST    => 'HttpPost',
+	PUT     => 'HttpPut',
+	DELETE  => 'HttpDelete',
+	HEAD    => 'HttpHead',
+	OPTIONS => 'HttpOptions',
+	TRACE   => 'HttpTrace'
 );
 
 sub CTOR {
-	my ($this,%args) = @_;
-	
-	$this->verbs($args{verbs} || {});
-	$this->resources($args{resources} || []);
-	
-	$this->accessCheck($args{accessCheck})
-	   if $args{accessCheck};
-	   
-    while(my ($verb,$methodName) = each %RESOURCE_BINDINGS) {
-    	if(my $method = $this->can($methodName)) {
-    		$this->verbs->{lc($verb)} ||= $method;
-    	}
-    }
+	my ( $this, %args ) = @_;
+
+	$this->verbs( $args{verbs}         || {} );
+	$this->resources( $args{resources} || [] );
+
+	$this->accessCheck( $args{accessCheck} )
+	  if $args{accessCheck};
+
+	while ( my ( $verb, $methodName ) = each %RESOURCE_BINDINGS ) {
+		if ( my $method = $this->can($methodName) ) {
+			$this->verbs->{ lc($verb) } ||= $method;
+		}
+	}
 }
 
 sub FindChildResourceInfo {
-    my ( $this, $name ) = @_;
-    
-    $this->PrepareResourcesCache()
-        unless $this->namedResources;
-    
-    if ( my $info = $this->namedResources->{$name} ) {
-        return $info, [$name];
-    }
-    else {
-        foreach my $info ( @{$this->regexResources} ) {
-            my $rx = $info->{match};
-            if(my @childId = $name =~ m/$rx/) {
-                return $info, \@childId; 
-            }
-        }
-    }
+	my ( $this, $name ) = @_;
+
+	$this->PrepareResourcesCache()
+	  unless $this->namedResources;
 
-    return;
+	if ( my $info = $this->namedResources->{$name} ) {
+		return $info, [$name];
+	}
+	else {
+		foreach my $info ( @{ $this->regexResources } ) {
+			my $rx = $info->{match};
+			if ( my @childId = $name =~ m/$rx/ ) {
+				return $info, \@childId;
+			}
+		}
+	}
+
+	return;
 }
 
-sub GetAllowedMethods {
-    map( uc, keys %{ shift->verbs } );
+# это реализация по умолчанию, базируется информации о ресурсах, содержащийся
+# в контракте.
+sub FetchChildResource {
+	my ( $this, $childId ) = @_;
+
+	$this->AccessCheck('FETCH');
+
+	my ( $info, $childIdParts ) = $this->FindChildResourceInfo($childId);
+
+	die NotFoundException->new( $this->location->url, $childId ) unless $info;
+
+	my %args;
+
+	my $binding  = $info->{binding};
+	my $contract = $info->{contract};
+	if ( ref($binding) eq 'HASH' ) {
+		$args{$_} = _InvokeDelegate( $binding->{$_}, $this, @$childIdParts )
+		  foreach keys %$binding;
+	}
+	else {
+		$args{model} = _InvokeDelegate( $binding, $this, @$childIdParts );
+	}
+
+	# support for dynamic contracts
+	if ( ref $contract eq 'CODE' || eval { $contract->can('Invoke') } ) {
+		$contract = _InvokeDelegate( $contract, $this, $args{model} );
+	}
+
+	die OperationException->new( "Can't fetch a contract for the resource",
+		$childId )
+	  unless $contract;
+
+	$args{parent}  = $this;
+	$args{id}      = $childId;
+	$args{request} = $this->request;
+
+	my $factory;
+
+	if ( ref($contract) eq 'HASH' ) {
+		$factory = delete $contract->{class} || CustomResource;
+		hashApply( \%args, $contract );
+
+		Loader->default->Require($factory)
+		  unless ref($factory);
+	}
+	else {
+		die OperationException->new(
+			"Unsupported contract for the child resource '$childId'",
+			$contract, $this->location );
+	}
+
+	return $factory->new(%args);
 }
 
 sub PrepareResourcesCache {
-    my ($this) = @_;
-    
-    my @resources = ($this->GetChildResources(), @{$this->resources});
-    
-    my %nameMap;
-    my @rxMap;
+	my ($this) = @_;
+
+	my @resources = ( $this->GetChildResources(), @{ $this->resources } );
+
+	my %nameMap;
+	my @rxMap;
+
+	foreach my $res (@resources) {
 
-    foreach my $res (@resources) {
-        #skip resources without contract
-        next unless $res->{contract};
-        
-        if ( my $name = $res->{name} ) {
-            $nameMap{$name} = $res;
-        }
-        if ( $res->{match} ) {
-            push @rxMap,$res;
-        }
-    }
+		#skip resources without contract
+		next unless $res->{contract};
 
-    $this->regexResources(\@rxMap);
-    $this->namedResources(\%nameMap);
+		if ( my $name = $res->{name} ) {
+			$nameMap{$name} = $res;
+		}
+		if ( $res->{match} ) {
+			push @rxMap, $res;
+		}
+	}
+
+	$this->regexResources( \@rxMap );
+	$this->namedResources( \%nameMap );
 }
 
 sub AccessCheck {
-	my ($this,$verb) = @_;
-	
-	my $handler = $this->accessCheck; 
-	
-	if(ref($handler) eq 'CODE') {
-		return &$handler($this,$verb);
+	my ( $this, $verb ) = @_;
+
+	my $handler = $this->accessCheck;
+
+	if ( ref($handler) eq 'CODE' ) {
+		return &$handler( $this, $verb );
 	}
 }
 
 sub GetChildResources {
-    
+
 }
 
 sub HttpOptions {
-    my ($this) = @_;
-    
-    my @allow = $this->GetAllowedMethods();
-    return HttpResponse->new(
-        status => '200 OK',
-        headers => {
-            allow => join ( ',', @allow )
-        }
-    );
+	my ($this) = @_;
+
+	my @allow = $this->GetAllowedMethods();
+	return HttpResponse->new(
+		status  => '200 OK',
+		headers => {
+			allow => join( ',', @allow )
+		}
+	);
+}
+
+sub _InvokeDelegate {
+	my $delegate = shift;
+
+	return $delegate->(@_) if ref $delegate eq 'CODE';
+	return $delegate->Invoke(@_) if eval { $delegate->can('Invoke') };
 }
 
 1;
@@ -227,4 +288,4 @@
 Метод возвращает список из хешей, которые будут переданы в качестве параметра
 C<resources> контракту данного ресурса.
 
-=cut
\ No newline at end of file
+=cut
--- a/Lib/IMPL/Web/Application/Resource.pm	Fri Dec 13 16:49:47 2013 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,323 +0,0 @@
-package IMPL::Web::Application::Resource;
-use strict;
-
-use URI;
-use Carp qw(carp);
-use IMPL::lang qw(:hash :base);
-use IMPL::Const qw(:prop);
-use IMPL::declare {
-	require => {
-		ViewResult          => 'IMPL::Web::ViewResult',
-		Exception           => 'IMPL::Exception',
-		ArgumentException   => '-IMPL::InvalidArgumentException',
-		OperationException  => '-IMPL::InvalidOperationException',
-		NotAllowedException => 'IMPL::Web::NotAllowedException',
-		NotFoundException   => 'IMPL::Web::NotFoundException',
-		Loader              => 'IMPL::Code::Loader',
-		CustomResource      => '-IMPL::Web::Application::CustomResource' 
-	  },
-	  base => [
-		'IMPL::Object'                              => undef,
-		'IMPL::Web::Application::ResourceInterface' => undef
-	  ],
-	  props => [
-		request     => PROP_RO,
-		application => PROP_RO,
-		parent      => PROP_RO,
-		model       => PROP_RO,
-		id          => PROP_RO,
-		location    => PROP_RO,
-		role        => PROP_RO | PROP_LIST
-	  ]
-};
-
-sub CTOR {
-	my ( $this, %args ) = @_;
-
-	die ArgumentException->new( id => 'A resource identifier is required' )
-	  unless $args{id};
-
-	
-    die ArgumentException->new(request => 'A request object must be specified')
-        unless $args{request};
-	
-	$this->request( $args{request} );	
-	$this->parent( $args{parent} );
-	$this->model( $args{model} );
-	$this->id( $args{id} );
-	$this->application( $args{request}->application );
-	
-# если расположение явно не указано, то оно вычисляется автоматически,
-# либо остается не заданным
-	$this->location( $args{location}
-		  || eval { $this->parent->location->Child( $this->id ) } );
-		  
-	if (my $role = $args{role}) {
-		if (ref($role) eq 'ARRAY') {
-			$this->role($role);
-		} elsif (not ref($role)) {
-			$this->role(split(/\s+/, $role));
-		} else {
-			die ArgumentException->new( role => 'A invalid value is provided, expected ARRAY or SCALAR');
-		}
-	}
-}
-
-sub InvokeHttpVerb {
-	my ( $this, $verb ) = @_;
-
-	my $operation = $this->verbs->{ lc($verb) };
-
-	die NotAllowedException->new(
-		allow => join( ',', $this->GetAllowedMethods ) )
-	  unless $operation;
-
-	$this->AccessCheck($verb);
-	my $request = $this->request;
-
-# в случае, когда один ресурс вызывает HTTP метод другого ресурса, нужно
-# сохранить оригинальный resourceLocation
-	$request->context->{resourceLocation} ||= $this->location;
-
-# это свойство специфично только для REST приложений.
-# сохранение текущего ресурса не повлечет за собой существенных расходов,
-# т.к. они просто освободятся несколько позже.
-	if ( not $request->context->{resource} ) {
-		$request->context->{resource} = $this;
-		$request->context->{environment} = sub {
-			carp "using request environment is deprecated";
-			$this->PrepareEnvironment()
-		};
-	}
-
-	return _InvokeDelegate( $operation, $this, $request );
-}
-
-sub security {
-	shift->request->security
-}
-
-sub verbs {
-	{} # возвращаем пстой список операций
-}
-
-sub GetAllowedMethods {
-	# возвращаем пустой список доступных операций
-}
-
-sub FindChildResourceInfo {
-	
-}
-
-sub AccessCheck {
-
-}
-
-# это реализация по умолчанию, базируется информации о ресурсах, содержащийся
-# в контракте.
-sub FetchChildResource {
-	my ( $this, $childId ) = @_;
-
-	$this->AccessCheck('FETCH');
-
-	my ( $info, $childIdParts ) =
-	  $this->FindChildResourceInfo($childId);
-
-	die NotFoundException->new( $this->location->url, $childId ) unless $info;
-	
-	my %args;
-
-	my $binding  = $info->{binding};
-	my $contract = $info->{contract};
-	if (ref($binding) eq 'HASH' ) {
-		$args{$_} = _InvokeDelegate( $binding->{$_}, $this, @$childIdParts )
-			foreach keys %$binding;
-	} else {
-		$args{model} = _InvokeDelegate( $binding, $this, @$childIdParts );
-	}
-
-    # support for dynamic contracts
-	if ( ref $contract eq 'CODE' || eval { $contract->can('Invoke') } ) {
-		$contract = _InvokeDelegate( $contract, $this, $args{model} );
-	}
-
-	die OperationException->new( "Can't fetch a contract for the resource",
-		$childId )
-	  unless $contract;
-
-	$args{parent} = $this;
-	$args{id}     = $childId;
-	$args{request} = $this->request;
-	
-	my $factory;
-	
-	if (ref($contract) eq 'HASH') {
-	    $factory = delete $contract->{class} || CustomResource;
-	    hashApply(\%args,$contract);
-	    
-	    Loader->default->Require($factory)
-	       unless ref($factory);
-	} else {
-	    die OperationException->new("Unsupported contract for the child resource '$childId'",$contract,$this->location);
-	}
-
-	return $factory->new(%args);
-}
-
-sub _InvokeDelegate {
-	my $delegate = shift;
-
-	return $delegate->(@_) if ref $delegate eq 'CODE';
-	return $delegate->Invoke(@_) if eval { $delegate->can('Invoke') };
-}
-
-sub Seek {
-	my ($this, $role) = @_;
-	
-	my @roles;
-	
-	if (ref($role) eq 'ARRAY') {
-		@roles = @{$role};	
-	} elsif (not ref($role)) {
-		@roles = split(/\s+/, $role);
-	} else {
-		die ArgumentException->new( role => 'A invalid value is provided, expected ARRAY or SCALAR');
-	}
-		
-	
-	for(my $r = $this; $r; $r = $r->parent) {
-		return $r if $r->HasRole(@roles);
-	}
-	return;
-}
-
-sub HasRole {
-	my ($this, @roles) = @_;	
-	my %cache = map { $_, 1 } @{$this->role};
-	return scalar(grep not($cache{$_}), @roles) ? 0 : 1;
-}
-
-1;
-
-__END__
-
-=pod
-
-=head1 NAME
-
-C<IMPL::Web::Application::Resource> - Web-ресурс.
-
-=head1 SYNOPSIS
-
-Класс для внутреннего использования. Объединяет в себе контракт и модель данных.
-Основная задача - обработать поступающий от контроллера запрос на вызов C<HTTP>
-метода.
-
-Экземпляры данного класса передаются в качестве параметров делегатам
-осуществляющим привязку к модели в C<IMPL::Web::Application::ResourceContract>
-и C<IMPL::Web::Application::OperationContract>.
-
-=head1 DESCRIPTION
-
-Весь функционал ресурса, поддерживаемые им C<HTTP> методы определяются
-контрактом. Однако можно реализовывать ресурсы, которые не имеют контракта
-или он отличается от того, что предоставляется стандартно
-C<IMPL::Web::Application::ResourceContract>.
-
-Каждый ресурс является контейнером, тоесть позволяет получить дочерний ресурс
-по идентифифкатору, если таковой имеется, тоесть ресурс, у которого нет дочерних
-ресурсов на самом деле рассматривается как пустой контейнер.
-
-С ресурсом непосредственно взаимодействует котроллер запросов
-C<IMPL::Web::Handler::RestController>, вызывая два метода.
-
-=over
-
-=item * C<FetchChildResource($childId)>
-
-Данный метод возвращает дочерний ресурс, соответствующий C<$childId>.
-Текущая реализация использует метод C<FindChildResourceInfo> контракта текущего
-ресурса, после чего создает дочерний ресурс.
-
-Если дочерний ресурс не найден, вызывается исключение
-C<IMPL::Web::NotFoundException>.
-
-=item * C<InvokeHttpVerb($verb,$action)>
-
-Обрабатывает запрос к ресурсу. Для этого используется контракт ресурса, в
-нем выбирается соответсвующий C<IMPL::Web::Application::OperationContract>.
-Затем найденный контракт для указанной операции используется для обработки
-запроса.
-
-=back
-
-Если объект реализует два вышеуказанных метода, он является веб-ресурсом, а
-детали его реализации, котнракт и прочее уже не важно, поэтому можно реализовать
-собственный класс ресурса, например унаследованный от 
-C<IMPL::Web::Application::CustomResource>.
-
-=head1 MEMBERS
-
-=head2 C<[get]request>
-
-Объект C<IMPL::Web::Application::Action> представляющий запрос к серверу.
-
-=head2 C<[get]application>
-
-Ссылка на приложение, к которому относится данный ресурс. Получается
-автоматически из объекта запроса.
-
-=head2 C<[get]contract>
-
-Обязательное свойство для ресурса, ссылается, на контракт, соответствующий
-данному ресурсу, используется для выполнения C<HTTP> методов и получения
-дочерних ресурсов.
-
-=head2 C<[get]id>
-
-Обязательное свойство ресурса, идентифицирует его в родительском контейнере,
-для корневого ресурса может иметь произвольное значение.
-
-=head2 C<[get]parent>
-
-Ссылка на родительский ресурс, для корневого ресурса не определена.
-
-=head2 C<[get]model>
-
-Ссылка на объект предметной области, представляемый данным ресурсом. Данное 
-свойство не является обязательным и может быть не задано.
-
-=head2 C<[get]location>
-
-Объект типа C<IMPL::Web::AutoLocator> или аналогичный описывающий адрес текущего
-ресурса, может быть как явно передан при создании ресурса, так и вычислен
-автоматически (только для ресурсов имеющих родителя). Следует заметить, что
-адрес ресурса не содержит параметров запроса, а только путь.
-
-=head2 C<[get,list]role>
-
-Список ролей ресурса. Роль это условный маркер, который позволяет определить
-функции выполняемые ресурсом, например контейнер, профиль пользователя и т.п.
-
-Используется при построении цепочек навигации, а также при поиске с использованием
-метода C<seek>.
-
-=head2 C<seek($role)>
-
-Ищет ресурс в цепочке родителей (включая сам ресурс) с подходящими ролями.
-
-Роли могут быть переданы в виде массива или строки, где роли разделены пробелами 
-
-=head2 C<[get]FetchChildResource($id)>
-
-Возвращает дочерний ресурс, по его идентификатору.
-
-Данная реализация использует контракт текущего ресурса для поиска информации о
-дочернем ресурсе C<< $this->contract->FindChildResourceInfo($id) >>.
-
-Затем осуществляется привязка к моделе, тоесть, выполняется делегат, для
-получения модели дочернего ресурса, а затем осуществляется привязка к контракту,
-при этом в делегат, который должен вернуть контракт дочернего ресурса передаются
-текущий ресурc и модель дочернего ресурса.
-
-=cut
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/Web/Application/ResourceBase.pm	Tue Dec 24 20:01:55 2013 +0400
@@ -0,0 +1,262 @@
+package IMPL::Web::Application::ResourceBase;
+use strict;
+
+use URI;
+use Carp qw(carp);
+use IMPL::lang qw(:hash :base);
+use IMPL::Const qw(:prop);
+use IMPL::declare {
+	require => {
+		
+		Exception           => 'IMPL::Exception',
+		ArgumentException   => '-IMPL::InvalidArgumentException',
+		OperationException  => '-IMPL::InvalidOperationException',
+		NotAllowedException => 'IMPL::Web::NotAllowedException',
+		
+	  },
+	  base => [
+		'IMPL::Object'                              => undef,
+		'IMPL::Web::Application::ResourceInterface' => undef
+	  ],
+	  props => [
+		request     => PROP_RO,
+		application => PROP_RO,
+		parent      => PROP_RO,
+		model       => PROP_RO,
+		id          => PROP_RO,
+		location    => PROP_RO,
+		role        => PROP_RO | PROP_LIST
+	  ]
+};
+
+sub CTOR {
+	my ( $this, %args ) = @_;
+
+    die ArgumentException->new(request => 'A request object must be specified')
+        unless $args{request};
+	
+	$this->request( $args{request} );	
+	$this->parent( $args{parent} );
+	$this->model( $args{model} );
+	$this->id( $args{id} );
+	$this->application( $args{request}->application );
+	
+# если расположение явно не указано, то оно вычисляется автоматически,
+# либо остается не заданным
+	$this->location( $args{location}
+		  || eval { $this->parent->location->Child( $this->id ) } );
+		  
+	if (my $role = $args{role}) {
+		if (ref($role) eq 'ARRAY') {
+			$this->role($role);
+		} elsif (not ref($role)) {
+			$this->role(split(/\s+/, $role));
+		} else {
+			die ArgumentException->new( role => 'A invalid value is provided, expected ARRAY or SCALAR');
+		}
+	}
+}
+
+sub InvokeHttpVerb {
+	my ( $this, $verb ) = @_;
+
+	my $operation = $this->verbs->{ lc($verb) };
+
+	die NotAllowedException->new(
+		allow => join( ',', $this->GetAllowedMethods ) )
+	  unless $operation;
+
+	$this->AccessCheck($verb);
+	my $request = $this->request;
+
+# в случае, когда один ресурс вызывает HTTP метод другого ресурса, нужно
+# сохранить оригинальный resourceLocation
+	$request->context->{resourceLocation} ||= $this->location;
+
+# это свойство специфично только для REST приложений.
+# сохранение текущего ресурса не повлечет за собой существенных расходов,
+# т.к. они просто освободятся несколько позже.
+	if ( not $request->context->{resource} ) {
+		$request->context->{resource} = $this;
+		$request->context->{environment} = sub {
+			carp "using request environment is deprecated";
+			$this->PrepareEnvironment()
+		};
+	}
+
+	return _InvokeDelegate( $operation, $this, $request );
+}
+
+sub security {
+	shift->request->security
+}
+
+sub verbs {
+	{} # возвращаем пстой список операций
+}
+
+sub GetAllowedMethods {
+	map( uc, keys %{ shift->verbs } );
+}
+
+sub AccessCheck {
+
+}
+
+sub Seek {
+	my ($this, $role) = @_;
+	
+	my @roles;
+	
+	if (ref($role) eq 'ARRAY') {
+		@roles = @{$role};	
+	} elsif (not ref($role)) {
+		@roles = split(/\s+/, $role);
+	} else {
+		die ArgumentException->new( role => 'A invalid value is provided, expected ARRAY or SCALAR');
+	}
+		
+	
+	for(my $r = $this; $r; $r = $r->parent) {
+		return $r if $r->HasRole(@roles);
+	}
+	return;
+}
+
+sub HasRole {
+	my ($this, @roles) = @_;	
+	my %cache = map { $_, 1 } @{$this->role};
+	return scalar(grep not($cache{$_}), @roles) ? 0 : 1;
+}
+
+sub _InvokeDelegate {
+	my $delegate = shift;
+
+	return $delegate->(@_) if ref $delegate eq 'CODE';
+	return $delegate->Invoke(@_) if eval { $delegate->can('Invoke') };
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+C<IMPL::Web::Application::Resource> - Web-ресурс.
+
+=head1 SYNOPSIS
+
+Класс для внутреннего использования. Объединяет в себе контракт и модель данных.
+Основная задача - обработать поступающий от контроллера запрос на вызов C<HTTP>
+метода.
+
+Экземпляры данного класса передаются в качестве параметров делегатам
+осуществляющим привязку к модели в C<IMPL::Web::Application::ResourceContract>
+и C<IMPL::Web::Application::OperationContract>.
+
+=head1 DESCRIPTION
+
+Весь функционал ресурса, поддерживаемые им C<HTTP> методы определяются
+контрактом. Однако можно реализовывать ресурсы, которые не имеют контракта
+или он отличается от того, что предоставляется стандартно
+C<IMPL::Web::Application::ResourceContract>.
+
+Каждый ресурс является контейнером, тоесть позволяет получить дочерний ресурс
+по идентифифкатору, если таковой имеется, тоесть ресурс, у которого нет дочерних
+ресурсов на самом деле рассматривается как пустой контейнер.
+
+С ресурсом непосредственно взаимодействует котроллер запросов
+C<IMPL::Web::Handler::RestController>, вызывая два метода.
+
+=over
+
+=item * C<FetchChildResource($childId)>
+
+Данный метод возвращает дочерний ресурс, соответствующий C<$childId>.
+Текущая реализация использует метод C<FindChildResourceInfo> контракта текущего
+ресурса, после чего создает дочерний ресурс.
+
+Если дочерний ресурс не найден, вызывается исключение
+C<IMPL::Web::NotFoundException>.
+
+=item * C<InvokeHttpVerb($verb,$action)>
+
+Обрабатывает запрос к ресурсу. Для этого используется контракт ресурса, в
+нем выбирается соответсвующий C<IMPL::Web::Application::OperationContract>.
+Затем найденный контракт для указанной операции используется для обработки
+запроса.
+
+=back
+
+Если объект реализует два вышеуказанных метода, он является веб-ресурсом, а
+детали его реализации, котнракт и прочее уже не важно, поэтому можно реализовать
+собственный класс ресурса, например унаследованный от 
+C<IMPL::Web::Application::CustomResource>.
+
+=head1 MEMBERS
+
+=head2 C<[get]request>
+
+Объект C<IMPL::Web::Application::Action> представляющий запрос к серверу.
+
+=head2 C<[get]application>
+
+Ссылка на приложение, к которому относится данный ресурс. Получается
+автоматически из объекта запроса.
+
+=head2 C<[get]contract>
+
+Обязательное свойство для ресурса, ссылается, на контракт, соответствующий
+данному ресурсу, используется для выполнения C<HTTP> методов и получения
+дочерних ресурсов.
+
+=head2 C<[get]id>
+
+Обязательное свойство ресурса, идентифицирует его в родительском контейнере,
+для корневого ресурса может иметь произвольное значение.
+
+=head2 C<[get]parent>
+
+Ссылка на родительский ресурс, для корневого ресурса не определена.
+
+=head2 C<[get]model>
+
+Ссылка на объект предметной области, представляемый данным ресурсом. Данное 
+свойство не является обязательным и может быть не задано.
+
+=head2 C<[get]location>
+
+Объект типа C<IMPL::Web::AutoLocator> или аналогичный описывающий адрес текущего
+ресурса, может быть как явно передан при создании ресурса, так и вычислен
+автоматически (только для ресурсов имеющих родителя). Следует заметить, что
+адрес ресурса не содержит параметров запроса, а только путь.
+
+=head2 C<[get,list]role>
+
+Список ролей ресурса. Роль это условный маркер, который позволяет определить
+функции выполняемые ресурсом, например контейнер, профиль пользователя и т.п.
+
+Используется при построении цепочек навигации, а также при поиске с использованием
+метода C<seek>.
+
+=head2 C<seek($role)>
+
+Ищет ресурс в цепочке родителей (включая сам ресурс) с подходящими ролями.
+
+Роли могут быть переданы в виде массива или строки, где роли разделены пробелами 
+
+=head2 C<[get]FetchChildResource($id)>
+
+Возвращает дочерний ресурс, по его идентификатору.
+
+Данная реализация использует контракт текущего ресурса для поиска информации о
+дочернем ресурсе C<< $this->contract->FindChildResourceInfo($id) >>.
+
+Затем осуществляется привязка к моделе, тоесть, выполняется делегат, для
+получения модели дочернего ресурса, а затем осуществляется привязка к контракту,
+при этом в делегат, который должен вернуть контракт дочернего ресурса передаются
+текущий ресурc и модель дочернего ресурса.
+
+=cut
--- a/Lib/IMPL/Web/Handler/View.pm	Fri Dec 13 16:49:47 2013 +0400
+++ b/Lib/IMPL/Web/Handler/View.pm	Tue Dec 24 20:01:55 2013 +0400
@@ -10,7 +10,7 @@
         Factory      => 'IMPL::Web::View::ObjectFactory',
         HttpResponse => 'IMPL::Web::HttpResponse',
         Loader       => 'IMPL::Code::Loader',
-        ViewResult   => '-IMPL::Web::ViewResult',
+        ViewResult   => 'IMPL::Web::ViewResult',
         Security     => 'IMPL::Security'
       },
       base => [
@@ -58,7 +58,8 @@
         layout      => $this->layout,
         document    => {},
         session     => sub { Security->context },
-        user        => sub { Security->principal }
+        user        => sub { Security->principal },
+        security    => sub { $action->security }
 	};
 
     my %responseParams = (