view Lib/IMPL/Web/Application/Resource.pm @ 250:129e48bb5afb

DOM refactoring ObjectToDOM methods are virtual QueryToDOM uses inflators Fixed transform for the complex values in the ObjectToDOM QueryToDOM doesn't allow to use complex values (HASHes) as values for nodes (overpost problem)
author sergey
date Wed, 07 Nov 2012 04:17:53 +0400
parents 814d755e5d12
children 827cf96faa1c
line wrap: on
line source

package IMPL::Web::Application::Resource;
use strict;

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'
      },
      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 $operation = $this->contract->verbs->{ lc($verb) };
      
      die NotAllowedException->new(
          allow => join( ',', map( uc, keys %{ $this->contract->verbs } ) )
      )
        unless $operation;
      
      # в случае, когда один ресурс вызывает HTTP метод другого ресурса, нужно
      # сохранить оригинальный resourceLocation  
      $action->context->{resourceLocation} ||= $this->location; 

      return $operation->Invoke( $this, $action );
}

# это реализация по умолчанию, базируется информации о ресурсах, содержащийся
# в контракте.
sub FetchChildResource {
      my ( $this, $childId ) = @_;
      
      my ($info,$childIdParts) = $this->contract->FindChildResourceInfo($childId);
      
      die NotFoundException->new($this->location->url,$childId) unless $info;
      
      my $binding = $info->{binding};
      my $contract = $info->{contract};
        
      if (ref $contract eq 'CODE') {
          $contract = $contract->();
          $info->{contract} = $contract;
      }
      
      die OperationException->new("Can't fetch a contract for the resource", $childId)
        unless $contract;
        
      my %args = (
            parent => $this,
            id     => $childId
      );
      
      $args{model} = _InvokeDelegate($binding,$this,@$childIdParts);

      return $contract->CreateResource(%args);
}

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]contract>

Обязательное свойство для ресурса, ссылается, на контракт, соответствующий
данному ресурсу, используется для выполнения C<HTTP> методов и получения
дочерних ресурсов.

=head2 C<[get]id>

Обязательное свойство ресурса, идентифицирует его в родительском контейнере,
для корневого ресурса может иметь произвольное значение.

=head2 C<[get]parent>

Ссылка на родительский ресурс, для корневого ресурса не определена.

=head2 C<[get]model>

Ссылка на объект предметной области, представляемый данным ресурсом. Данное 
свойство не является обязательным и может быть не задано.

=head2 C<[get]location>

Объект типа C<IMPL::Web::AutoLocator> или аналогичный описывающий адрес текущего
ресурса, может быть как явно передан при создании ресурса, так и вычислен
автоматически (только для ресурсов имеющих родителя). Следует заметить, что
адрес ресурса не содержит параметров запроса, а только путь.

=cut