# HG changeset patch # User sergey # Date 1335282727 -14400 # Node ID 0c018a247c8a95695c9f5274aaa8ed7a788418a8 # Parent a9dbe534d236adaac52d0ba4f9557f2e7572358e Reworked REST resource classes to be more transparent and intuitive diff -r a9dbe534d236 -r 0c018a247c8a Lib/IMPL/Web/Application/RestBaseResource.pm --- a/Lib/IMPL/Web/Application/RestBaseResource.pm Tue Apr 24 02:34:49 2012 +0400 +++ b/Lib/IMPL/Web/Application/RestBaseResource.pm Tue Apr 24 19:52:07 2012 +0400 @@ -22,6 +22,7 @@ public property id => PROP_GET | PROP_OWNERSET; public property parent => PROP_GET | PROP_OWNERSET; public property contract => PROP_GET | PROP_OWNERSET; + protected property final => PROP_ALL; } sub target { @@ -49,11 +50,11 @@ } sub InvokeHttpMethod { - my ($this,$method,$childId,$action) = @_; + my ($this,$method,$action) = @_; my $impl = $this->GetHttpImpl($method) || 'HttpFallbackImpl'; - return $this->$impl($childId,$action); + return $this->$impl($action); } sub GetImpl { @@ -76,6 +77,10 @@ die ForbiddenException->new(); } +sub FetchChildResource { + return undef; +} + sub InvokeMember { my ($this,$method,$action) = @_; @@ -90,6 +95,7 @@ if (ref $method eq 'HASH') { my $member = $method->{method} or die InvalidOpException->new("A member name isn't specified"); + my @args; if (my $params = $method->{parameters}) { @@ -108,7 +114,7 @@ } elsif (ref $method eq TResolve) { return $method->Invoke($this->target); } elsif (ref $method eq 'CODE') { - return $method->($this->target,$action); + return $method->($this,$action); } else { die InvalidOpException->new("Unsupported type of the method information", ref $method); } @@ -121,13 +127,9 @@ if (is $param, TTransform ) { return $param->Transform($this,$action->query); } elsif ($param and not ref $param) { - my %std = ( - id => $this->id, - action => $action, - query => $action->query - ); - - return $std{$param} || $action->query->param($param); + return $action->query->param($param); + } else { + die new InvalidOpException->new("Unsupported parameter mapping", $param); } } else { return undef; @@ -135,4 +137,12 @@ } -1; \ No newline at end of file +1; + +__END__ + +=pod + + + +=cut \ No newline at end of file diff -r a9dbe534d236 -r 0c018a247c8a Lib/IMPL/Web/Application/RestCustomResource.pm --- a/Lib/IMPL/Web/Application/RestCustomResource.pm Tue Apr 24 02:34:49 2012 +0400 +++ b/Lib/IMPL/Web/Application/RestCustomResource.pm Tue Apr 24 19:52:07 2012 +0400 @@ -6,7 +6,8 @@ require => { Exception => "IMPL::Exception", ArgumentException => '-IMPL::InvalidArgumentException', - ForbiddenException => 'IMPL::Web::ForbiddenException' + ForbiddenException => 'IMPL::Web::ForbiddenException', + NotFoundException => 'IMPL::Web::NotFoundException' }, base => { 'IMPL::Web::Application::RestBaseResource' => '@_' @@ -29,6 +30,8 @@ sub FetchChildResource { my ($this,$id,$action) = @_; + die NotFoundException->new() if $this->final; + return $this->contract->Transform( $this->GetImpl($action), { parent => $this, id => $id } )->FetchChildResource($id,$action); } @@ -36,25 +39,25 @@ my ($this,$action) = @_; my $method = $this->get or die ForbiddenException->new(); - return $this->$method($action); + return $this->InvokeMember($method,$action); } sub PutImpl { my ($this,$action) = @_; my $method = $this->put or die ForbiddenException->new(); - return $this->$method($action); + return $this->InvokeMember($method,$action); } sub PostImpl { my ($this,$action) = @_; my $method = $this->post or die ForbiddenException->new(); - return $this->$method($action); + return $this->InvokeMember($method,$action); } sub DeleteImpl { my ($this,$action) = @_; my $method = $this->delete or die ForbiddenException->new(); - return $this->$method($action); + return $this->InvokeMember($method,$action); } 1; \ No newline at end of file diff -r a9dbe534d236 -r 0c018a247c8a Lib/IMPL/Web/Application/RestResource.pm --- a/Lib/IMPL/Web/Application/RestResource.pm Tue Apr 24 02:34:49 2012 +0400 +++ b/Lib/IMPL/Web/Application/RestResource.pm Tue Apr 24 19:52:07 2012 +0400 @@ -15,199 +15,106 @@ CustomResource => 'IMPL::Web::Application::CustomResource' }, base => { - 'IMPL::Web::Application::RestBaseResource' => '@_' + 'IMPL::Web::Application::RestCustomResource' => '@_' } }; BEGIN { public property target => PROP_GET | PROP_OWNERSET; + public property index => PROP_GET | PROP_OWNERSET; + public property fetch => 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 orphan => PROP_GET | PROP_OWNERSET; - public property listChildren => PROP_GET | PROP_OWNERSET; - public property fetchChild => PROP_GET | PROP_OWNERSET; - public property createChild => PROP_GET | PROP_OWNERSET; - public property updateChild => PROP_GET | PROP_OWNERSET; - public property deleteChild => PROP_GET | PROP_OWNERSET; } 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; - die ArgumentException->new("A contract is required") unless $this->contract; + + $this->final($this->childRegex ? 0 : 1); if ($this->enableForms) { - + $this->methods->{create} = { + get => \&_ParentGet, + post => \&_ParentPost, + final => 1 # this resource doesn't have any children + }; + + $this->methods->{edit} = { + get => \&_ParentGet, + post => \&_ParentPut, + final => 1 # this resource doesn't have any children + }; + + $this->methods->{delete} { + get => \&_ParentGet, + post => \&_ParentDelete + final => 1 # this resource doesn't have any children + } } } -sub GetImpl { - my ($this,$action) = @_; - - return $this->target; -} - -sub PutImpl { +sub _ParentGet { my ($this,$action) = @_; - - die ForbiddenException->new() if $this->orhpan; - - $this->parent->UpdateImpl($this->id,$action); + return $this->parent->GetImpl($action); } -sub PostImpl { - my ($this,$id,$action) = @_; - - my $method; - - if (length $id == 0) { - $method = $this->insert or die ForbiddenException->new(); - - $method = { - method => $method, - parameters => [qw(query)] - } unless ref $method; - } elsif ($this->methods and $method = $this->methods->{$id}->{post}) { - # we got method info - } else { - die ForbiddenException->new(); - } - - return $this->InvokeMemeber($method,$id,$action); +sub _ParentPut { + my ($this,$action) = @_; + return $this->parent->PutImpl($action); } -sub DeleteImpl { - my ($this,$id,$action) = @_; - - my $rx = $this->childRegex; - if ($rx and $id =~ m/$rx/ and my $method = $this->delete) { - - $method = { - method => $method, - parameters => [qw(id)] - } unless ref $method; - - return $this->InvokeMember($method,$id,$action); - } else { - die ForbiddenException->new(); - } +sub _ParentPost { + my ($this,$action) = @_; + return $this->parent->PostImpl($action); } -sub HttpFallbackImpl { - die ForbiddenException->new(); -} - -sub UpdateImpl { - my ($this,$id,$action) = @_; - - my $method = $this->updateChild or die ForbiddenException->new(); - $this->InvokeMember($method,$action); +sub _ParentDelete { + my ($this,$action) = @_; + return $this->parent->DeleteImpl($action); } sub FetchChildResource { my ($this,$id,$action) = @_; my $rx = $this->childRegex; - my $method; - my %params = ( - parent => $this, - id => $id - ); + + my $res; if (length $id == 0) { - $method = $this->list; + my $method = $this->index; die ForbiddenException->new() unless $method; - return $this->contract->Transform( $this->InvokeMember($method,$id,$action), \%params ); + $res = $this->InvokeMember($method,$action); - } elsif ($method = $this->methods->{$id}) { - # поскольку данный объект был получен не как дочерний объект, - # а как выполнение метода, то для него не определены операции - # put и delete по умолчанию. - $params{orphan} = 1; - - return $this->contract->Transform( $this->InvokeMember($method,$id,$action), \%params ); + } elsif (my $resource = $this->methods->{$id}) { + return CustomResource->new( + get => $resource->{get}, + post => $resource->{post}, + put => $resource->{put}, + delete => $resource->{delete}, + parent => $this, + id => $id, + target => $this->target + ); } elsif ($rx and $id =~ m/^$rx$/ and $method = $this->fetch) { - # ok + $res = $this->InvokeMember($method,$action, { id => $id } ); } else { die ForbiddenException->new(); } - my $res = $this->InvokeMember($method,$id,$action); die NotFoundException->new() unless defined $res; return $this->contract->Transform($res, {parent => $this, id => $id} ); } -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 = { - method => $method - }; - } - - if (ref $method eq 'HASH') { - my $member = $method->{method} or die InvalidOpException->new("A member name isn't specified"); - my @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); - } -} - -sub MakeParameter { - my ($this,$param,$id,$action) = @_; - - if ($param) { - if (is $param, TTransform ) { - return $param->Transform($this,$action->query); - } elsif ($param and not ref $param) { - my %std = ( - id => $id, - action => $action, - query => $action->query - ); - - return $std{$param} || $action->query->param($param); - } - } else { - return undef; - } -} - - - - 1; __END__