changeset 213:d6e2ea24af08

sync
author sergey
date Fri, 03 Aug 2012 01:15:15 +0400
parents 292226770180
children 4683002758aa
files Lib/IMPL/Web/Application.pm Lib/IMPL/Web/Application/ActionResult.pm Lib/IMPL/Web/Application/Response.pm Lib/IMPL/Web/Application/ViewResult.pm Lib/IMPL/Web/Handler/ErrorHandler.pm Lib/IMPL/Web/Handler/RestController.pm Lib/IMPL/Web/Handler/ViewSelector.pm Lib/IMPL/Web/RestContract.pm Lib/IMPL/Web/View/TTControl.pm Lib/IMPL/lang.pm
diffstat 10 files changed, 259 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/Lib/IMPL/Web/Application.pm	Fri Jun 29 19:24:15 2012 +0400
+++ b/Lib/IMPL/Web/Application.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -19,7 +19,7 @@
 };
 
 BEGIN {
-	public property handlerError => PROP_ALL;
+	public property errorHandler => PROP_ALL;
 	public property actionFactory => PROP_ALL;
 	public property handlers => PROP_ALL | PROP_LIST;
 	public property responseCharset => PROP_ALL;
@@ -44,7 +44,7 @@
     $this->actionFactory(TAction) unless $this->actionFactory;
     $this->responseCharset('utf-8') unless $this->responseCharset;
     $this->fetchRequestMethod(\&defaultFetchRequest) unless $this->fetchRequestMethod;
-    $this->handlerError(\&defaultHandlerError) unless $this->handlerError;
+    $this->errorHandler(\&defaultErrorHandler) unless $this->errorHandler;
 }
 
 sub Run {
@@ -71,7 +71,7 @@
         if ($@) {
             my $e = $@;
             # we are expecting this method to be safe otherwise we can trust nothing in this wolrd 
-            $this->handlerError()->($this,$action,$e);
+            $this->errorHandler()->($this,$action,$e);
         }
     }
 }
@@ -138,7 +138,7 @@
     }
 }
 
