view Lib/IMPL/Web/View/TTLoader.pm @ 222:84a6382b49c8

resources schema in progress
author sergey
date Mon, 27 Aug 2012 17:28:32 +0400
parents c8fe3f84feba
children 5c82eec23bb6
line wrap: on
line source

package IMPL::Web::View::TTLoader;
use strict;

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

use Template::Provider();
use Template::Context();
use Template::Constants qw(:status);

use IMPL::Web::View::TTDocument();

use parent qw(
    IMPL::Object
    IMPL::Object::Serializable
);

BEGIN {
    public property options => PROP_ALL;
    public property provider => PROP_GET | PROP_OWNERSET;
    public property context => PROP_GET | PROP_OWNERSET;
    public property ext => PROP_ALL;
    public property layoutBase => PROP_GET | PROP_OWNERSET;
    
    public property isInitialized => PROP_GET | PROP_OWNERSET;
    public property initializer => PROP_GET | PROP_OWNERSET;
    
    private property _globals => PROP_ALL;
}

sub save {
	my ($this,$context) = @_;
	
	$context->AddVar($_, $this->$_()) for qw(options provider context ext layoutBase);
}

sub restore {
	my ($class,$data,$surrogate) = @_;
	
	my %params = @$data;
	
	my $refOpts = delete $params{options};
	
	if ($surrogate){
		$surrogate->callCTOR($refOpts,%params);
	} else {
		$surrogate = $class->new($refOpts,%params);
	}
	return $surrogate;
}

sub CTOR {
    my ($this,$refOpts,%args) = @_;
    
    $refOpts ||= {};
    
    $this->ext($args{ext}) if $args{ext};
    $this->initializer($args{initializer}) if $args{initializer};
    $this->_globals(ref $args{globals} eq 'HASH' ? $args{globals} : {});
    
    $this->options($refOpts);
    $this->layoutBase($args{layoutBase}) if $args{layoutBase};
    
    # to aviod cyclic references we need to do a copy of $refOpts
    $refOpts->{LOAD_TEMPLATES} = $this->provider(new Template::Provider( { %$refOpts } ));
    
    $this->context(new Template::Context($refOpts));
}

sub document {
    my ($this,$name,$vars) = @_;
    
    my $tt = $this->template($name);
        
    $this->_init();
    
    my $opts = { %{ $this->options } };
    
    $opts->{STASH} = $this->context->stash->clone();
    $opts->{LOAD_TEMPLATES} = $this->provider;
    
    return new IMPL::Web::View::TTDocument( $tt, $opts, $this, $vars );
}

sub template {
    my ($this,$name) = @_;
    
    $name =~ s/^\s+|\s+$//g;
    
    die new IMPL::InvalidArgumentException("A valid template name is required") unless length $name;
    
    $name = $this->_appendExt($name);
    
    my ($tt,$error) = $this->provider->fetch($name);
    
    if (defined $error and $error == STATUS_DECLINED) {
        die new IMPL::KeyNotFoundException($name);
    } elsif (defined $error and $error == STATUS_ERROR) {
        die new IMPL::Exception("Failed to load a template", $name, $tt);
    }
    
    return $tt;
}

sub layout {
	my ($this,$name) = @_;
	
	my $layout;
	
	if ($this->layoutBase) {
		$layout = $this->layoutBase . "/";
	}
	
	$layout .= $name;
	return $this->template($layout);
}

sub _appendExt {
    my ($this,$name) = @_;
    
    return $name unless $this->ext;
    
    if (length $this->ext and substr( $name, -length($this->ext) ) eq $this->ext) {
        return $name;
    } else {
        return $name . $this->ext;
    }
}

sub _init {
    my ($this) = @_;
    
    if (!$this->isInitialized) {
        my $initializer = $this->initializer || sub {};
        
        eval {
            $this->context->process($initializer,$this->_globals);
        };
        if (my $e = $@) {
            die new IMPL::Exception("Failed to process an initializer", $this->initializer, $e);
        }
        
        $this->isInitialized(1);
    }
}

1;

__END__

=pod

=head1 NAME

C<IMPL::Web::View::TTLoader> - предоставляет глобальный контекст для загрузки шаблонов

=head1 SYNOPSIS

=begin code

use IMPL::Web::View::TTLoader();

my $loader = new IMPL::Web::View::TTLoader(
    {
        INCLUDE_PATH => [
            '/my/app/tt',
            '/my/app/tt/lib'
        ]
    },
    ext => '.tt',
    initializer => 'shared/global'
        
);

my $doc = $loader->document('index');

my $html = $doc->Render();

=end code

=head1 DESCRIPTION

=head1 MEMBERS

=head2 C<document($docName)>

=cut