# HG changeset patch # User cin # Date 1364162658 -14400 # Node ID aeeb57a12046785719b4426d608611acfe858534 # Parent bf3af33b900394da7ec41dea2445f416b0635572 *IMPL::Web::View: templates inheritance support diff -r bf3af33b9003 -r aeeb57a12046 Lib/IMPL/Web/View/TTControl.pm --- a/Lib/IMPL/Web/View/TTControl.pm Fri Mar 22 01:05:11 2013 +0400 +++ b/Lib/IMPL/Web/View/TTControl.pm Mon Mar 25 02:04:18 2013 +0400 @@ -42,7 +42,7 @@ $this->attributes({}); - if(reftype($attrs) eq 'HASH') { + if(ref($attrs) eq 'HASH') { while (my($key,$value) = each %$attrs) { $this->SetAttribute($key,$value); } @@ -80,7 +80,7 @@ $args = {} unless ref $args eq 'HASH'; return $this->context->include( - $this->template, + $this->template->block, { %$args, this => $this, diff -r bf3af33b9003 -r aeeb57a12046 Lib/IMPL/Web/View/TTDocument.pm --- a/Lib/IMPL/Web/View/TTDocument.pm Fri Mar 22 01:05:11 2013 +0400 +++ b/Lib/IMPL/Web/View/TTDocument.pm Mon Mar 25 02:04:18 2013 +0400 @@ -31,6 +31,7 @@ $this->loader($loader) if $loader; $this->layout( $template->layout ) unless $this->layout; + $this->title( $template->title ) unless $this->title; $this->context->stash->update($vars) if ref $vars eq 'HASH'; @@ -51,7 +52,7 @@ my $control = shift; unless($self) { - carp("Cant load control $control outside the rendering procedure"); + carp("Cant load control $control outside the document rendering process"); return; } @@ -61,10 +62,14 @@ my $path = $control; if ( my $template = $self->loader->template($path) ) { + + $documentContext->localise(); + my $ctx = _clone_context($documentContext); + $documentContext->delocalise(); $factory = new IMPL::Web::View::TTFactory( $template, - $documentContext, + $ctx, join( '/', splice( @{[split(/\//,$path)]}, 0, -1 ) ), $require ); @@ -114,6 +119,8 @@ return $this->next::method($args); } }; + + my $e = $@; undef $require; undef $documentContext; @@ -121,15 +128,11 @@ undef $self; $this->context->delocalise(); - - my $e = $@; if ($e) { die $e; } else { return $text; } - - } sub _clone_context { diff -r bf3af33b9003 -r aeeb57a12046 Lib/IMPL/Web/View/TTFactory.pm --- 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"); } diff -r bf3af33b9003 -r aeeb57a12046 _test/Resources/TTView/My/Org/Base.tt --- a/_test/Resources/TTView/My/Org/Base.tt Fri Mar 22 01:05:11 2013 +0400 +++ b/_test/Resources/TTView/My/Org/Base.tt Mon Mar 25 02:04:18 2013 +0400 @@ -4,6 +4,8 @@ [% BLOCK FOOTER %] base footer [% END %] -[% INCLUDE LABEL %] -Render -[% INCLUDE FOOTER %] \ No newline at end of file +BEGIN BASE DOCUMENT +LABEL: [% INCLUDE LABEL %] +base document body +FOOTER: [% INCLUDE FOOTER %] +END OF BASE DOCUMENT diff -r bf3af33b9003 -r aeeb57a12046 _test/Resources/TTView/My/Org/Derived.tt --- a/_test/Resources/TTView/My/Org/Derived.tt Fri Mar 22 01:05:11 2013 +0400 +++ b/_test/Resources/TTView/My/Org/Derived.tt Mon Mar 25 02:04:18 2013 +0400 @@ -1,5 +1,7 @@ -[% META base='./Base' %] +[% META extends='./Base' %] [% BLOCK LABEL %] derived label [% END %] -[% INCLUDE $base.RENDER %] \ No newline at end of file +BEGIN DERIVED DOCUMENT +[% INCLUDE BASE %] +END OF DERIVED DOCUMENT diff -r bf3af33b9003 -r aeeb57a12046 _test/Resources/TTView/My/Org/Derived2.tt --- a/_test/Resources/TTView/My/Org/Derived2.tt Fri Mar 22 01:05:11 2013 +0400 +++ b/_test/Resources/TTView/My/Org/Derived2.tt Mon Mar 25 02:04:18 2013 +0400 @@ -1,2 +1,7 @@ -[% META base='./Derived2'%] -[% BLOCK LABEL %] \ No newline at end of file +[% META extends='./Derived'%] +[% BLOCK LABEL %] +derived 2 label +[% END %] +BEGIN DERIVED 2 DOCUMENT +[% INCLUDE BASE %] +END OF DERIVED 2 DOCUMENT \ No newline at end of file diff -r bf3af33b9003 -r aeeb57a12046 _test/Resources/TTView/My/Org/Panel.tt --- a/_test/Resources/TTView/My/Org/Panel.tt Fri Mar 22 01:05:11 2013 +0400 +++ b/_test/Resources/TTView/My/Org/Panel.tt Mon Mar 25 02:04:18 2013 +0400 @@ -11,7 +11,7 @@ this.visualClass = this.visualClass || 'classic'; this.childNodes = []; FOREACH text IN this.data; - this.childNodes.push( TPreview.new('preview', nodeValue = text ) ); + this.childNodes.push( TPreview.new(nodeValue = text ) ); END; %] diff -r bf3af33b9003 -r aeeb57a12046 _test/Resources/TTView/My/Org/TextPreview.tt --- a/_test/Resources/TTView/My/Org/TextPreview.tt Fri Mar 22 01:05:11 2013 +0400 +++ b/_test/Resources/TTView/My/Org/TextPreview.tt Mon Mar 25 02:04:18 2013 +0400 @@ -3,6 +3,4 @@ dojo.require.push("dijit.layout.ContentPane"); END; %] -[% BLOCK RENDER %] -