changeset 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
files Lib/IMPL/Web/Application/RestBaseResource.pm Lib/IMPL/Web/Application/RestCustomResource.pm Lib/IMPL/Web/Application/RestResource.pm
diffstat 3 files changed, 77 insertions(+), 157 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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
--- 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__