changeset 332:04a093f0a5a6

IMPL::Web::Application refactoring: resources are created per client request
author cin
date Sun, 09 Jun 2013 21:48:57 +0400
parents 2ff1726c066c
children cd6409f66a5f
files Lib/IMPL/Web/Application/CustomResource.pm Lib/IMPL/Web/Application/Resource.pm Lib/IMPL/Web/Application/ResourceContract.pm Lib/IMPL/Web/Handler/RestController.pm
diffstat 4 files changed, 131 insertions(+), 119 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/Web/Application/CustomResource.pm	Wed Jun 05 18:21:11 2013 +0400
+++ b/Lib/IMPL/Web/Application/CustomResource.pm	Sun Jun 09 21:48:57 2013 +0400
@@ -111,6 +111,19 @@
     $this->model->update( $form->Bind($action) );
 }
 
+our %COMPONENTS = (
+	item => {
+		verbs => {
+			get => sub {
+				shift->model;
+			}
+		},
+		resources => [
+			edit => 
+		]
+	}
+);
+
 =end code
 
 =head1 MEMBERS
--- a/Lib/IMPL/Web/Application/Resource.pm	Wed Jun 05 18:21:11 2013 +0400
+++ b/Lib/IMPL/Web/Application/Resource.pm	Sun Jun 09 21:48:57 2013 +0400
@@ -4,132 +4,146 @@
 use URI;
 use IMPL::Const qw(:prop);
 use IMPL::declare {
-    require => {
-        ViewResult          => 'IMPL::Web::ViewResult',
-        Exception           => 'IMPL::Exception',
-        ArgumentException   => '-IMPL::InvalidArgumentException',
-        OperationException  => '-IMPL::InvalidOperationException',
-        NotAllowedException => 'IMPL::Web::NotAllowedException',
-        NotFoundException   => 'IMPL::Web::NotFoundException'
-      },
-      base => [
-        'IMPL::Object'                              => undef,
-        'IMPL::Web::Application::ResourceInterface' => undef
-      ],
-      props => [
-        application => PROP_RO,
-        parent      => PROP_RO,
-        model       => PROP_RO,
-        id          => PROP_RO,
-        contract    => PROP_RO,
-        location    => PROP_RO,
-      ]
+	require => {
+		ViewResult          => 'IMPL::Web::ViewResult',
+		Exception           => 'IMPL::Exception',
+		ArgumentException   => '-IMPL::InvalidArgumentException',
+		OperationException  => '-IMPL::InvalidOperationException',
+		NotAllowedException => 'IMPL::Web::NotAllowedException',
+		NotFoundException   => 'IMPL::Web::NotFoundException'
+	  },
+	  base => [
+		'IMPL::Object'                              => undef,
+		'IMPL::Web::Application::ResourceInterface' => undef
+	  ],
+	  props => [
+		request      => PROP_RO,
+		application => PROP_RO,
+		parent      => PROP_RO,
+		model       => PROP_RO,
+		id          => PROP_RO,
+		contract    => PROP_RO,
+		location    => PROP_RO,
+	  ]
 };
 
 sub CTOR {
-    my ( $this, %args ) = @_;
+	my ( $this, %args ) = @_;
+
+	die ArgumentException->new( id => 'A resource identifier is required' )
+	  unless $args{id};
+	die ArgumentException->new( contract => 'A contract is required' )
+	  unless $args{contract};
 
-    die ArgumentException->new( id => 'A resource identifier is required' )
-      unless $args{id};
-    die ArgumentException->new( contract => 'A contract is required' )
-      unless $args{contract};
-
-    $this->parent( $args{parent} );
-    $this->model( $args{model} );
-    $this->id( $args{id} );
-    $this->contract( $args{contract} );
-    $this->application( $args{application} || ($args{parent} && $args{parent}->application) );
+	$this->request($args{action})
+		or die ArgumentException->new(request => 'A request object must be specified');
+	$this->parent( $args{parent} );
+	$this->model( $args{model} );
+	$this->id( $args{id} );
+	$this->contract( $args{contract} );
+	$this->application( $args{action}{application} );
 
 # если расположение явно не указано, то оно вычисляется автоматически,
 # либо остается не заданным
-    $this->location( $args{location}
-          || eval { $this->parent->location->Child( $this->id ) } );
+	$this->location( $args{location}
+		  || eval { $this->parent->location->Child( $this->id ) } );
 }
 
 sub InvokeHttpVerb {
-    my ( $this, $verb, $action ) = @_;
+	my ( $this, $verb ) = @_;
 
-    my $operation = $this->contract->verbs->{ lc($verb) };
+	my $operation = $this->contract->verbs->{ lc($verb) };
 
-    die NotAllowedException->new(
-        allow => join( ',', map( uc, keys %{ $this->contract->verbs } ) ) )
-      unless $operation;
-      
-    $this->AccessCheck($verb);
+	die NotAllowedException->new(
+		allow => join( ',', map( uc, keys %{ $this->contract->verbs } ) ) )
+	  unless $operation;
+
+	$this->AccessCheck($verb);
+	my $request = $this->request;
 
 # в случае, когда один ресурс вызывает HTTP метод другого ресурса, нужно
 # сохранить оригинальный resourceLocation
-    $action->context->{resourceLocation} ||= $this->location;
-    
-    # это свойство специфично только для REST приложений.
-    # сохранение текущего ресурса не повлечет за собой существенных расходов,
-    # т.к. они просто освободятся несколько позже.
-    if(not $action->context->{resource}) { 
-        $action->context->{resource} = $this;
-        $action->context->{environment} = sub { $this->PrepareEnvironment() };
-    }
+	$request->context->{resourceLocation} ||= $this->location;
 
-    return _InvokeDelegate($operation, $this, $action );
+# это свойство специфично только для REST приложений.
+# сохранение текущего ресурса не повлечет за собой существенных расходов,
+# т.к. они просто освободятся несколько позже.
+	if ( not $request->context->{resource} ) {
+		$request->context->{resource} = $this;
+		$request->context->{environment} = sub { $this->PrepareEnvironment() };
+	}
+
+	return _InvokeDelegate( $operation, $this, $request );
 }
 
 sub AccessCheck {
-	
+
 }
 
 sub PrepareEnvironment {
-    my ($this) = @_;
-    
-    my @stack;
-    my $env = {};
-    
-    for(my $res = $this; $res; $res = $res->parent) {
-        push @stack,$res if $res->can('SetupEnvironment');
-    }
-    
-    map $_->SetupEnvironment($env), reverse @stack; 
-    
-    return $env;
+	my ($this) = @_;
+
+	my @stack;
+	my $env = {};
+
+	for ( my $res = $this ; $res ; $res = $res->parent ) {
+		push @stack, $res if $res->can('SetupEnvironment');
+	}
+
+	map $_->SetupEnvironment($env), reverse @stack;
+
+	return $env;
+}
+
+sub FindChildResourceInfo {
+	my ($this,$resourceId) = @_;
+	return $this->contract->FindChildResourceInfo($resourceId);
 }
 
 # это реализация по умолчанию, базируется информации о ресурсах, содержащийся
 # в контракте.
 sub FetchChildResource {
-    my ( $this, $childId ) = @_;
-    
-    $this->AccessCheck('FETCH');
+	my ( $this, $childId ) = @_;
+
+	$this->AccessCheck('FETCH');
 
-    my ( $info, $childIdParts ) =
-      $this->contract->FindChildResourceInfo($childId);
+	my ( $info, $childIdParts ) =
+	  $this->FindChildResourceInfo($childId);
 
-    die NotFoundException->new( $this->location->url, $childId ) unless $info;
-
-    my $binding  = $info->{binding};
-    my $contract = $info->{contract};
-    my $model    = _InvokeDelegate( $binding, $this, @$childIdParts );
+	die NotFoundException->new( $this->location->url, $childId ) unless $info;
+	
+	my %args;
 
-    if ( ref $contract eq 'CODE' || $contract->can('Invoke')) {
-        $contract = _InvokeDelegate($contract,$this,$model);
-        $info->{contract} = $contract;
-    }
+	my $binding  = $info->{binding};
+	my $contract = $info->{contract};
+	if (ref($binding) eq 'HASH' ) {
+		$args{$_} = _InvokeDelegate( $binding->{$_}, $this, @$childIdParts )
+			foreach keys %$binding;
+	} else {
+		$args{model} = _InvokeDelegate( $binding, $this, @$childIdParts );
+	}
 
-    die OperationException->new( "Can't fetch a contract for the resource",
-        $childId )
-      unless $contract;
+	if ( ref $contract eq 'CODE' || $contract->can('Invoke') ) {
+		$contract = _InvokeDelegate( $contract, $this, $args{model} );
+		$info->{contract} = $contract;
+	}
 
-    my %args = (
-        parent => $this,
-        id     => $childId,
-        model  => $model
-    );
+	die OperationException->new( "Can't fetch a contract for the resource",
+		$childId )
+	  unless $contract;
 
-    return $contract->CreateResource(%args);
+	$args{parent} = $this;
+	$args{id}     = $childId;
+	$args{request} = $this->request;
+
+	return $contract->CreateResource(%args);
 }
 
 sub _InvokeDelegate {
-    my $delegate = shift;
+	my $delegate = shift;
 
-    return $delegate->(@_) if ref $delegate eq 'CODE';
-    return $delegate->Invoke(@_) if eval { $delegate->can('Invoke') };
+	return $delegate->(@_) if ref $delegate eq 'CODE';
+	return $delegate->Invoke(@_) if eval { $delegate->can('Invoke') };
 }
 
 1;
