# HG changeset patch # User sergey # Date 1343942115 -14400 # Node ID d6e2ea24af08b872a6dc7f49ccf3e8765b0f4e9c # Parent 292226770180446f0b906f7511094ff977051b54 sync diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/Application.pm --- 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> Обработчик который будет вызван в случае возникновения необработанной ошибки в процессе работы приложения. После чего приложение корректно завершается. diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/Application/ActionResult.pm --- /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 - Результат обработки C запроса. + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +Базовый класс для ответов приложения на C запрос. Каждый вид ответа, +например + +Данный объект используется для формирования и передачи данных C ответа +напрямую. Основными полями являются C и C. + +Кроме свойств относящихся непосредственно к самому C ответу, данный объект +может содержать свойства относящиеся к процессу обработки запроса, например +механизму формирования представления. + +=head1 MEMBERS + +=head2 C<[get,set]status> + +Статус который будет отправлен сервером клиенту, например, C<200 OK> или +C<204 No response>. Если не указан, то будет C<200 OK>. + +=head2 C<[get,set]type> + +Тип содержимого, которое будет передано клиенту, если не указано, будет +C. + +=head2 C<[get,set]charset> + +Кодировка в которой будут переданны данные. Следует задавать если и только, если +передается текстовая информация. Если указана кодировка, то она будет +автоматически применена к потоку, который будет передан методу C. + +=head2 C<[get,set]cookies> + +Опционально. Ссылка на хеш с печеньками. + +=head2 C<[get,set]headers> + +Опционально. Ссылка на хеш с дополнительными полями заголовка ответа. Формат +имен полей как у модуля C. + +=head2 C<[get,set]body> + +Тело ответа. Может быть как простой скаляр, который будет приведен к строке и +выдан в поток вывода метода C. Также может быть ссылкой на +процедуру, в таком случае будет вызвана эта процедура и ей будет передан +первым параметром поток для вывода тела ответа. + +=head2 C + +Формирует заголовок и выводит ответ сервера в указанный параметром поток. + +=cut \ No newline at end of file diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/Application/Response.pm --- 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 > diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/Application/ViewResult.pm --- /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 diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/Handler/ErrorHandler.pm --- 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 diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/Handler/RestController.pm --- 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 - Транслирует запросы к ресурсам в вызовы методов. + =head1 SYNOPSIS +Использует контракты для преобразования стандартных C запросов в вызовы методов объектов. +C<$ENV{PATH_INFO}> используется как путь к нужному ресурсу у которого будет вызван метод указанный в запросе. + =head1 DESCRIPTION -Использует C<$ENV{PATH_INFO}> для получения ресурса и вызова метода. +=head2 Resource model + +Ресурсы имеют иерархическую структуру, аналогичную файлам и каталогам, которая описывается контрактом, также +контрак описывает то, как должны обрабатываться методы C запроса, такие как C и C. + +За корректность реализации данных методов отвечает разработчик. + +Каждый ресурс представляет собой коллкецию вложенных ресурсов, путь указанный в C запросе разбивается на +части, затем каждый сегмент последовательно используется для поиска дочернего ресурса. При обработки +первого сегмента используется корневой ресурс. Корневой ресурс должен существовать всегда. + +=head2 Contract + +Контрактом может быть любое преобразование которое определяет соответсвие между объектами приложения и +ресурсами, доступными через протокол C. + + =cut \ No newline at end of file diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/Handler/ViewSelector.pm --- 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 для выбора подходящего представления, если задано свойство C, пытается в первую очередь по расширению определить, какое представление подходит. +В случаях, когда не требуется строить представление для данных (например, при перенаправлении к другому +ресурсу или если нет данных), нужно, чтобы данному обработчику был возвращен +L, который будет просто передан далее. + =head1 MEMBERS =head2 C<[get,set,list]views> diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/RestContract.pm --- 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 diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/Web/View/TTControl.pm --- 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 -указывает на эземпляр элемента упарвления +указывает на эземпляр элемента упарвления. Данный блок можно использовать для инициализации свойств элемента +управления. =head3 RENDER -Данный блок выполняется при вызове метода C, вывод данного блока и есть результат отображения элемента управления. +Данный блок выполняется при вызове метода C, вывод данного блока и есть результат отображения элемента управления. +Если в шаблоне нет блока C, то сам шаблон считается таковым. =head2 TEMPLATE VARS diff -r 292226770180 -r d6e2ea24af08 Lib/IMPL/lang.pm --- 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 => [