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