view Lib/IMPL/Web/Handler/ViewSelector.pm @ 218:358f867712b4

sync
author sergey
date Mon, 20 Aug 2012 17:24:48 +0400
parents d6e2ea24af08
children 47f77e6409f7
line wrap: on
line source

package IMPL::Web::Handler::ViewSelector;
use strict;

use IMPL::lang qw(:declare :constants);

use IMPL::declare {
	require => {
		NotAcceptable => 'IMPL::Web::NotAcceptableException',
		ViewResult => 'IMPL::Web::Application::ViewResult'
	},
	base => {
		'IMPL::Object' => undef,
		'IMPL::Object::Autofill' => '@_',
		'IMPL::Object::Serializable' => undef
	}
};

BEGIN {
	public property views => PROP_ALL | PROP_LIST;
	public property fallback => PROP_ALL;
	public property types => PROP_ALL;
}

sub Invoke {
	my ($this,$action,$next) = @_;
	
	my $result = $next ? $next->($action) : undef;
	
	my $model;
	
	if( eval { $result->isa(ViewResult) } ) {
	
		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;
	}
}

1;

__END__

=pod

=head1 NAME

C<IMPL::Web::Handler::ViewSelector> - Выбор нужного представления на основе заголовка C<Accept>

=head1 DESCRIPTION

Использует заголовок запроса C<Accept> для выбора подходящего представления, если задано свойство C<types>,
пытается в первую очередь по расширению определить, какое представление подходит.

В случаях, когда не требуется строить представление для данных (например, при перенаправлении к другому
ресурсу или если нет данных), нужно, чтобы данному обработчику был возвращен
L<IMPL::Web::Application::ActionResult>, который будет просто передан далее.

=head1 MEMBERS

=head2 C<[get,set,list]views>

Список представлений, которые могут быть возвращены.

=head2 C<[get,set]types>

Хеш с соотвествием между расширением и типом содержимого, для подсказки при выборе представления.

=cut