Mercurial > pub > Impl
comparison Lib/IMPL/Web/Application/RestResource.pm @ 201:0c018a247c8a
Reworked REST resource classes to be more transparent and intuitive
| author | sergey |
|---|---|
| date | Tue, 24 Apr 2012 19:52:07 +0400 |
| parents | a9dbe534d236 |
| children | 5146e17a7b76 |
comparison
equal
deleted
inserted
replaced
| 200:a9dbe534d236 | 201:0c018a247c8a |
|---|---|
| 13 TTransform => '-IMPL::Transform', | 13 TTransform => '-IMPL::Transform', |
| 14 TResolve => '-IMPL::Config::Resolve', | 14 TResolve => '-IMPL::Config::Resolve', |
| 15 CustomResource => 'IMPL::Web::Application::CustomResource' | 15 CustomResource => 'IMPL::Web::Application::CustomResource' |
| 16 }, | 16 }, |
| 17 base => { | 17 base => { |
| 18 'IMPL::Web::Application::RestBaseResource' => '@_' | 18 'IMPL::Web::Application::RestCustomResource' => '@_' |
| 19 } | 19 } |
| 20 }; | 20 }; |
| 21 | 21 |
| 22 BEGIN { | 22 BEGIN { |
| 23 public property target => PROP_GET | PROP_OWNERSET; | 23 public property target => PROP_GET | PROP_OWNERSET; |
| 24 public property index => PROP_GET | PROP_OWNERSET; | |
| 25 public property fetch => PROP_GET | PROP_OWNERSET; | |
| 24 | 26 |
| 25 public property methods => PROP_GET | PROP_OWNERSET; | 27 public property methods => PROP_GET | PROP_OWNERSET; |
| 26 | 28 |
| 27 public property childRegex => PROP_GET | PROP_OWNERSET; | 29 public property childRegex => PROP_GET | PROP_OWNERSET; |
| 28 public property enableForms => PROP_GET | PROP_OWNERSET; | 30 public property enableForms => PROP_GET | PROP_OWNERSET; |
| 29 public property orphan => PROP_GET | PROP_OWNERSET; | 31 |
| 30 | |
| 31 public property listChildren => PROP_GET | PROP_OWNERSET; | |
| 32 public property fetchChild => PROP_GET | PROP_OWNERSET; | |
| 33 public property createChild => PROP_GET | PROP_OWNERSET; | |
| 34 public property updateChild => PROP_GET | PROP_OWNERSET; | |
| 35 public property deleteChild => PROP_GET | PROP_OWNERSET; | |
| 36 } | 32 } |
| 37 | 33 |
| 38 sub CTOR { | 34 sub CTOR { |
| 39 my ($this) = @_; | 35 my ($this) = @_; |
| 40 | 36 |
| 41 die ArgumentException->new("id","Identifier is required for non-root resources") if $this->id and not length $this->id; | |
| 42 die ArgumentException->new("target") unless $this->target; | 37 die ArgumentException->new("target") unless $this->target; |
| 43 die ArgumentException->new("A contract is required") unless $this->contract; | 38 |
| 39 $this->final($this->childRegex ? 0 : 1); | |
| 44 | 40 |
| 45 if ($this->enableForms) { | 41 if ($this->enableForms) { |
| 42 $this->methods->{create} = { | |
| 43 get => \&_ParentGet, | |
| 44 post => \&_ParentPost, | |
| 45 final => 1 # this resource doesn't have any children | |
| 46 }; | |
| 47 | |
| 48 $this->methods->{edit} = { | |
| 49 get => \&_ParentGet, | |
| 50 post => \&_ParentPut, | |
| 51 final => 1 # this resource doesn't have any children | |
| 52 }; | |
| 53 | |
| 54 $this->methods->{delete} { | |
| 55 get => \&_ParentGet, | |
| 56 post => \&_ParentDelete | |
| 57 final => 1 # this resource doesn't have any children | |
| 58 } | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 sub _ParentGet { | |
| 63 my ($this,$action) = @_; | |
| 64 return $this->parent->GetImpl($action); | |
| 65 } | |
| 66 | |
| 67 sub _ParentPut { | |
| 68 my ($this,$action) = @_; | |
| 69 return $this->parent->PutImpl($action); | |
| 70 } | |
| 71 | |
| 72 sub _ParentPost { | |
| 73 my ($this,$action) = @_; | |
| 74 return $this->parent->PostImpl($action); | |
| 75 } | |
| 76 | |
| 77 sub _ParentDelete { | |
| 78 my ($this,$action) = @_; | |
| 79 return $this->parent->DeleteImpl($action); | |
| 80 } | |
| 81 | |
| 82 sub FetchChildResource { | |
| 83 my ($this,$id,$action) = @_; | |
| 84 | |
| 85 my $rx = $this->childRegex; | |
| 86 | |
| 87 my $res; | |
| 88 | |
| 89 if (length $id == 0) { | |
| 46 | 90 |
| 47 } | 91 my $method = $this->index; |
| 48 } | 92 die ForbiddenException->new() unless $method; |
| 49 | |
| 50 sub GetImpl { | |
| 51 my ($this,$action) = @_; | |
| 52 | |
| 53 return $this->target; | |
| 54 } | |
| 55 | |
| 56 sub PutImpl { | |
| 57 my ($this,$action) = @_; | |
| 58 | |
| 59 die ForbiddenException->new() if $this->orhpan; | |
| 60 | |
| 61 $this->parent->UpdateImpl($this->id,$action); | |
| 62 } | |
| 63 | |
| 64 sub PostImpl { | |
| 65 my ($this,$id,$action) = @_; | |
| 66 | |
| 67 my $method; | |
| 68 | |
| 69 if (length $id == 0) { | |
| 70 $method = $this->insert or die ForbiddenException->new(); | |
| 71 | 93 |
| 72 $method = { | 94 $res = $this->InvokeMember($method,$action); |
| 73 method => $method, | 95 |
| 74 parameters => [qw(query)] | 96 } elsif (my $resource = $this->methods->{$id}) { |
| 75 } unless ref $method; | 97 return CustomResource->new( |
| 76 } elsif ($this->methods and $method = $this->methods->{$id}->{post}) { | 98 get => $resource->{get}, |
| 77 # we got method info | 99 post => $resource->{post}, |
| 100 put => $resource->{put}, | |
| 101 delete => $resource->{delete}, | |
| 102 parent => $this, | |
| 103 id => $id, | |
| 104 target => $this->target | |
| 105 ); | |
| 106 | |
| 107 } elsif ($rx and $id =~ m/^$rx$/ and $method = $this->fetch) { | |
| 108 $res = $this->InvokeMember($method,$action, { id => $id } ); | |
| 78 } else { | 109 } else { |
| 79 die ForbiddenException->new(); | 110 die ForbiddenException->new(); |
| 80 } | 111 } |
| 81 | 112 |
| 82 return $this->InvokeMemeber($method,$id,$action); | |
| 83 } | |
| 84 | |
| 85 sub DeleteImpl { | |
| 86 my ($this,$id,$action) = @_; | |
| 87 | |
| 88 my $rx = $this->childRegex; | |
| 89 if ($rx and $id =~ m/$rx/ and my $method = $this->delete) { | |
| 90 | |
| 91 $method = { | |
| 92 method => $method, | |
| 93 parameters => [qw(id)] | |
| 94 } unless ref $method; | |
| 95 | |
| 96 return $this->InvokeMember($method,$id,$action); | |
| 97 } else { | |
| 98 die ForbiddenException->new(); | |
| 99 } | |
| 100 } | |
| 101 | |
| 102 sub HttpFallbackImpl { | |
| 103 die ForbiddenException->new(); | |
| 104 } | |
| 105 | |
| 106 sub UpdateImpl { | |
| 107 my ($this,$id,$action) = @_; | |
| 108 | |
| 109 my $method = $this->updateChild or die ForbiddenException->new(); | |
| 110 $this->InvokeMember($method,$action); | |
| 111 } | |
| 112 | |
| 113 sub FetchChildResource { | |
| 114 my ($this,$id,$action) = @_; | |
| 115 | |
| 116 my $rx = $this->childRegex; | |
| 117 my $method; | |
| 118 my %params = ( | |
| 119 parent => $this, | |
| 120 id => $id | |
| 121 ); | |
| 122 | |
| 123 if (length $id == 0) { | |
| 124 | |
| 125 $method = $this->list; | |
| 126 die ForbiddenException->new() unless $method; | |
| 127 | |
| 128 return $this->contract->Transform( $this->InvokeMember($method,$id,$action), \%params ); | |
| 129 | |
| 130 } elsif ($method = $this->methods->{$id}) { | |
| 131 # поскольку данный объект был получен не как дочерний объект, | |
| 132 # а как выполнение метода, то для него не определены операции | |
| 133 # put и delete по умолчанию. | |
| 134 $params{orphan} = 1; | |
| 135 | |
| 136 return $this->contract->Transform( $this->InvokeMember($method,$id,$action), \%params ); | |
| 137 | |
| 138 } elsif ($rx and $id =~ m/^$rx$/ and $method = $this->fetch) { | |
| 139 # ok | |
| 140 } else { | |
| 141 die ForbiddenException->new(); | |
| 142 } | |
| 143 | |
| 144 my $res = $this->InvokeMember($method,$id,$action); | |
| 145 die NotFoundException->new() unless defined $res; | 113 die NotFoundException->new() unless defined $res; |
| 146 | 114 |
| 147 return $this->contract->Transform($res, {parent => $this, id => $id} ); | 115 return $this->contract->Transform($res, {parent => $this, id => $id} ); |
| 148 } | 116 } |
| 149 | |
| 150 sub InvokeMember { | |
| 151 my ($this,$method,$id,$action) = @_; | |
| 152 | |
| 153 die ArgumentException->new("method","No method information provided") unless $method; | |
| 154 | |
| 155 #normalize method info | |
| 156 if (not ref $method) { | |
| 157 $method = { | |
| 158 method => $method | |
| 159 }; | |
| 160 } | |
| 161 | |
| 162 if (ref $method eq 'HASH') { | |
| 163 my $member = $method->{method} or die InvalidOpException->new("A member name isn't specified"); | |
| 164 my @args; | |
| 165 | |
| 166 if (my $params = $method->{parameters}) { | |
| 167 if (ref $params eq 'HASH') { | |
| 168 @args = map { | |
| 169 $_, | |
| 170 $this->MakeParameter($params->{$_},$id,$action) | |
| 171 } keys %$params; | |
| 172 } elsif (ref $params eq 'ARRAY') { | |
| 173 @args = map $this->MakeParameter($_,$id,$action), @$params; | |
| 174 } else { | |
| 175 @args = ($this->MakeParameter($params,$id,$action)); | |
| 176 } | |
| 177 } | |
| 178 return $this->target->$member(@args); | |
| 179 } elsif (ref $method eq TResolve) { | |
| 180 return $method->Invoke($this->target); | |
| 181 } elsif (ref $method eq 'CODE') { | |
| 182 return $method->($this,$id,$action); | |
| 183 } else { | |
| 184 die InvalidOpException->new("Unsupported type of the method information", ref $method); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 sub MakeParameter { | |
| 189 my ($this,$param,$id,$action) = @_; | |
| 190 | |
| 191 if ($param) { | |
| 192 if (is $param, TTransform ) { | |
| 193 return $param->Transform($this,$action->query); | |
| 194 } elsif ($param and not ref $param) { | |
| 195 my %std = ( | |
| 196 id => $id, | |
| 197 action => $action, | |
| 198 query => $action->query | |
| 199 ); | |
| 200 | |
| 201 return $std{$param} || $action->query->param($param); | |
| 202 } | |
| 203 } else { | |
| 204 return undef; | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 | |
| 209 | |
| 210 | 117 |
| 211 1; | 118 1; |
| 212 | 119 |
| 213 __END__ | 120 __END__ |
| 214 | 121 |
