view Lib/IMPL/Web/Application/OperationContract.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 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