Mercurial > pub > Impl
comparison Lib/IMPL/Web/Application/RestResource.pm @ 199:e743a8481327
Added REST support for forms (with only get and post methods)
author | sergey |
---|---|
date | Mon, 23 Apr 2012 01:36:52 +0400 |
parents | 2ffe6f661605 |
children | a9dbe534d236 |
comparison
equal
deleted
inserted
replaced
198:2ffe6f661605 | 199:e743a8481327 |
---|---|
7 use IMPL::declare { | 7 use IMPL::declare { |
8 require => { | 8 require => { |
9 ForbiddenException => 'IMPL::Web::ForbiddenException', | 9 ForbiddenException => 'IMPL::Web::ForbiddenException', |
10 InvalidOpException => '-IMPL::InvalidOperationException', | 10 InvalidOpException => '-IMPL::InvalidOperationException', |
11 ArgumentException => '-IMPL::InvalidArgumentException', | 11 ArgumentException => '-IMPL::InvalidArgumentException', |
12 TTransform => '-IMPL::Transform' | 12 TTransform => '-IMPL::Transform', |
13 TResolve => '-IMPL::Config::Resolve' | |
13 }, | 14 }, |
14 base => { | 15 base => { |
15 'IMPL::Object' => undef, | 16 'IMPL::Object' => undef, |
16 'IMPL::Object::Autofill' => '@_' | 17 'IMPL::Object::Autofill' => '@_' |
17 } | 18 } |
18 }; | 19 }; |
19 | 20 |
20 BEGIN { | 21 BEGIN { |
22 public property id => PROP_GET | PROP_OWNERSET; | |
21 public property target => PROP_GET | PROP_OWNERSET; | 23 public property target => PROP_GET | PROP_OWNERSET; |
24 public property parent => PROP_GET | PROP_OWNERSET; | |
22 public property methods => PROP_GET | PROP_OWNERSET; | 25 public property methods => PROP_GET | PROP_OWNERSET; |
23 public property childRegex => PROP_GET | PROP_OWNERSET; | 26 public property childRegex => PROP_GET | PROP_OWNERSET; |
27 public property enableForms => PROP_GET | PROP_OWNERSET; | |
24 public property list => PROP_GET | PROP_OWNERSET; | 28 public property list => PROP_GET | PROP_OWNERSET; |
25 public property fetch => PROP_GET | PROP_OWNERSET; | 29 public property fetch => PROP_GET | PROP_OWNERSET; |
26 public property insert => PROP_GET | PROP_OWNERSET; | 30 public property insert => PROP_GET | PROP_OWNERSET; |
27 public property update => PROP_GET | PROP_OWNERSET; | 31 public property update => PROP_GET | PROP_OWNERSET; |
28 public property delete => PROP_GET | PROP_OWNERSET; | 32 public property delete => PROP_GET | PROP_OWNERSET; |
29 } | 33 } |
30 | 34 |
31 sub CTOR { | 35 sub CTOR { |
32 my ($this) = @_; | 36 my ($this) = @_; |
33 | 37 |
38 die ArgumentException->new("id","Identifier is required for non-root resources") if $this->id and not length $this->id; | |
34 die ArgumentException->new("target") unless $this->target; | 39 die ArgumentException->new("target") unless $this->target; |
40 | |
41 if ($this->enableForms && $this->parent) { | |
42 $this->methods({}) unless $this->methods; | |
43 | |
44 if ($this->insert) { | |
45 $this->methods->{create} = { | |
46 get => sub { | |
47 my ($that,$id,$action) = @_; | |
48 return $that->target; | |
49 } | |
50 }; | |
51 } | |
52 | |
53 if ($this->parent->update) { | |
54 $this->methods->{edit} = { | |
55 get => sub { | |
56 my ($that,$id,$action) = @_; | |
57 return $that->target; | |
58 }, | |
59 post => sub { | |
60 my ($that,$id,$action) = @_; | |
61 return $that->parent->PutImpl($that->id,$action); | |
62 } | |
63 }; | |
64 } | |
65 | |
66 if ($this->parent->delete) { | |
67 $this->methods->{delete} = { | |
68 get => sub { | |
69 my ($that,$id,$action) = @_; | |
70 return $that->target; | |
71 }, | |
72 post => sub { | |
73 my ($that,$id,$action) = @_; | |
74 return $that->parent->DeleteImpl($that->id,$action); | |
75 } | |
76 }; | |
77 } | |
78 } | |
35 } | 79 } |
36 | 80 |
37 sub GetHttpImpl { | 81 sub GetHttpImpl { |
38 my($this,$method) = @_; | 82 my($this,$method) = @_; |
39 | 83 |
60 | 104 |
61 my $rx; | 105 my $rx; |
62 my $method; | 106 my $method; |
63 if (length $id == 0) { | 107 if (length $id == 0) { |
64 $method = $this->list or die ForbiddenException->new(); | 108 $method = $this->list or die ForbiddenException->new(); |
65 } elsif ($this->methods and $method = $this->methods->{$id}) { | 109 } elsif ($this->methods and $method = $this->methods->{$id}->{get}) { |
66 if (ref $method eq 'HASH' and not $method->{allowGet}) { | 110 # we got method info |
67 die ForbiddenException->new(); | |
68 } | |
69 } elsif($rx = $this->childRegex and $id =~ m/$rx/ ) { | 111 } elsif($rx = $this->childRegex and $id =~ m/$rx/ ) { |
70 $method = $this->fetch or die ForbiddenException->new(); | 112 $method = $this->fetch or die ForbiddenException->new(); |
71 | 113 |
72 $method = { | 114 $method = { |
73 method => $method, | 115 method => $method, |
74 parameters => [qw(id)] | 116 parameters => [qw(id)] |
75 } unless ref $method; | 117 } unless ref $method; |
76 | 118 |
119 } else { | |
120 die ForbiddenException->new(); | |
77 } | 121 } |
78 | 122 |
79 return $this->InvokeMember($method,$id,$action); | 123 return $this->InvokeMember($method,$id,$action); |
80 } | 124 } |
81 | 125 |
107 | 151 |
108 $method = { | 152 $method = { |
109 method => $method, | 153 method => $method, |
110 parameters => [qw(query)] | 154 parameters => [qw(query)] |
111 } unless ref $method; | 155 } unless ref $method; |
112 } elsif ($method = $this->methods->{$id}) { | 156 } elsif ($this->methods and $method = $this->methods->{$id}->{post}) { |
113 die ForbiddenException->new() unless ref $method and $method->{allowPost}; | 157 # we got method info |
114 } else { | 158 } else { |
115 die ForbiddenException->new(); | 159 die ForbiddenException->new(); |
116 } | 160 } |
117 | 161 |
118 return $this->InvokeMemeber($method,$id,$action); | 162 return $this->InvokeMemeber($method,$id,$action); |
139 die ForbiddenException->new(); | 183 die ForbiddenException->new(); |
140 } | 184 } |
141 | 185 |
142 sub InvokeMember { | 186 sub InvokeMember { |
143 my ($this,$method,$id,$action) = @_; | 187 my ($this,$method,$id,$action) = @_; |
188 | |
189 die ArgumentException->new("method","No method information provided") unless $method; | |
144 | 190 |
145 #normalize method info | 191 #normalize method info |
146 if (not ref $method) { | 192 if (not ref $method) { |
147 $method = { | 193 $method = { |
148 method => $method | 194 method => $method |
149 }; | 195 }; |
150 } | 196 } |
151 | 197 |
152 if (ref $method eq 'HASH') { | 198 if (ref $method eq 'HASH') { |
199 my $member = $method->{method} or die InvalidOpException->new("A member name isn't specified"); | |
153 my @args; | 200 my @args; |
154 my $member = $method->{method} or die InvalidOpException->new("A member name isn't specified"); | 201 |
155 if (my $params = $method->{parameters}) { | 202 if (my $params = $method->{parameters}) { |
156 if (ref $params eq 'HASH') { | 203 if (ref $params eq 'HASH') { |
157 @args = map { | 204 @args = map { |
158 $_, | 205 $_, |
159 $this->MakeParameter($params->{$_},$id,$action) | 206 $this->MakeParameter($params->{$_},$id,$action) |
160 } keys %$params; | 207 } keys %$params; |
161 } elsif (ref $params eq 'ARRAY') { | 208 } elsif (ref $params eq 'ARRAY') { |
162 @args = map $this->MakeParameter($_,$id,$action), @$params; | 209 @args = map $this->MakeParameter($_,$id,$action), @$params; |
163 } else { | 210 } else { |
164 @args = ($this->MakeParameter($params,$id,$action)); | 211 @args = ($this->MakeParameter($params,$id,$action)); |
165 } | 212 } |
166 } | 213 } |
167 $this->target->$member(@args); | 214 return $this->target->$member(@args); |
215 } elsif (ref $method eq TResolve) { | |
216 return $method->Invoke($this->target); | |
217 } elsif (ref $method eq 'CODE') { | |
218 return $method->($this,$id,$action); | |
168 } else { | 219 } else { |
169 die InvalidOpException->new("Unsupported type of the method information", ref $method); | 220 die InvalidOpException->new("Unsupported type of the method information", ref $method); |
170 } | 221 } |
171 } | 222 } |
172 | 223 |
253 my $cds = TRes->new( | 304 my $cds = TRes->new( |
254 DataContext->Default, | 305 DataContext->Default, |
255 { | 306 { |
256 methods => { | 307 methods => { |
257 history => { | 308 history => { |
258 allowGet => 1, | 309 get => { |
259 method => 'GetHistory', | 310 method => 'GetHistory', |
260 parameters => [qw(from to)] | 311 parameters => [qw(from to)] |
312 }, | |
261 }, | 313 }, |
314 rating => { | |
315 get => { | |
316 method => 'GetRating' | |
317 } | |
318 post => { | |
319 method => 'Vote', | |
320 parameters => [qw(id rating comment)] | |
321 } | |
322 } | |
262 } | 323 } |
263 list => 'search', | 324 list => 'search', |
264 fetch => 'GetItemById' | 325 fetch => 'GetItemById' |
265 } | 326 } |
266 ); | 327 ); |
307 =head3 C<POST {method}> | 368 =head3 C<POST {method}> |
308 | 369 |
309 Вызывает метод C<method>, в отличии от C<GET> методы опубликованные через C<POST> могут вносить | 370 Вызывает метод C<method>, в отличии от C<GET> методы опубликованные через C<POST> могут вносить |
310 изменения в объекты. | 371 изменения в объекты. |
311 | 372 |
373 =head1 BROWSER COMPATIBILITY | |
374 | |
375 Однако существует проблема с браузерами, поскольку тег C<< <form> >> реализет только методы | |
376 C<GET,POST>. Для решения данной проблемы используется режим совместимости C<compatible>. В | |
377 случае когда данный режим активен, автоматически публикуются дочерние C<create,edit,delete>. | |
378 | |
379 =head2 C<GET create> | |
380 | |
381 Возвращает C<target>. | |
382 | |
383 =head2 C<POST create> | |
384 | |
385 Вызывает метод C<PostImpl> передавая ему свои параметры. | |
386 | |
387 =head2 C<GET edit> | |
388 | |
389 Возвращает C<target>. | |
390 | |
391 =head2 C<POST edit> | |
392 | |
393 Вызывает метод C<$this->parent->PutImpl($this->id)> передавая ему свои параметры. | |
394 | |
395 =head2 C<GET delete>. | |
396 | |
397 Возвращает C<target>. | |
398 | |
399 =head2 C<POST delete>. | |
400 | |
401 Вызывает метод C<$this->parent->DeleteImpl($this->id)> передавая ему свои параметры. | |
402 | |
312 =head1 MEMBERS | 403 =head1 MEMBERS |
313 | 404 |
405 =head2 C<[get]id> | |
406 | |
407 Идентификатор текущего ресурса. | |
408 | |
314 =head2 C<[get]target> | 409 =head2 C<[get]target> |
315 | 410 |
316 Объект (также может быть и класс), обеспечивающий функционал ресурса. | 411 Объект (также может быть и класс), обеспечивающий функционал ресурса. |
412 | |
413 =head2 C<[get]parent> | |
414 | |
415 Родительский ресурс, в котором находится текущий ресурс. Может быть C<undef>, | |
416 если текущий ресурс является корнем. | |
317 | 417 |
318 =head2 C<[get]methods> | 418 =head2 C<[get]methods> |
319 | 419 |
320 Содержит описания методов, которые будут публиковаться как дочерние ресурсы. | 420 Содержит описания методов, которые будут публиковаться как дочерние ресурсы. |
321 | 421 |