view Lib/IMPL/Web/Application/OperationContract.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 6d8092d8ce1b
children 4abda21186cd
line wrap: on
line source

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

use IMPL::lang qw(:declare);
use IMPL::declare {
	require => {
		Exception         => 'IMPL::Exception',
		ArgumentException => '-IMPL::InvalidArgumentException',
		ResourceInterface => 'IMPL::Web::Application::ResourceInterface'
	  },
	  base => [
		'IMPL::Object'           => undef,
		'IMPL::Object::Autofill' => '@_'
	  ],
	  props => [
		binding    => PROP_ALL,
		success    => PROP_ALL,
		error      => PROP_ALL
	  ]
};

sub Invoke {
	my ( $this, $resource, $request ) = @_;

	die ArgumentException->new( resource => 'A valid resource is required' )
	  unless eval { $resource->isa(ResourceInterface) };
	
	my $result = eval {
		_InvokeDelegate($this->binding, $resource, $request)
	};
	
	if (my $e = $@) {
	    if ($this->error) {
		  $result = _InvokeDelegate($this->error, $resource, $request, $e) ;
	    } else {
	        die $e;
	    }
	    
	} else {
		$result = _InvokeDelegate($this->success, $resource, $request, $result)
		  if ($this->success);
	}

	return $result;
}

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::OperationContract> - Описание операции над
веб-ресурсом.

=head1 SYNOPSIS

=begin code

use IMPL::require {
	'OperationContract' => 'IMPL::Web::Application::OperationContract',
	'RedirectResponse' => 'IMPL::Web::Application::RedirectResponse'
};

my $operation = OperationContract->new(
    binding => sub {
    	my ($resource,$request) = @_;
    	
    	my $itemName = $request->param('itemName', qr/^(\w+)$/);
    	
    	return $model->FindItem($itemName);
    },
    success => sub {
        my ($resource,$request,$result) = @_;
        return HttpReponse->Redirect(location => $resource->location->Child($result->id));
    }
);

my $response = $operation->InvokeOperation($resource);

=end code

=head1 DESCRIPTION

Для орисания контракта операции используется понятие делегата, тоесть объекта,
представляющего собой функцию, либо объект, имеющий метод C<Invoke>.

Поскольку предметная область должна быть отделена от
контроллеров веб-сервиса, она ничего не знает про существование ресурсов и их
организацию и тем более о протоколе C<HTTP>, поэтому все вещи, связанные с
формированием ответов сервера, представлениями данных и т.п. должны выполняться
самими контроллерами. Поведение контроллеров описывается контрактами, в которых
указываются делегаты для реализации необходимого функционала, для корректного
отображения ресурсов в объекты предметной области и обратно.

Контракт операции состоит из нескольких свойств, осуществляющих привязку к 
предметной области:

=over

=item * C<binding>

делегат для привязки операции над ресурсом к предметной области.

=item * C<success>

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

=item * C<error>

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

=back    

=head1 MEMBERS

=head2 C<[get,set] binding>

Привязка операции к ресурсу, например

=begin code

$operationContract->binding(sub {
	my ($resource,$action) = @_;
	$resource->model
})

=end code

Может быть как ссылка на процедуру, так и ссылкой на объект, имеющий метод
C<Invoke>.

=head2 C<[get,set] success>

Обрабатывает результат привязки к предметной области.

=begin code

# redirect (for example after POST)
$operationContract->success(sub {
	my ($resource,$action,$result) = @_;
	
	return IMPL::Web::HttpResponse
	   ->Redirect($resource->location->Child($result->id));
})

=end code

Может быть как ссылка на процедуру, так и ссылкой на объект, имеющий метод
C<Invoke>.

=head2 C<[get,set] error>

Обрабатывает ошибку возникшую при выполнении привязки к предметной области.

=begin

$operationContract->error(sub {
	my ($resource,$action,$error) = @_;
	
	$action->form->errors->{''} = $error;
	
	return $resource->model;
});

=end 

=cut