Mercurial > pub > Impl
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