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 |
