Mercurial > pub > Impl
comparison lib/IMPL/Web/Application/ResourceBase.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
author | cin |
---|---|
date | Fri, 04 Sep 2015 19:40:23 +0300 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
406:f23fcb19d3c1 | 407:c6e90e02dd17 |
---|---|
1 package IMPL::Web::Application::ResourceBase; | |
2 use strict; | |
3 | |
4 use URI; | |
5 use Carp qw(carp); | |
6 use IMPL::lang qw(:hash :base); | |
7 use IMPL::Const qw(:prop); | |
8 use IMPL::declare { | |
9 require => { | |
10 | |
11 Exception => 'IMPL::Exception', | |
12 ArgumentException => '-IMPL::InvalidArgumentException', | |
13 OperationException => '-IMPL::InvalidOperationException', | |
14 NotAllowedException => 'IMPL::Web::NotAllowedException', | |
15 | |
16 }, | |
17 base => [ | |
18 'IMPL::Object' => undef, | |
19 'IMPL::Web::Application::ResourceInterface' => undef | |
20 ], | |
21 props => [ | |
22 request => PROP_RO, | |
23 application => PROP_RO, | |
24 parent => PROP_RO, | |
25 model => PROP_RO, | |
26 id => PROP_RO, | |
27 location => PROP_RO, | |
28 role => PROP_RO | PROP_LIST | |
29 ] | |
30 }; | |
31 | |
32 sub CTOR { | |
33 my ( $this, %args ) = @_; | |
34 | |
35 die ArgumentException->new(request => 'A request object must be specified') | |
36 unless $args{request}; | |
37 | |
38 $this->request( $args{request} ); | |
39 $this->parent( $args{parent} ) if $args{parent}; | |
40 $this->model( $args{model} ) if $args{model}; | |
41 $this->id( $args{id} ) if $args{id}; | |
42 $this->application( $args{request}->application ); | |
43 | |
44 # если расположение явно не указано, то оно вычисляется автоматически, | |
45 # либо остается не заданным | |
46 $this->location( $args{location} | |
47 || eval { $this->parent->location->Child( $this->id ) } ); | |
48 | |
49 if (my $role = $args{role}) { | |
50 if (ref($role) eq 'ARRAY') { | |
51 $this->role($role); | |
52 } elsif (not ref($role)) { | |
53 $this->role(split(/\s+/, $role)); | |
54 } else { | |
55 die ArgumentException->new( role => 'A invalid value is provided, expected ARRAY or SCALAR'); | |
56 } | |
57 } | |
58 } | |
59 | |
60 sub InvokeHttpVerb { | |
61 my ( $this, $verb ) = @_; | |
62 | |
63 my $operation = $this->verbs->{ lc($verb) }; | |
64 | |
65 die NotAllowedException->new( | |
66 allow => join( ',', $this->GetAllowedMethods ) ) | |
67 unless $operation; | |
68 | |
69 $this->AccessCheck($verb); | |
70 my $request = $this->request; | |
71 | |
72 # в случае, когда один ресурс вызывает HTTP метод другого ресурса, нужно | |
73 # сохранить оригинальный resourceLocation | |
74 $request->context->{resourceLocation} ||= $this->location; | |
75 | |
76 # это свойство специфично только для REST приложений. | |
77 # сохранение текущего ресурса не повлечет за собой существенных расходов, | |
78 # т.к. они просто освободятся несколько позже. | |
79 if ( not $request->context->{resource} ) { | |
80 $request->context->{resource} = $this; | |
81 } | |
82 | |
83 return _InvokeDelegate( $operation, $this, $request ); | |
84 } | |
85 | |
86 sub security { | |
87 shift->request->security | |
88 } | |
89 | |
90 sub context { | |
91 shift->request->context | |
92 } | |
93 | |
94 sub verbs { | |
95 {} # возвращаем пстой список операций | |
96 } | |
97 | |
98 sub GetAllowedMethods { | |
99 map( uc, keys %{ shift->verbs } ); | |
100 } | |
101 | |
102 sub AccessCheck { | |
103 | |
104 } | |
105 | |
106 sub Seek { | |
107 my ($this, $role) = @_; | |
108 | |
109 my @roles; | |
110 | |
111 if (ref($role) eq 'ARRAY') { | |
112 @roles = @{$role}; | |
113 } elsif (not ref($role)) { | |
114 @roles = split(/\s+/, $role); | |
115 } else { | |
116 die ArgumentException->new( role => 'A invalid value is provided, expected ARRAY or SCALAR'); | |
117 } | |
118 | |
119 | |
120 for(my $r = $this; $r; $r = $r->parent) { | |
121 return $r if $r->HasRole(@roles); | |
122 } | |
123 return; | |
124 } | |
125 | |
126 sub HasRole { | |
127 my ($this, @roles) = @_; | |
128 my %cache = map { $_, 1 } @{$this->role}; | |
129 return scalar(grep not($cache{$_}), @roles) ? 0 : 1; | |
130 } | |
131 | |
132 sub _InvokeDelegate { | |
133 my $delegate = shift; | |
134 | |
135 return $delegate->(@_) if ref $delegate eq 'CODE'; | |
136 return $delegate->Invoke(@_) if eval { $delegate->can('Invoke') }; | |
137 } | |
138 | |
139 1; | |
140 | |
141 __END__ | |
142 | |
143 =pod | |
144 | |
145 =head1 NAME | |
146 | |
147 C<IMPL::Web::Application::Resource> - Web-ресурс. | |
148 | |
149 =head1 SYNOPSIS | |
150 | |
151 Класс для внутреннего использования. Объединяет в себе контракт и модель данных. | |
152 Основная задача - обработать поступающий от контроллера запрос на вызов C<HTTP> | |
153 метода. | |
154 | |
155 Экземпляры данного класса передаются в качестве параметров делегатам | |
156 осуществляющим привязку к модели в C<IMPL::Web::Application::ResourceContract> | |
157 и C<IMPL::Web::Application::OperationContract>. | |
158 | |
159 =head1 DESCRIPTION | |
160 | |
161 Весь функционал ресурса, поддерживаемые им C<HTTP> методы определяются | |
162 контрактом. Однако можно реализовывать ресурсы, которые не имеют контракта | |
163 или он отличается от того, что предоставляется стандартно | |
164 C<IMPL::Web::Application::ResourceContract>. | |
165 | |
166 Каждый ресурс является контейнером, тоесть позволяет получить дочерний ресурс | |
167 по идентифифкатору, если таковой имеется, тоесть ресурс, у которого нет дочерних | |
168 ресурсов на самом деле рассматривается как пустой контейнер. | |
169 | |
170 С ресурсом непосредственно взаимодействует котроллер запросов | |
171 C<IMPL::Web::Handler::RestController>, вызывая два метода. | |
172 | |
173 =over | |
174 | |
175 =item * C<FetchChildResource($childId)> | |
176 | |
177 Данный метод возвращает дочерний ресурс, соответствующий C<$childId>. | |
178 Текущая реализация использует метод C<FindChildResourceInfo> контракта текущего | |
179 ресурса, после чего создает дочерний ресурс. | |
180 | |
181 Если дочерний ресурс не найден, вызывается исключение | |
182 C<IMPL::Web::NotFoundException>. | |
183 | |
184 =item * C<InvokeHttpVerb($verb,$action)> | |
185 | |
186 Обрабатывает запрос к ресурсу. Для этого используется контракт ресурса, в | |
187 нем выбирается соответсвующий C<IMPL::Web::Application::OperationContract>. | |
188 Затем найденный контракт для указанной операции используется для обработки | |
189 запроса. | |
190 | |
191 =back | |
192 | |
193 Если объект реализует два вышеуказанных метода, он является веб-ресурсом, а | |
194 детали его реализации, котнракт и прочее уже не важно, поэтому можно реализовать | |
195 собственный класс ресурса, например унаследованный от | |
196 C<IMPL::Web::Application::CustomResource>. | |
197 | |
198 =head1 MEMBERS | |
199 | |
200 =head2 C<[get]request> | |
201 | |
202 Объект C<IMPL::Web::Application::Action> представляющий запрос к серверу. | |
203 | |
204 =head2 C<[get]application> | |
205 | |
206 Ссылка на приложение, к которому относится данный ресурс. Получается | |
207 автоматически из объекта запроса. | |
208 | |
209 =head2 C<[get]contract> | |
210 | |
211 Обязательное свойство для ресурса, ссылается, на контракт, соответствующий | |
212 данному ресурсу, используется для выполнения C<HTTP> методов и получения | |
213 дочерних ресурсов. | |
214 | |
215 =head2 C<[get]id> | |
216 | |
217 Обязательное свойство ресурса, идентифицирует его в родительском контейнере, | |
218 для корневого ресурса может иметь произвольное значение. | |
219 | |
220 =head2 C<[get]parent> | |
221 | |
222 Ссылка на родительский ресурс, для корневого ресурса не определена. | |
223 | |
224 =head2 C<[get]model> | |
225 | |
226 Ссылка на объект предметной области, представляемый данным ресурсом. Данное | |
227 свойство не является обязательным и может быть не задано. | |
228 | |
229 =head2 C<[get]location> | |
230 | |
231 Объект типа C<IMPL::Web::AutoLocator> или аналогичный описывающий адрес текущего | |
232 ресурса, может быть как явно передан при создании ресурса, так и вычислен | |
233 автоматически (только для ресурсов имеющих родителя). Следует заметить, что | |
234 адрес ресурса не содержит параметров запроса, а только путь. | |
235 | |
236 =head2 C<[get,list]role> | |
237 | |
238 Список ролей ресурса. Роль это условный маркер, который позволяет определить | |
239 функции выполняемые ресурсом, например контейнер, профиль пользователя и т.п. | |
240 | |
241 Используется при построении цепочек навигации, а также при поиске с использованием | |
242 метода C<seek>. | |
243 | |
244 =head2 C<seek($role)> | |
245 | |
246 Ищет ресурс в цепочке родителей (включая сам ресурс) с подходящими ролями. | |
247 | |
248 Роли могут быть переданы в виде массива или строки, где роли разделены пробелами | |
249 | |
250 =head2 C<[get]FetchChildResource($id)> | |
251 | |
252 Возвращает дочерний ресурс, по его идентификатору. | |
253 | |
254 Данная реализация использует контракт текущего ресурса для поиска информации о | |
255 дочернем ресурсе C<< $this->contract->FindChildResourceInfo($id) >>. | |
256 | |
257 Затем осуществляется привязка к моделе, тоесть, выполняется делегат, для | |
258 получения модели дочернего ресурса, а затем осуществляется привязка к контракту, | |
259 при этом в делегат, который должен вернуть контракт дочернего ресурса передаются | |
260 текущий ресурc и модель дочернего ресурса. | |
261 | |
262 =cut |