Mercurial > pub > Impl
diff Lib/IMPL/Web/View/TTFactory.pm @ 301:aeeb57a12046
*IMPL::Web::View: templates inheritance support
author | cin |
---|---|
date | Mon, 25 Mar 2013 02:04:18 +0400 |
parents | bf3af33b9003 |
children | b5d5793f348e |
line wrap: on
line diff
--- a/Lib/IMPL/Web/View/TTFactory.pm Fri Mar 22 01:05:11 2013 +0400 +++ b/Lib/IMPL/Web/View/TTFactory.pm Mon Mar 25 02:04:18 2013 +0400 @@ -3,6 +3,7 @@ use Template::Context(); +use Carp qw(carp); use IMPL::lang qw(:hash); use IMPL::Exception(); use Scalar::Util qw(weaken); @@ -11,11 +12,13 @@ use IMPL::Const qw(:prop); use IMPL::declare { require => { - Loader => 'IMPL::Code::Loader' + Loader => 'IMPL::Code::Loader', + OpException => '-IMPL::InvalidOperationException', + ArgException => '-IMPL::InvalidArgumentException' }, base => [ 'IMPL::Object::Factory' => sub { - shift; + shift->class || 'IMPL::Web::View::TTControl'; } ], props => [ @@ -23,17 +26,20 @@ context => PROP_RW, instances => PROP_RW, baseLocation => PROP_RW, - require => PROP_RO + base => PROP_RW, + require => PROP_RO, + blocks => PROP_RO, + initialized => PROP_RO ] }; sub CTOR { - my ($this,$class,$template,$context,$baseLocation,$require) = @_; + my ($this,$template,$context,$baseLocation,$require) = @_; - die IMPL::ArgumentException("A template is required") unless $template; + die ArgException->new("A template is required") unless $template; - Loader->safe->Require($class) - if $class and not ref $class; + Loader->safe->Require($this->factory) + if $this->factory and not ref $this->factory; $context ||= new Template::Context(); @@ -42,55 +48,107 @@ $this->baseLocation($baseLocation); $this->instances(0); $this->require($require); + + if (my $baseTplName = $template->extends) { + $baseTplName =~ s{^\./}{$baseLocation/}; + + my $base = &$require($baseTplName) + or die OpException->new("The specified base template isn't found"); + + $this->base($base); + + $this->blocks(hashMerge($base->blocks, $template->blocks)); + + # блок BASE должен меняться в процессе выполнения, в зависимости от + # шаблона, который рендерится, по мере перехода в BASE + + my @baseStack; + + $this->blocks->{BASE} = sub { + my $ctx = shift; + + die OpException->new("This tamplate doesn't have a base template") + unless $base; + + push @baseStack, $base; + my $block = $base->template->block; + + $base = $base->base; + + my $result = eval { + $ctx->process($block); + }; + my $e = $@; + + $base = pop @baseStack; + + die $e if $e; + return $result; + }; + } else { + $this->blocks( $template->blocks ); + } + + if(my $blocks = $this->blocks) { + while (my ($name,$block) = each %$blocks) { + $context->define_block($name,$block); + } + } + + $context->stash->update({ require => sub { + my ($module) = @_; + + $module =~ s/^\.\//$baseLocation\//; + return $require->($module); + }}); } sub MergeParameters { - my ($this,$refProps) = @_; + my $this = shift; + my $refProps = shift || {}; + + unless (ref($refProps) eq 'HASH') { + carp "Passing control name through the first parameter is deprecated"; + my $name = $refProps; + $refProps = shift; + $refProps->{name} ||= $name; + } $refProps->{factory} = $this; - - my $baseLocation = $this->baseLocation; - my $ctx = $this->CloneContext(); - - my $require = $this->require; - - $ctx->stash->update({ - require => sub { - my ($module) = @_; - - $module =~ s/^\.\//$baseLocation\//; - return $require->($module); - } - }); - + return ($this->template, $ctx, $refProps); } sub CreateObject { my $this = shift; - my $count = $this->instances; - - unless($count) { - # нужно выполнить именно блок INIT шаблона при создании первого экземпляра - if (my $init = $this->template->blocks->{INIT}) { - $this->context->process($init); - } - - $this->context->visit($this->template->blocks); - } + $this->InitOnDemand(); my $instance = $this->SUPER::CreateObject(@_); - - $instance->InitInstance(); - + + my $count = $this->instances; $count++; $this->instances($count); return $instance; } +sub InitOnDemand { + my ($this) = @_; + + unless ($this->initialized) { + $this->initialized(1); + + $this->base->InitOnDemand() + if $this->base; + + if (my $init = $this->template->blocks->{INIT}) { + $this->context->process($init); + } + } +} + sub CloneContext { my ($this) = @_; @@ -104,6 +162,12 @@ return Template::Context->new($args); } +sub Render { + my ($this, $args) = @_; + + return $this->new()->Render($args); +} + sub save { die new IMPL::NotImplementedException("This class doesn't support serialization"); }