-sub defaultHandlerError {
+sub defaultErrorHandler {
     my ($this,$action,$e) = @_;
     warn $e;
     if ( eval {    $action->ReinitResponse(); 1; } ) {
@@ -208,7 +208,7 @@
 
 =head1 DESCRIPTION
 
-C< use parent qw( IMPL::Config IMPL::Object::Singleton )>
+C< inherits IMPL::Config, IMPL::Object::Singleton >
 
 Зкземпляр приложения содержит в себе глобальные настройки, реализует контроллер запросов,
 в качестве источника запросов используется CGI или иной совместимый модуль.
@@ -310,7 +310,7 @@
 
 =over
 
-=item C<[get,set] handlerError>
+=item C<[get,set] errorHandler>
 
 Обработчик который будет вызван в случае возникновения необработанной ошибки
 в процессе работы приложения. После чего приложение корректно завершается.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/Web/Application/ActionResult.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -0,0 +1,130 @@
+use strict;
+package IMPL::Web::Application::ActionResult;
+
+use CGI();
+use IMPL::lang qw(:declare);
+use IMPL::declare {
+	require => {
+		Exception => 'IMPL::Exception',
+		ArgumentException => '-IMPL::InvalidArgumentException' 
+	},
+	base => [
+		'IMPL::Object' => undef,
+		'IMPL::Object::Autofill' => '@_'
+	]
+};
+
+BEGIN {
+	public property status => PROP_ALL;
+	public property type => PROP_ALL;
+    public property charset => PROP_ALL;
+	public property cookies => PROP_ALL;
+	public property headers => PROP_ALL;
+	public property body => PROP_ALL;	
+}
+
+sub CTOR {
+	my ($this) = @_;
+	
+	$this->headers({});
+	$this->cookies({});
+}
+
+sub PrintResponse {
+	my ($this,$out) = @_;
+	
+	my $q = CGI->new({});
+	
+	my %headers = %{$this->headers};
+	
+	if(my $cookies = $this->cookies) {
+		$headers{-cookie} = [map _createCookie($_,$cookies->{$_}), keys %$cookies] if $cookies;
+	}
+	
+	$headers{'-status'} = $this->status || '200 OK';
+	$headers{'-type'} = $this->type || 'text/html';
+	
+	if(my $charset = $this->charset) {
+	   $q->charset($charset);
+	   binmode $out, ":encoding($charset)";
+	}
+	
+	$q->header(\%headers);
+	
+	if(my $body = $this->body) {
+		if(ref $body eq 'CODE') {
+			$body->($out);
+		} else {
+			print $out $body;
+		}
+	}
+}
+
+#used to map a pair name valie to a valid cookie object
+sub _createCookie {
+    return UNIVERSAL::isa($_[1], 'CGI::Cookie') ? $_[1] : CGI::Cookie->new(-name => $_[0], -value => $_[1] );
+}
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+C<IMPL::Web::Application::ActionResult> - Результат обработки C<HTTP> запроса.
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Базовый класс для ответов приложения на C<HTTP> запрос. Каждый вид ответа,
+например 
+
+Данный объект используется для формирования и передачи данных C<HTTP> ответа
+напрямую. Основными полями являются C<body> и C<status>.
+
+Кроме свойств относящихся непосредственно к самому C<HTTP> ответу, данный объект
+может содержать свойства относящиеся к процессу обработки запроса, например
+механизму формирования представления.
+
+=head1 MEMBERS
+
+=head2 C<[get,set]status>
+
+Статус который будет отправлен сервером клиенту, например, C<200 OK> или
+C<204 No response>. Если не указан, то будет C<200 OK>.
+
+=head2 C<[get,set]type>
+
+Тип содержимого, которое будет передано клиенту, если не указано, будет
+C<text/html>.
+
+=head2 C<[get,set]charset>
+
+Кодировка в которой будут переданны данные. Следует задавать если и только, если
+передается текстовая информация. Если указана кодировка, то она будет
+автоматически применена к потоку, который будет передан методу C<PrintResponse>. 
+
+=head2 C<[get,set]cookies>
+
+Опционально. Ссылка на хеш с печеньками.
+
+=head2 C<[get,set]headers>
+
+Опционально. Ссылка на хеш с дополнительными полями заголовка ответа. Формат
+имен полей как у модуля C<CGI>.
+
+=head2 C<[get,set]body>
+
+Тело ответа. Может быть как простой скаляр, который будет приведен к строке и
+выдан в поток вывода метода C<PrintResponse>. Также может быть ссылкой на
+процедуру, в таком случае будет вызвана эта процедура и ей будет передан
+первым параметром поток для вывода тела ответа.
+
+=head2 C<PrintResponse($outStream)>
+
+Формирует заголовок и выводит ответ сервера в указанный параметром поток. 
+
+=cut
\ No newline at end of file
--- a/Lib/IMPL/Web/Application/Response.pm	Fri Jun 29 19:24:15 2012 +0400
+++ b/Lib/IMPL/Web/Application/Response.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -223,7 +223,7 @@
 
 =item C< [get,set] status >
 
-Код ошибки HTTP. Например, '200 OK'. По умолчанию не установлен, при отправке клиенту бедт отправлен '200 ОК'.
+Код HTTP. Например, '200 OK'. По умолчанию не установлен, при отправке клиенту бедт отправлен '200 ОК'.
 
 =item C< [get,set] contentType >
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Lib/IMPL/Web/Application/ViewResult.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -0,0 +1,22 @@
+package IMPL::Web::Application::ViewResult;
+use strict;
+
+use IMPL::lang qw(:declare);
+use IMPL::declare {
+	base => [
+	   'IMPL::Web::Application::ActionResult' => '@_'
+	]
+};
+
+BEGIN {
+	public property model => PROP_ALL;
+	public property type => PROP_ALL;
+}
+
+sub CTOR {
+	my $this = shift;
+	$this->status('200 OK') unless $this->status;
+}
+
+
+1;
\ No newline at end of file
--- a/Lib/IMPL/Web/Handler/ErrorHandler.pm	Fri Jun 29 19:24:15 2012 +0400
+++ b/Lib/IMPL/Web/Handler/ErrorHandler.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -68,4 +68,16 @@
 	return $result;
 }
 
-1;
\ No newline at end of file
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=cut
\ No newline at end of file
--- a/Lib/IMPL/Web/Handler/RestController.pm	Fri Jun 29 19:24:15 2012 +0400
+++ b/Lib/IMPL/Web/Handler/RestController.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -75,11 +75,32 @@
 
 =head1 NAME
 
+C<IMPL::Web::Handler::RestController> - Транслирует запросы к ресурсам в вызовы методов.
+
 =head1 SYNOPSIS
 
+Использует контракты для преобразования стандартных C<REST> запросов в вызовы методов объектов.
+C<$ENV{PATH_INFO}> используется как путь к нужному ресурсу у которого будет вызван метод указанный в запросе.
+
 =head1 DESCRIPTION
 
-Использует C<$ENV{PATH_INFO}> для получения ресурса и вызова метода.
+=head2 Resource model
+
+Ресурсы имеют иерархическую структуру, аналогичную файлам и каталогам, которая описывается контрактом, также
+контрак описывает то, как должны обрабатываться методы C<HTTP> запроса, такие как C<GET> и C<POST>.
+
+За корректность реализации данных методов отвечает разработчик.
+
+Каждый ресурс представляет собой коллкецию вложенных ресурсов, путь указанный в C<HTTP> запросе разбивается на
+части, затем каждый сегмент последовательно используется для поиска дочернего ресурса. При обработки
+первого сегмента используется корневой ресурс. Корневой ресурс должен существовать всегда.
+
+=head2 Contract
+
+Контрактом может быть любое преобразование которое определяет соответсвие между объектами приложения и
+ресурсами, доступными через протокол C<HTTP>.
+
+
 
 
 =cut
\ No newline at end of file
--- a/Lib/IMPL/Web/Handler/ViewSelector.pm	Fri Jun 29 19:24:15 2012 +0400
+++ b/Lib/IMPL/Web/Handler/ViewSelector.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -5,7 +5,8 @@
 
 use IMPL::declare {
 	require => {
-		NotAcceptable => 'IMPL::Web::NotAcceptableException'
+		NotAcceptable => 'IMPL::Web::NotAcceptableException',
+		ViewResult => 'IMPL::Web::Application::ViewResult'
 	},
 	base => {
 		'IMPL::Object' => undef,
@@ -23,38 +24,47 @@
 sub Invoke {
 	my ($this,$action,$next) = @_;
 	
-	my $handler;
-	my $path = $action->query->path_info;
+	my $result = $next ? $next->($action) : undef;
 	
-	if ($this->types and $path =~ m/\.(\w+)$/) {
-		my $forced;	
-		if ($forced = $this->types->{$1} and $action->query->Accept($forced) ) {
-			($handler) = grep eval { $_->can('contentType') } && $_->contentType eq $forced, $this->views;
-		}
-	}
+	my $model;
 	
-	if (not $handler) {
+	if( eval { $result->isa(ViewResult) } ) {
 	
-		my @handlers =
-	        sort {
-	            $b->{preference} <=> $a->{preference}
-	        } map {
-	            {
-	            	handler => $_,
-	                preference =>
-	                    eval { $_->can('contentType') } ? $action->query->Accept($_->contentType) : 0
-	            }
-			} $this->views;
-	
-	    my $info = shift @handlers;
-	    $handler = $info ? $info->{handler} : undef;
-    
+		my $handler;
+		my $path = $action->query->path_info;
+		
+		if ($this->types and $path =~ m/\.(\w+)$/) {
+			my $forced;	
+			if ($forced = $this->types->{$1} and $action->query->Accept($forced) ) {
+				($handler) = grep eval { $_->can('contentType') } && $_->contentType eq $forced, $this->views;
+			}
+		}
+		
+		if (not $handler) {
+		
+			my @handlers =
+		        sort {
+		            $b->{preference} <=> $a->{preference}
+		        } map {
+		            {
+		            	handler => $_,
+		                preference =>
+		                    eval { $_->can('contentType') } ? $action->query->Accept($_->contentType) : 0
+		            }
+				} $this->views;
+		
+		    my $info = shift @handlers;
+		    $handler = $info ? $info->{handler} : undef;
+	    
+		}
+	    
+	    die NotAcceptable->new(map { eval {$_->can('contentType') } ? $_->contentType : () } $this->views )
+	        unless $handler;
+	        
+	    return $handler->Invoke($action,sub { $result });
+	} else {
+		return $result;
 	}
-    
-    die NotAcceptable->new(map { eval {$_->can('contentType') } ? $_->contentType : () } $this->views )
-        unless $handler;
-        
-    return $handler->Invoke($action,$next);
 }
 
 1;
@@ -72,6 +82,10 @@
 Использует заголовок запроса C<Accept> для выбора подходящего представления, если задано свойство C<types>,
 пытается в первую очередь по расширению определить, какое представление подходит.
 
+В случаях, когда не требуется строить представление для данных (например, при перенаправлении к другому
+ресурсу или если нет данных), нужно, чтобы данному обработчику был возвращен
+L<IMPL::Web::Application::ActionResult>, который будет просто передан далее.
+
 =head1 MEMBERS
 
 =head2 C<[get,set,list]views>
--- a/Lib/IMPL/Web/RestContract.pm	Fri Jun 29 19:24:15 2012 +0400
+++ b/Lib/IMPL/Web/RestContract.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -1,7 +1,7 @@
 package IMPL::Web::RestContract;
 use strict;
 
-use IMPL::lang qw(:declare :constants);
+use IMPL::lang qw(:declare);
 use IMPL::declare {
 	require => {
 		Exception => 'IMPL::Exception',
@@ -57,4 +57,10 @@
 
 =end code 
 
+=head1 DESCRIPTION
+
+Контракт представляет собой отображение модели предметной области на модель
+ресурсов. Контракт описывает дерево ресурсов начиная с корневого ресурса,
+и далее.
+
 =cut
\ No newline at end of file
--- a/Lib/IMPL/Web/View/TTControl.pm	Fri Jun 29 19:24:15 2012 +0400
+++ b/Lib/IMPL/Web/View/TTControl.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -105,20 +105,22 @@
 
 =head2 BLOCKS
 
-При загрузке шаблона, создается фабрика, с собственным контекстом в которой выполняется шаблон элемента управления
-
 =head3 INIT
 
-Данный блок шаблона управления выполняется один раз при создании первого экземпляра элемента управления
+Данный блок шаблона управления выполняется один раз при создании первого экземпляра элемента управления,
+может использоваться для формирования заголовочной части документа, скрипта подключающего ajax модули
+при необходимости и т.п.
 
 =head3 CTOR
 
 данный блок выполняется каждый раз при создании нового экземпляра элемента управления, при этом переменная C<this>
-указывает на эземпляр элемента упарвления
+указывает на эземпляр элемента упарвления. Данный блок можно использовать для инициализации свойств элемента
+управления.
 
 =head3 RENDER
 
-Данный блок выполняется при вызове метода C<Render()>, вывод данного блока и есть результат отображения элемента управления. 
+Данный блок выполняется при вызове метода C<Render()>, вывод данного блока и есть результат отображения элемента управления.
+Если в шаблоне нет блока C<RENDER>, то сам шаблон считается таковым. 
 
 =head2 TEMPLATE VARS
 
--- a/Lib/IMPL/lang.pm	Fri Jun 29 19:24:15 2012 +0400
+++ b/Lib/IMPL/lang.pm	Fri Aug 03 01:15:15 2012 +0400
@@ -38,6 +38,14 @@
           &property
           &static
           &property
+          &ACCESS_PUBLIC
+          &ACCESS_PROTECTED
+          &ACCESS_PRIVATE
+          &PROP_GET
+          &PROP_SET
+          &PROP_OWNERSET
+          &PROP_LIST
+          &PROP_ALL
           )
     ],
     compare => [