--- a/Lib/IMPL/Web/Application/ResourceContract.pm	Wed Jun 05 18:21:11 2013 +0400
+++ b/Lib/IMPL/Web/Application/ResourceContract.pm	Sun Jun 09 21:48:57 2013 +0400
@@ -121,32 +121,18 @@
 
 my $contract = ResourceContract->new(
     verbs => {
-    	get => OperationContract->new(
+    	get => sub {
+        	my ($resource,$action) = @_;
+            return "Hello!";
+        },
+        post => OperationContract->new(
             binding => sub {
             	my ($resource,$action) = @_;
-                return "Hello!";
-            }
-        ),
-        post => OperationContract->new(
-            parameters => [
-                IMPL::Transform::DataToModel->new() # создаем преобразование для формы
-            ],
-            binding => sub {
-            	my ($resource,$action,$data) = @_;
-            	return $resource->model->AddItem($data);
-            },
-            success => sub {
-            	my ($resource,$action,$result) = @_;
+            	my $data = My::App::ModelBinder->new($resource->ds)->Bind($action);
             	return IMPL::Web::HttpResponse->Redirect(
             	   location => $resource->location->Child($result->id)
             	)
-            },
-            error => sub {
-            	my ($resource,$action,$error) = @_;
-            	$action->errors->Append($error);
-            	return $resource->model;
             }
-            
         ),
     },
     resources => [
@@ -155,16 +141,15 @@
         	binding => sub {
         		return $_[0]->model->info;
         	},
-        	contract => ResourceContract->new(
+        	contract => {
         	   verbs => {
-        	       # using method references is also possible
 	        	   get => sub {
 	        	   	   my ($resource,$action) = @_;
 	        	   	   return $resource->model;
 	        	   }
 	        	   
         	   }
-        	)
+        	}
         }
     ]
 )
