Mercurial > pub > Impl
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 228:431db7034a88 | 229:47f77e6409f7 |
|---|---|
| 1 package IMPL::Web::Application::OperationContract; | 1 package IMPL::Web::Application::OperationContract; |
| 2 use strict; | 2 use strict; |
| 3 | 3 |
| 4 use IMPL::lang qw(:declare); | |
| 4 use IMPL::declare { | 5 use IMPL::declare { |
| 5 base => [ | 6 require => { |
| 6 'IMPL::Object' => undef, | 7 'Exception' => 'IMPL::Exception', |
| 7 'IMPL::Object::Autofill' => '@_' | 8 'ArgumentException' => '-IMPL::ArgumentException', |
| 8 ], | 9 'ResourceBaseClass' => 'IMPL::Web::Application::ResourceBase' |
| 9 props => [ | 10 }, |
| 10 binding => PROP_ALL, | 11 base => [ |
| 11 response => PROP_ALL | 12 'IMPL::Object' => undef, |
| 12 ] | 13 'IMPL::Object::Autofill' => '@_' |
| 14 ], | |
| 15 props => [ | |
| 16 binding => PROP_ALL, | |
| 17 success => PROP_ALL, | |
| 18 error => PROP_ALL | |
| 19 ] | |
| 13 }; | 20 }; |
| 14 | 21 |
| 15 sub Invoke { | 22 sub Invoke { |
| 16 my ($this, $resource, $httpAction) = @_; | 23 my ( $this, $resource, $request ) = @_; |
| 24 | |
| 25 die ArgumentException( resource => 'A valid resource is required' ) | |
| 26 unless eval { $resource->isa(ResourceBaseClass) }; | |
| 27 | |
| 28 my $result = eval { | |
| 29 _InvokeDelegate($this->binding, $resource, $request) | |
| 30 }; | |
| 17 | 31 |
| 18 if ($this->) | 32 if (my $e = $@) { |
| 33 $result = _InvokeDelegate($this->error, $resource, $request, $e); | |
| 34 } else { | |
| 35 $result = _InvokeDelegate($this->success, $resource, $request, $result); | |
| 36 } | |
| 37 | |
| 38 return $result; | |
| 39 } | |
| 40 | |
| 41 sub _InvokeDelegate { | |
| 42 my $delegate = shift; | |
| 19 | 43 |
| 44 return $delegete->(@_) if ref $delegate eq 'CODE'; | |
| 45 return $delegate->Invoke(@_) if eval { $delegate->can('Invoke')}; | |
| 20 } | 46 } |
| 21 | 47 |
| 22 1; | 48 1; |
| 23 | 49 |
| 24 __END__ | 50 __END__ |
| 38 'OperationContract' => 'IMPL::Web::Application::OperationContract', | 64 'OperationContract' => 'IMPL::Web::Application::OperationContract', |
| 39 'RedirectResponse' => 'IMPL::Web::Application::RedirectResponse' | 65 'RedirectResponse' => 'IMPL::Web::Application::RedirectResponse' |
| 40 }; | 66 }; |
| 41 | 67 |
| 42 my $operation = OperationContract->new( | 68 my $operation = OperationContract->new( |
| 43 bind => sub { | 69 binding => sub { |
| 44 my ($resource,$model,$itemName) = @_; | 70 my ($resource,$request) = @_; |
| 45 | 71 |
| 46 return $model->findItem($itemName); | 72 my $itemName = $request->param('itemName', qr/^(\w+)$/); |
| 73 | |
| 74 return $model->FindItem($itemName); | |
| 47 }, | 75 }, |
| 48 response => RedirectResponse->new( | 76 success => sub { |
| 49 locator => $relativeLocator | 77 my ($resource,$request,$result) = @_; |
| 50 ) | 78 return HttpReponse->Redirect(location => $resource->location->Child($result->id)); |
| 79 } | |
| 51 ); | 80 ); |
| 52 | 81 |
| 53 my $response = $operation->InvokeOperation($resource); | 82 my $response = $operation->InvokeOperation($resource); |
| 54 | 83 |
| 55 =end code | 84 =end code |
| 56 | 85 |
| 57 =head1 DESCRIPTION | 86 =head1 DESCRIPTION |
| 58 | 87 |
| 59 Связывает методы предметной области с операциями над ресурсами. Для связи с | 88 Для орисания контракта операции используется понятие делегата, тоесть объекта, |
| 60 моделью используется функция, которой будут переданы параметры: | 89 представляющего собой функцию, либо объект, имеющий метод C<Invoke>. |
| 90 | |
| 91 Поскольку предметная область должна быть отделена от | |
| 92 контроллеров веб-сервиса, она ничего не знает про существование ресурсов и их | |
| 93 организацию и тем более о протоколе C<HTTP>, поэтому все вещи, связанные с | |
| 94 формированием ответов сервера, представлениями данных и т.п. должны выполняться | |
| 95 самими контроллерами. Поведение контроллеров описывается контрактами, в которых | |
| 96 указываются делегаты для реализации необходимого функционала, для корректного | |
| 97 отображения ресурсов в объекты предметной области и обратно. | |
| 98 | |
| 99 Контракт операции состоит из нескольких свойств, осуществляющих привязку к | |
| 100 предметной области: | |
| 61 | 101 |
| 62 =over | 102 =over |
| 63 | 103 |
| 64 =item C<$reousrce> Ресурс для которого выполняется операция | 104 =item * C<binding> |
| 65 | 105 |
| 66 =item C<$model> Объект модели данных, связанный с данным ресурсом, тоже, что | 106 делегат для привязки операции над ресурсом к предметной области. |
| 67 и C<<>$resource->model>> только для краткости | |
| 68 | 107 |
| 69 =item C<$action> Контекст текущего C<HTTP> запроса. | 108 =item * C<success> |
| 70 | 109 |
| 71 =back | 110 делегат для обработки результат операции, например для формирования ответа с |
| 111 перенаправлением. | |
| 72 | 112 |
| 73 Результат выполнения будет передан дополнительному обработчику C<response>, | 113 =item * C<error> |
| 74 который выполнит необходимое преобразование. | 114 |
| 115 делегат для обработки исключительной ситуации, может быть использован для | |
| 116 формирования представления для повторного ввода данных на форме. | |
| 117 | |
| 118 =back | |
| 75 | 119 |
| 76 =head1 MEMBERS | 120 =head1 MEMBERS |
| 77 | 121 |
| 122 =head2 C<[get,set] binding> | |
| 123 | |
| 124 Привязка операции к ресурсу, например | |
| 125 | |
| 126 =begin code | |
| 127 | |
| 128 $operationContract->binding(sub { | |
| 129 my ($resource,$action) = @_; | |
| 130 $resource->model | |
| 131 }) | |
| 132 | |
| 133 =end code | |
| 134 | |
| 135 Может быть как ссылка на процедуру, так и ссылкой на объект, имеющий метод | |
| 136 C<Invoke>. | |
| 137 | |
| 138 =head2 C<[get,set] success> | |
| 139 | |
| 140 Обрабатывает результат привязки к предметной области. | |
| 141 | |
| 142 =begin code | |
| 143 | |
| 144 # redirect (for example after POST) | |
| 145 $operationContract->success(sub { | |
| 146 my ($resource,$action,$result) = @_; | |
| 147 | |
| 148 return IMPL::Web::HttpResponse | |
| 149 ->Redirect($resource->location->Child($result->id)); | |
| 150 }) | |
| 151 | |
| 152 =end code | |
| 153 | |
| 154 Может быть как ссылка на процедуру, так и ссылкой на объект, имеющий метод | |
| 155 C<Invoke>. | |
| 156 | |
| 157 =head2 C<[get,set] error> | |
| 158 | |
| 159 Обрабатывает ошибку возникшую при выполнении привязки к предметной области. | |
| 160 | |
| 161 =begin | |
| 162 | |
| 163 $operationContract->error(sub { | |
| 164 my ($resource,$action,$error) = @_; | |
| 165 | |
| 166 $action->form->errors->{''} = $error; | |
| 167 | |
| 168 return $resource->model; | |
| 169 }); | |
| 170 | |
| 171 =end | |
| 172 | |
| 78 =cut | 173 =cut |
