diff Lib/IMPL/Web/Application/Resource.pm @ 229:47f77e6409f7

heavily reworked the resource model of the web application: *some ResourcesContraact functionality moved to Resource +Added CustomResource *Corrected action handlers
author sergey
date Sat, 29 Sep 2012 02:34:47 +0400
parents
children 6d8092d8ce1b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/Web/Application/Resource.pm	Sat Sep 29 02:34:47 2012 +0400
@@ -0,0 +1,148 @@
+package IMPL::Web::Application::Resource;
+use strict;
+
+use IMPL::lang qw(:constants);
+use IMPL::declare {
+    require => {
+        Exception           => 'IMPL::Exception',
+        ArgumentException   => '-IMPL::InvalidArgumentException',
+        OperationException  => '-IMPL::InvalidOperationException',
+        NotAllowedException => 'IMPL::Web::NotAllowedException',
+        NotFoundException   => 'IMPL::Web::NotFoundException'
+      },
+      base => [
+        'IMPL::Object'                              => undef,
+        'IMPL::Web::Application::ResourceInterface' => undef
+      ],
+      props => [
+        parent   => PROP_GET | PROP_OWNERSET,
+        model    => PROP_GET | PROP_OWNERSET,
+        id       => PROP_GET | PROP_OWNERSET,
+        contract => PROP_GET | PROP_OWNERSET,
+        location => PROP_GET | PROP_OWNERSET
+      ]
+};
+
+sub CTOR {
+    my ( $this, %args ) = @_;
+
+    die ArgumentException->new( id => 'A resource identifier is required' )
+      unless $args{id};
+    die ArgumentException->new( contract => 'A contract is required' )
+      unless $args{id};
+
+    $this->parent( $args{parent} );
+    $this->model( $args{model} );
+    $this->id( $args{id} );
+    $this->contract( $args{contract} );
+
+    # если расположение явно не указано, что обычно делается для корневого
+    # ресурса, то оно вычисляется автоматически, либо остается не заданным
+    $this->location( $args{location}
+          || eval { $this->parent->location->Child( $this->id ) } );
+    )
+
+}
+
+sub InvokeHttpVerb {
+      my ( $this, $verb, $action ) = @_;
+
+      my $verb = $this->contract->verbs->{ lc($verb) };
+
+      die NotAllowedException->new(
+          allow => join( ',' map( uc, keys %{ $this->contract->verbs } ) ) )
+        unless $verb;
+
+      return $verb->Invoke( $this, $action );
+}
+
+# это реализация по умолчанию, базируется информации о ресурсах, содержащийся
+# в контракте.
+sub FetchChildResource {
+      my ( $this, $childId ) = @_;
+      
+      my $info = $this->contract->FindChildResourceInfo($childId);
+      
+      die NotFoundException->new() unless $info;
+      
+      my $binding = $this->{binding};
+      my $contract = $this->{contract}
+        or die OperationException->new("Can't fetch a contract for the resource", $childId);
+        
+      my %args = (
+            parent => $this,
+            id     => $childId
+      );
+      
+      $args{model} = _InvokeDelegate($binding,$this);
+
+      return $contract->CreateResource(%args);
+}
+
+sub _InvokeDelegate {
+    my $delegate = shift;
+    
+    return $delegete->(@_) 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>.
+
+=cut