@@ -172,6 +157,7 @@
 my $obj = My::App::Data->fetch('something');
 
 my $resource = $contract->CreateResource(
+	request => $currentRequest,
     model => $obj,
     parent => $prentResource,
     id => 'item-something'
--- a/Lib/IMPL/Web/Handler/RestController.pm	Wed Jun 05 18:21:11 2013 +0400
+++ b/Lib/IMPL/Web/Handler/RestController.pm	Sun Jun 09 21:48:57 2013 +0400
@@ -49,25 +49,24 @@
 
 
 sub Invoke {
-	my ($this,$action) = @_;
+	my ($this,$request) = @_;
 	
-	my $method = $action->requestMethod;
+	my $method = $request->requestMethod;
 	
-	my @segments = $this->GetResourcePath($action);
+	my @segments = $this->GetResourcePath($request);
 	
 	my $res = $this->resourceFactory->new(
 	   id => 'root',
-	   location => Locator->new(base => $action->application->baseUrl),
-	   application => $action->application
+	   request => $request,
+	   location => Locator->new(base => $request->application->baseUrl),
 	);
 	
 	while(@segments) {
 		my $id = shift @segments;
-		
 		$res = $res->FetchChildResource($id);
 	}
 	
-	$res = $res->InvokeHttpVerb($method,$action);
+	$res = $res->InvokeHttpVerb($method);
 }
 
 1;