Mercurial > pub > Impl
diff 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 |
line wrap: on
line diff
--- a/Lib/IMPL/Web/Application/RestResource.pm Fri Apr 20 16:06:36 2012 +0400 +++ b/Lib/IMPL/Web/Application/RestResource.pm Mon Apr 23 01:36:52 2012 +0400 @@ -9,7 +9,8 @@ ForbiddenException => 'IMPL::Web::ForbiddenException', InvalidOpException => '-IMPL::InvalidOperationException', ArgumentException => '-IMPL::InvalidArgumentException', - TTransform => '-IMPL::Transform' + TTransform => '-IMPL::Transform', + TResolve => '-IMPL::Config::Resolve' }, base => { 'IMPL::Object' => undef, @@ -18,9 +19,12 @@ }; BEGIN { + public property id => PROP_GET | PROP_OWNERSET; public property target => PROP_GET | PROP_OWNERSET; + public property parent => PROP_GET | PROP_OWNERSET; public property methods => PROP_GET | PROP_OWNERSET; public property childRegex => PROP_GET | PROP_OWNERSET; + public property enableForms => PROP_GET | PROP_OWNERSET; public property list => PROP_GET | PROP_OWNERSET; public property fetch => PROP_GET | PROP_OWNERSET; public property insert => PROP_GET | PROP_OWNERSET; @@ -31,7 +35,47 @@ sub CTOR { my ($this) = @_; + die ArgumentException->new("id","Identifier is required for non-root resources") if $this->id and not length $this->id; die ArgumentException->new("target") unless $this->target; + + if ($this->enableForms && $this->parent) { + $this->methods({}) unless $this->methods; + + if ($this->insert) { + $this->methods->{create} = { + get => sub { + my ($that,$id,$action) = @_; + return $that->target; + } + }; + } + + if ($this->parent->update) { + $this->methods->{edit} = { + get => sub { + my ($that,$id,$action) = @_; + return $that->target; + }, + post => sub { + my ($that,$id,$action) = @_; + return $that->parent->PutImpl($that->id,$action); + } + }; + } + + if ($this->parent->delete) { + $this->methods->{delete} = { + get => sub { + my ($that,$id,$action) = @_; + return $that->target; + }, + post => sub { + my ($that,$id,$action) = @_; + return $that->parent->DeleteImpl($that->id,$action); + } + }; + } + } } sub GetHttpImpl { @@ -62,10 +106,8 @@ my $method; if (length $id == 0) { $method = $this->list or die ForbiddenException->new(); - } elsif ($this->methods and $method = $this->methods->{$id}) { - if (ref $method eq 'HASH' and not $method->{allowGet}) { - die ForbiddenException->new(); - } + } elsif ($this->methods and $method = $this->methods->{$id}->{get}) { + # we got method info } elsif($rx = $this->childRegex and $id =~ m/$rx/ ) { $method = $this->fetch or die ForbiddenException->new(); @@ -74,6 +116,8 @@ parameters => [qw(id)] } unless ref $method; + } else { + die ForbiddenException->new(); } return $this->InvokeMember($method,$id,$action); @@ -109,8 +153,8 @@ method => $method, parameters => [qw(query)] } unless ref $method; - } elsif ($method = $this->methods->{$id}) { - die ForbiddenException->new() unless ref $method and $method->{allowPost}; + } elsif ($this->methods and $method = $this->methods->{$id}->{post}) { + # we got method info } else { die ForbiddenException->new(); } @@ -142,6 +186,8 @@ sub InvokeMember { my ($this,$method,$id,$action) = @_; + die ArgumentException->new("method","No method information provided") unless $method; + #normalize method info if (not ref $method) { $method = { @@ -150,21 +196,26 @@ } if (ref $method eq 'HASH') { + my $member = $method->{method} or die InvalidOpException->new("A member name isn't specified"); my @args; - my $member = $method->{method} or die InvalidOpException->new("A member name isn't specified"); - if (my $params = $method->{parameters}) { - if (ref $params eq 'HASH') { - @args = map { - $_, - $this->MakeParameter($params->{$_},$id,$action) - } keys %$params; - } elsif (ref $params eq 'ARRAY') { - @args = map $this->MakeParameter($_,$id,$action), @$params; - } else { - @args = ($this->MakeParameter($params,$id,$action)); - } - } - $this->target->$member(@args); + + if (my $params = $method->{parameters}) { + if (ref $params eq 'HASH') { + @args = map { + $_, + $this->MakeParameter($params->{$_},$id,$action) + } keys %$params; + } elsif (ref $params eq 'ARRAY') { + @args = map $this->MakeParameter($_,$id,$action), @$params; + } else { + @args = ($this->MakeParameter($params,$id,$action)); + } + } + return $this->target->$member(@args); + } elsif (ref $method eq TResolve) { + return $method->Invoke($this->target); + } elsif (ref $method eq 'CODE') { + return $method->($this,$id,$action); } else { die InvalidOpException->new("Unsupported type of the method information", ref $method); } @@ -255,10 +306,20 @@ { methods => { history => { - allowGet => 1, - method => 'GetHistory', - parameters => [qw(from to)] + get => { + method => 'GetHistory', + parameters => [qw(from to)] + }, }, + rating => { + get => { + method => 'GetRating' + } + post => { + method => 'Vote', + parameters => [qw(id rating comment)] + } + } } list => 'search', fetch => 'GetItemById' @@ -309,12 +370,51 @@ Вызывает метод C<method>, в отличии от C<GET> методы опубликованные через C<POST> могут вносить изменения в объекты. +=head1 BROWSER COMPATIBILITY + +Однако существует проблема с браузерами, поскольку тег C<< <form> >> реализет только методы +C<GET,POST>. Для решения данной проблемы используется режим совместимости C<compatible>. В +случае когда данный режим активен, автоматически публикуются дочерние C<create,edit,delete>. + +=head2 C<GET create> + +Возвращает C<target>. + +=head2 C<POST create> + +Вызывает метод C<PostImpl> передавая ему свои параметры. + +=head2 C<GET edit> + +Возвращает C<target>. + +=head2 C<POST edit> + +Вызывает метод C<$this->parent->PutImpl($this->id)> передавая ему свои параметры. + +=head2 C<GET delete>. + +Возвращает C<target>. + +=head2 C<POST delete>. + +Вызывает метод C<$this->parent->DeleteImpl($this->id)> передавая ему свои параметры. + =head1 MEMBERS +=head2 C<[get]id> + +Идентификатор текущего ресурса. + =head2 C<[get]target> Объект (также может быть и класс), обеспечивающий функционал ресурса. +=head2 C<[get]parent> + +Родительский ресурс, в котором находится текущий ресурс. Может быть C<undef>, +если текущий ресурс является корнем. + =head2 C<[get]methods> Содержит описания методов, которые будут публиковаться как дочерние ресурсы.