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