view Lib/IMPL/Web/Application/Resource.pm @ 245:7c517134c42f

Added Unsupported media type Web exception corrected resourceLocation setting in the resource Implemented localizable resources for text messages fixed TT view scopings, INIT block in controls now sets globals correctly.
author sergey
date Mon, 29 Oct 2012 03:15:22 +0400
parents a02b110da931
children 814d755e5d12
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}
        or die OperationException->new("Can't fetch a contract for the resource", $childId);
        
      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