diff Lib/IMPL/Web/Application/OperationContract.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 431db7034a88
children 6d8092d8ce1b
line wrap: on
line diff
--- a/Lib/IMPL/Web/Application/OperationContract.pm	Thu Sep 13 17:55:01 2012 +0400
+++ b/Lib/IMPL/Web/Application/OperationContract.pm	Sat Sep 29 02:34:47 2012 +0400
@@ -1,22 +1,48 @@
 package IMPL::Web::Application::OperationContract;
 use strict;
 
+use IMPL::lang qw(:declare);
 use IMPL::declare {
-	base => [
-	   'IMPL::Object' => undef,
-	   'IMPL::Object::Autofill' => '@_'
-	],
-	props => [
-	   binding => PROP_ALL,
-	   response => PROP_ALL
-	]
+	require => {
+		'Exception'         => 'IMPL::Exception',
+		'ArgumentException' => '-IMPL::ArgumentException',
+		'ResourceBaseClass' => 'IMPL::Web::Application::ResourceBase'
+	  },
+	  base => [
+		'IMPL::Object'           => undef,
+		'IMPL::Object::Autofill' => '@_'
+	  ],
+	  props => [
+		binding    => PROP_ALL,
+		success    => PROP_ALL,
+		error      => PROP_ALL
+	  ]
 };
 
 sub Invoke {
-	my ($this, $resource, $httpAction) = @_;
+	my ( $this, $resource, $request ) = @_;
+
+	die ArgumentException( resource => 'A valid resource is required' )
+	  unless eval { $resource->isa(ResourceBaseClass) };
+	  
+	my $result = eval {
+		_InvokeDelegate($this->binding, $resource, $request)
+	};
 	
-	if ($this->)
+	if (my $e = $@) {
+		$result = _InvokeDelegate($this->error, $resource, $request, $e);
+	} else {
+		$result = _InvokeDelegate($this->success, $resource, $request, $result);
+	}
+
+	return $result;
+}
+
+sub _InvokeDelegate {
+	my $delegate = shift;
 	
+	return $delegete->(@_) if ref $delegate eq 'CODE';
+	return $delegate->Invoke(@_) if eval { $delegate->can('Invoke')};
 }
 
 1;
@@ -40,14 +66,17 @@
 };
 
 my $operation = OperationContract->new(
-    bind => sub {
-    	my ($resource,$model,$itemName) = @_;
+    binding => sub {
+    	my ($resource,$request) = @_;
+    	
+    	my $itemName = $request->param('itemName', qr/^(\w+)$/);
     	
-    	return $model->findItem($itemName);
+    	return $model->FindItem($itemName);
     },
-    response => RedirectResponse->new(
-        locator => $relativeLocator
-    ) 
+    success => sub {
+        my ($resource,$request,$result) = @_;
+        return HttpReponse->Redirect(location => $resource->location->Child($result->id));
+    }
 );
 
 my $response = $operation->InvokeOperation($resource);
@@ -56,23 +85,89 @@
 
 =head1 DESCRIPTION
 
-Связывает методы предметной области с операциями над ресурсами. Для связи с
-моделью используется функция, которой будут переданы параметры:
+Для орисания контракта операции используется понятие делегата, тоесть объекта,
+представляющего собой функцию, либо объект, имеющий метод C<Invoke>.
+
+Поскольку предметная область должна быть отделена от
+контроллеров веб-сервиса, она ничего не знает про существование ресурсов и их
+организацию и тем более о протоколе C<HTTP>, поэтому все вещи, связанные с
+формированием ответов сервера, представлениями данных и т.п. должны выполняться
+самими контроллерами. Поведение контроллеров описывается контрактами, в которых
+указываются делегаты для реализации необходимого функционала, для корректного
+отображения ресурсов в объекты предметной области и обратно.
+
+Контракт операции состоит из нескольких свойств, осуществляющих привязку к 
+предметной области:
 
 =over
 
-=item C<$reousrce> Ресурс для которого выполняется операция
+=item * C<binding>
 
-=item C<$model> Объект модели данных, связанный с данным ресурсом, тоже, что
-и C<<>$resource->model>> только для краткости
+делегат для привязки операции над ресурсом к предметной области.
+
+=item * C<success>
 
-=item C<$action> Контекст текущего C<HTTP> запроса.
+делегат для обработки результат операции, например для формирования ответа с
+перенаправлением.
+
+=item * C<error>
 
-=back
+делегат для обработки исключительной ситуации, может быть использован для
+формирования представления для повторного ввода данных на форме.  
 
-Результат выполнения будет передан дополнительному обработчику C<response>,
-который выполнит необходимое преобразование.
+=back    
 
 =head1 MEMBERS
 
-=cut
\ No newline at end of file
+=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