Mercurial > pub > Impl
view Lib/IMPL/Web/Application.pm @ 229:47f77e6409f7
heavily reworked the resource model of the web application:
*some ResourcesContraact functionality moved to Resource
+Added CustomResource
*Corrected action handlers
author | sergey |
---|---|
date | Sat, 29 Sep 2012 02:34:47 +0400 |
parents | d6e2ea24af08 |
children | 6d8092d8ce1b |
line wrap: on
line source
package IMPL::Web::Application; use strict; use warnings; use IMPL::lang qw(:declare :constants); use CGI; use Carp qw(carp); use IMPL::declare { require => { TAction => 'IMPL::Web::Application::Action', HttpResponse => 'IMPL::Web::HttpResponse', TFactory => '-IMPL::Object::Factory', Exception => 'IMPL::Exception', InvalidOperationException => 'IMPL::InvalidOperationException', Loader => 'IMPL::Code::Loader' }, base => [ 'IMPL::Config' => '@_', 'IMPL::Object::Singleton' => '@_' ], props => [ actionFactory => PROP_ALL, handlers => PROP_ALL | PROP_LIST, security => PROP_ALL, options => PROP_ALL, fetchRequestMethod => PROP_ALL, output => PROP_ALL ] }; sub CTOR { my ($this) = @_; die IMPL::InvalidArgumentException->new( "handlers", "At least one handler should be supplied" ) unless $this->handlers->Count; $this->actionFactory(TAction) unless $this->actionFactory; $this->fetchRequestMethod( \&defaultFetchRequest ) unless $this->fetchRequestMethod; } sub Run { my ($this) = @_; my $handler; $handler = _ChainHandler( $_, $handler ) foreach $this->handlers; while ( my $query = $this->FetchRequest() ) { my $action = $this->actionFactory->new( query => $query, application => $this, ); eval { my $result = $handler->($action); die InvalidOperationException->new( "Invalid handlers result. A reference to IMPL::Web::HttpResponse is expexted." ) unless eval { $result->isa(HttpResponse) }; $result->PrintResponse( $this->output ); }; if ($@) { my $e = $@; HttpResponse->InternalError( type => 'text/plain', charset => 'utf-8', body => $e )->PrintResponse( $this->output ); } } } sub _ChainHandler { my ( $handler, $next ) = @_; if ( ref $handler eq 'CODE' ) { return sub { my ($action) = @_; return $handler->( $action, $next ); }; } elsif ( eval { $handler->can('Invoke') } ) { return sub { my ($action) = @_; return $handler->Invoke( $action, $next ); }; } elsif ( eval { $handler->isa(TFactory) } ) { return sub { my ($action) = @_; my $inst = $handler->new(); return $inst->Invoke( $action, $next ); } } elsif ( $handler and not ref $handler and $handler =~ m/^(-)?(\w+(?:::\w+)*)$/ ) { my $class = $2; if ( not $1 ) { Loader->safe->Require($class); die IMPL::InvalidArgumentException->( "An invalid handler supplied", $handler ) unless $class->can('Invoke'); } return sub { my ($action) = @_; my $inst = $class->new(); return $inst->Invoke( $action, $next ); }; } else { die new IMPL::InvalidArgumentException( "An invalid handler supplied", $handler ); } } sub FetchRequest { my ($this) = @_; if ( ref $this->fetchRequestMethod eq 'CODE' ) { return $this->fetchRequestMethod->($this); } else { die new IMPL::Exception( "Unknown fetchRequestMethod type", ref $this->fetchRequestMethod ); } } { my $hasFetched = 0; sub defaultFetchRequest { my ($this) = @_; return undef if $hasFetched; $hasFetched = 1; $this->output(*STDOUT); my $query = CGIWrapper->new(); return $query; } } sub defaultErrorHandler { my ( $this, $action, $e ) = @_; warn $e; if ( eval { $action->ReinitResponse(); 1; } ) { $action->response->contentType('text/plain'); $action->response->charset( $this->responseCharset ); $action->response->status(500); my $hout = $action->response->streamBody; print $hout $e; $action->response->Complete(); } } package CGIWrapper; use parent qw(CGI); use Encode; our $NO_DECODE = 0; sub param { my $this = shift; return $this->SUPER::param(@_) if $NO_DECODE; if (wantarray) { my @result = $this->SUPER::param(@_); return map Encode::is_utf8($_) ? $_ : Encode::decode( $this->charset, $_, Encode::LEAVE_SRC ), @result; } else { my $result = $this->SUPER::param(@_); return Encode::is_utf8($result) ? $result : Encode::decode( $this->charset, $result, Encode::LEAVE_SRC ); } } sub upload { my $this = shift; local $NO_DECODE = 1; my $oldCharset = $this->charset(); $this->charset('ISO-8859-1'); my $fh = $this->SUPER::upload(@_); $this->charset($oldCharset); return $fh; } 1; __END__ =pod =head1 NAME C<IMPL::Web::Application> Класс для создания экземпляров приложения =head1 SYNOPSIS =begin code use IMPL::require { App => 'IMPL::Web::Application' }; my $instance = App->spawn(); # will use ./IMPL/Web/Application.xml as configuration $instance->Run; =end code =head1 DESCRIPTION Создает экземпляр объекта, который получает и обрабатывает C<HTTP> запрос. Приложение можно загрузить из C<xml> файла в котором описано состояние свойств, для этого используется механизм C<IMPL::Serialization>. Приложение представлет собой модульную конструкцию, которая состоит из цепочки обработчиков. Цепочка обработчиков вызывается снизу вверх, при этом каждый обработчик самостоятельно рекурсивно вызывает следующий (более высокого уровня). =cut