Mercurial > pub > Impl
view Lib/IMPL/Web/Application/Action.pm @ 179:b3d91ff7aea9
minor changes
author | sourcer |
---|---|
date | Tue, 20 Dec 2011 23:00:30 +0300 |
parents | aaab45153411 |
children | d1676be8afcc |
line wrap: on
line source
package IMPL::Web::Application::Action; use strict; use parent qw(IMPL::Object IMPL::Object::Autofill); __PACKAGE__->PassThroughArgs; use IMPL::Class::Property; BEGIN { public property application => prop_get | owner_set; public property query => prop_get | owner_set; public property response => prop_get | owner_set; public property responseFactory => prop_get | owner_set; public property context => prop_get | owner_set; private property _entryPoint => prop_all; } sub CTOR { my ($this) = @_; $this->responseFactory('IMPL::Web::Application::Response') unless $this->responseFactory; $this->response( $this->responseFactory->new(query => $this->query) ); $this->context({}); } sub Invoke { my ($this) = @_; if ($this->_entryPoint) { $this->_entryPoint->(); } else { die new IMPL::InvalidOperationException("At least one handler is required"); } } sub ReinitResponse { my ($this) = @_; die new IMPL::InvalidOperationException("Response already sent") if $this->response->isHeaderPrinted; $this->response->Discard; $this->response($this->responseFactory->new(query => $this->query)); } sub ChainHandler { my ($this,$handler) = @_; my $delegateNext = $this->_entryPoint(); if (ref $handler eq 'CODE') { $this->_entryPoint( sub { $handler->($this,$delegateNext); } ); } elsif (ref $handler and UNIVERSAL::isa($handler,'IMPL::Web::QueryHandler')) { $this->_entryPoint( sub { $handler->Invoke($this,$delegateNext); } ); } elsif ($handler and not ref $handler) { if (my $method = $this->can($handler) ) { $this->_entryPoint( sub { $method->($this,$delegateNext); } ); } else { { no strict 'refs'; eval "require $handler; 1;" or die new IMPL::InvalidArgumentException("An invalid handler supplied",$handler,"Failed to load module") unless keys %{"${handler}::"}; } if (UNIVERSAL::isa($handler,'IMPL::Web::QueryHandler')) { $this->_entryPoint( sub { $handler->Invoke($this,$delegateNext); } ); } else { die new IMPL::InvalidArgumentException("An invalid handler supplied",$handler); } } } else { die new IMPL::InvalidArgumentException("An invalid handler supplied",$handler); } } sub cookie { my ($this,$name,$rx) = @_; $this->_launder(scalar( $this->query->cookie($name) ), $rx ); } sub param { my ($this,$name,$rx) = @_; $this->_launder(scalar( $this->query->param($name) ), $rx ); } sub _launder { my ($this,$value,$rx) = @_; if ( $value ) { if ($rx) { if ( my @result = ($value =~ m/$rx/) ) { return @result > 1 ? \@result : $result[0]; } else { return undef; } } else { return $value; } } else { return undef; } } 1; __END__ =pod =head1 NAME C<IMPL::Web::Application::Action> - Обертка вокруг C<CGI> запроса. =head1 DESCRIPTION C<[Infrastructure]> Определяет порядок выполнения запроса. Запрос выполняется последовательным вызовом цепочки обработчиков, при этом обработчики сами вызывают следующие. Обработчики выполняются в порядке, обратном их добавлению. Типичная цепочка может быть такой, в порядке добавления =begin code IMPL::Web::QueryHandler::SecCallToMethod IMPL::Web::QueryHandler::AuthenticateCookie IMPL::Web::QueryHandler::PageFormat =end code что приведет к следующей последовательности =begin code # the application creates a new Action object my $action = $application->actionFactory->new( action => $application, # the application passes self query => $query # current CGI query ); # forms query handlers stack $action->ChainHandler($_) foreach qw ( IMPL::Web::QueryHandler::SecCallToMethod IMPL::Web::QueryHandler::AuthenticateCookie IMPL::Web::QueryHandler::PageFormat ); # and finally invokes the action $action->Invoke() { # some internals IMPL::Web::QueryHandler::PageFormat->Invoke($action,$nextHandlerIsAuthHandler) { #some internals my $result = $nextHandlerIsAuthHandler() { # some internals IMPL::Web::QueryHandler::AuthenticateCookie->Invoke($action,$nextHandlerIsSecCall) { # some internals # do auth and generate security $context # impersonate $context and call the next handler return $context->Impersonate($nextHandlerIsSecCall) { # some internals IMPL::Web::QueryHandler::SecCallToMethod->Invoke($action,undef) { # next handler isn't present as it is the last hanler # some internals # calculate the $method and the $target from CGI request IMPL::Security->AccessCheck($target,$method); return $target->$method(); } } } } # some intenals # formatted output to $action->response->streamBody } } =end code или как альтернатива может быть еще =begin code IMPL::Web::QueryHandler::SecCallToMethod IMPL::Web::QueryHandler::AuthenticateCookie IMPL::Web::QueryHandler::Filter->new( target => IMPL::Transform::ObjectToJSON->new() , method => 'Transform') IMLP::Web::QueryHandler::JSONFormat =end code В данной цепочке также происходит вызов метода, но его результат потом преобразуется в простые структуры и передается JSON преобразователю. Таким образом модулю логики не требуется знать о выходном формате, всю работу проделают дополнительные фильтры. =head1 MEMBERS =head2 PROPERTIES =over =item C< [get] application> Экземпляр приложения создавшего текущий объект =item C< [get] query > Экземпляр C<CGI> запроса =item C< [get] response > Ответ на C<CGI> заспрос C<IMPL::Web::Application::Response> =item C< [get] responseFactory > Фабрика ответов на запрос, используется для создания нового ответа либо при конструировании текущего объекта C<IMPL::Web::Application::Action>, либо при вызове метода C<ReinitResponse> у текущего объекта. По умолчанию имеет значение C<IMPL::Web::Application::Response> =back =head2 METHODS =over =item C< ReinitResponse() > Отмена старого ответа C<response> и создание вместо него нового. Данная операция обычно проводится при обработке ошибок, когда уже сформированный ответ требуется отменить. Следует заметить, что эта операция не возможна, если ответ частично или полностью отправлен клиенту. Тогда возникает исключение C<IMPL::InvalidOperationException>. =item C< ChainHandler($handler) > Добавляет новый обработчик в цепочку. Выполнение цепочки начинается с конца, тоесть последний добавленный будет выполнен первым. =back =head1 HANDLERS =head2 subroutines =over =item CODE ref Ссылка на процедуру может являться обработчиком, при этом функция будет вызвана с двумя параметрами: ссылкой на action объект, и точкой входа следующего обработчика. =item Method Name Имя метода, передается в виде строки. У текущего объекта action ищется метод с указанным именем, после чего используется ссылка на этот метод для вызова с двумя параметрами: ссылкой на action объект, и точкой входа следующего обработчика. Получается вызов идентичный следующему C<< $action->MethodName($nextHandler) >>; =back =head2 C< IMPL::Web::QueryHandler > Любой объект наследованный от C< IMPL::Web::QueryHandler > может быть использован в качестве обработчика запроса =cut