# HG changeset patch # User cin # Date 1381492144 -14400 # Node ID feeb3bc4a818b9a57d134fc5ed90c3c233ec0cd6 # Parent 675cd182925595ce4c156c7c93a769ec1c327139 corrected error handling while loading templates corrected variables lookup in controls updated handles to use the new view features diff -r 675cd1829255 -r feeb3bc4a818 Lib/IMPL/Web/Handler/ErrorHandler.pm --- a/Lib/IMPL/Web/Handler/ErrorHandler.pm Thu Oct 10 19:51:19 2013 +0400 +++ b/Lib/IMPL/Web/Handler/ErrorHandler.pm Fri Oct 11 15:49:04 2013 +0400 @@ -17,7 +17,8 @@ }, props => [ errors => PROP_RW, - loader => PROP_RW, + view => PROP_RW, + layout => PROP_RW, fallback => PROP_RW, contentType => PROP_RW ] @@ -26,7 +27,7 @@ sub CTOR { my ($this) = @_; - die ArgumentException->new("loader") unless $this->loader; + die ArgumentException->new("view") unless $this->view; die ArgumentException->new("fallback") unless $this->fallback; $this->errors({}) unless $this->errors; @@ -44,7 +45,7 @@ if (my $err = $@) { - warn "$err"; + warn "error handler: $err"; my $vars = { error => $err @@ -58,12 +59,13 @@ my ($code) = ($status =~ m/^(\d+)/); - my $doc = $this->loader->document( - $this->errors->{$code} || $this->fallback, - $vars - ); - - my $text = $doc->Render($vars); + my $text = $this->view->display( + $err, + $this->errors->{$code} || $this->fallback, + { + layout => $this->layout + } + ); $result = HttpResponse->new( status => $status, diff -r 675cd1829255 -r feeb3bc4a818 Lib/IMPL/Web/Handler/TTView.pm --- a/Lib/IMPL/Web/Handler/TTView.pm Thu Oct 10 19:51:19 2013 +0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,348 +0,0 @@ -package IMPL::Web::Handler::TTView; -use strict; - -use Carp qw(carp); -use List::Util qw(first); -use IMPL::lang; -use IMPL::Const qw(:prop); -use IMPL::declare { - require => { - Factory => 'IMPL::Web::View::ObjectFactory', - HttpResponse => 'IMPL::Web::HttpResponse', - Loader => 'IMPL::Code::Loader', - ViewResult => '-IMPL::Web::ViewResult' - }, - base => [ - 'IMPL::Object' => undef, - 'IMPL::Object::Autofill' => '@_', - 'IMPL::Object::Serializable' => undef - ], - - props => [ - contentType => PROP_RO, - contentCharset => PROP_RO, - loader => PROP_RO, - selectors => PROP_RO, - defaultDocument => PROP_RW, - _selectorsCache => PROP_RW - ] -}; - -sub CTOR { - my ($this) = @_; - - $this->_selectorsCache([ map $this->ParseRule($_), @{$this->selectors || []} ]); -} - -sub Invoke { - my ( $this, $action, $next ) = @_; - - my $result = $next ? $next->($action) : undef; - - my ($model,$view); - if( ref $result and eval { $result->isa(ViewResult) } ) { - $model = $result->model; - $view = $result; - } else { - $model = $result; - $view = ViewResult->new(model => $model); - } - - my $vars = { - view => $view, - model => $model, - #TODO cleanup - action => sub { carp "action variable is deprecated"; $action }, - request => sub { $action }, - app => $action->application, - context => $action->context, - env => _cached($action->context->{environment}), - ImportClass => sub { - my $class = shift; - - carp "ImportClass is obsolete use import instead"; - - $class = Loader->safe->Require($class); - - return Factory->new($class); - }, - import => sub { - my $class = shift; - - $class = Loader->safe->Require($class); - - return Factory->new($class); - } - }; - - my $doc = - $this->loader->document( eval { $view->template } || $this->SelectView( $action, ref $model ), - $vars ); - - $doc->location($action->context->{resourceLocation}); - - my %responseParams = ( - type => $this->contentType, - charset => $this->contentCharset, - body => $doc->Render() - ); - - $responseParams{status} = $view->status if $view->status; - $responseParams{cookies} = $view->cookies if ref $view->cookies eq 'HASH'; - $responseParams{headers} = $view->headers if ref $view->headers eq 'HASH'; - - return HttpResponse->new( - %responseParams - ); -} - -sub _cached { - my $arg = shift; - - return $arg unless ref $arg eq 'CODE'; - - return sub { - ref $arg eq 'CODE' ? $arg = &$arg() : $arg; - } -} - -sub SelectView { - my ($this,$action) = @_; - - my @path; - - for(my $r = $action->context->{resource}; $r ; $r = $r->parent ) { - unshift @path, { - name => $r->id, - class => typeof($r->model) - }; - } - - @path = map { name => $_}, split /\/+/, $action->query->path_info() - unless (@path); - - return $this->MatchPath(\@path,$this->_selectorsCache) || $this->defaultDocument; -} - -sub ParseRule { - my ($this, $rule) = @_; - - my ($selector,$data) = split /\s+=>\s+/, $rule; - - my @parts; - my $first = 1; - my $weight = 0; - foreach my $part ( split /\//, $selector ) { - # если первым символом является / - # значит путь в селекторе абсолютный и не нужно - # добавлять "любой" элемент в начало - - if($part) { - $weight ++; - push @parts,{ any => 1 } if $first; - } else { - push @parts,{ any => 1 } unless $first; - next; - } - - my ($name,$class) = split /@/, $part; - - if ( my ( $varName, $rx ) = ( $name =~ m/^\{(?:(\w+)\:)?(.*)\}$/ ) ) { - #this is a regexp - - push @parts, { - rx => $rx, - var => $varName, - class => $class, - }; - } else { - push @parts, { - name => length($name) ? $name : undef, - class => $class, - }; - } - } continue { - $first = 0; - } - - return { selector => \@parts, data => $data, weight => $weight }; -} - -sub MatchPath { - my ($this,$path,$rules) = @_; - - $path ||= []; - $rules ||= []; - - my @next; - - foreach my $segment (@$path) { - foreach my $rule (@$rules) { - my @selector = @{$rule->{selector}}; - - my $part = shift @selector; - - # if this rule doesn't have a selector - next unless $part; - - if ($part->{any}) { - #keep the rule for the next try - push @next, $rule; - - $part = shift @selector while $part->{any}; - } - - my $newRule = { - selector => \@selector, - data => $rule->{data}, - weight => $rule->{weight}, - vars => { %{$rule->{vars} || {}} } - }; - - my $success = 1; - if (my $class = $part->{class}) { - $success = isclass($segment->{class},$class); - } - - if($success && (my $name = $part->{name})) { - $success = $segment->{name} eq $name; - } elsif ($success && (my $rx = $part->{rx})) { - if( my @captures = ($segment->{name} =~ m/($rx)/) ) { - $newRule->{vars}->{$part->{var}} = \@captures - if $part->{var}; - } else { - $success = 0; - } - } - - push @next, $newRule if $success; - - } - $rules = [@next]; - undef @next; - } - - my $result = ( - sort { - $b->{weight} <=> $a->{weight} - } - grep { - scalar(@{$_->{selector}}) == 0 - } - @$rules - )[0]; - - if($result) { - my $data = $result->{data}; - $data =~ s/{(\w+)(?:\:(\d+))?}/ - my ($name,$index) = ($1,$2 || 0); - - if ($result->{vars}{$name}) { - $result->{vars}{$name}[$index]; - } else { - ""; - } - /gex; - - return $data; - } else { - return; - } -} - -1; - -__END__ - -=pod - -=head1 NAME - -C - использует шаблоны для построения представления. - -=head1 SYNOPSIS - -=begin code xml - - - text/html - - - - IMPL::Config - view - - 1 - 1 - utf-8 - - .tt - global.tt - layouts - - default - - @HASH => dump - @My::Data::Product => product/info - {action:.*} @My::Data::Product => product/{action} - - - -=end code xml - -=head1 DESCRIPTION - -Подбирает шаблон для представления результата, полученного при выполнении следующего обработчика. При -выборе используется принцип похожий на селекторы C, основывающийся на именах ресурсов и их типах -данных. - -Данный обработчик понимает определенные свойства контекста: - -=over - -=item * C - -В данном свойстве может быть передана информация о текущем расположении ресурса, -для которого строится представление. Эта информация будет доступна в шаблоне -через свойство документа C. - -=item * C - -В данном совойстве контекста передается дополнительная информация об окружении -ресурса, например, которую задали родительские ресурсы. Использование данного -свойства позволяет не загромождать ресурс реализацией функциональности по -поддержке окружения. Это свойство может быть ссылкой на функцию, что позволяет -формировать контекст только по необходимости, при этом указанная функция будет -выполнена только один раз, при первом обращении. - -=back - -=head1 SELECTORS - -=begin text - -syntax::= selector => template - -selector::= ([>]segment-template[@class-name]) - -segment-template::= {'{'name:regular-expr'}'|segment-name} - -name::= \w+ - -segment-name::= \S+ - -class-name::= name[(::name)] - -url-template@class => template - -shoes => product/list -/shop//{action:*.}@My::Data::Product => product/{action} - -stuff >list => product/list -details => product/details - -=end text - - -=cut - diff -r 675cd1829255 -r feeb3bc4a818 Lib/IMPL/Web/Handler/View.pm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/IMPL/Web/Handler/View.pm Fri Oct 11 15:49:04 2013 +0400 @@ -0,0 +1,337 @@ +package IMPL::Web::Handler::View; +use strict; + +use Carp qw(carp); +use List::Util qw(first); +use IMPL::lang; +use IMPL::Const qw(:prop); +use IMPL::declare { + require => { + Factory => 'IMPL::Web::View::ObjectFactory', + HttpResponse => 'IMPL::Web::HttpResponse', + Loader => 'IMPL::Code::Loader', + ViewResult => '-IMPL::Web::ViewResult' + }, + base => [ + 'IMPL::Object' => undef, + 'IMPL::Object::Autofill' => '@_', + 'IMPL::Object::Serializable' => undef + ], + + props => [ + contentType => PROP_RO, + contentCharset => PROP_RO, + view => PROP_RO, + layout => PROP_RO, + selectors => PROP_RO, + defaultDocument => PROP_RW, + _selectorsCache => PROP_RW + ] +}; + +sub CTOR { + my ($this) = @_; + + $this->_selectorsCache([ map $this->ParseRule($_), @{$this->selectors || []} ]); +} + +sub Invoke { + my ( $this, $action, $next ) = @_; + + my $result = $next ? $next->($action) : undef; + + my ($model,$view,$template); + if( ref $result and eval { $result->isa(ViewResult) } ) { + $model = $result->model; + $view = $result; + $template = $result->template; + } else { + $model = $result; + $view = ViewResult->new(model => $model); + } + + my $vars = { + view => $view, + request => sub { $action }, + app => $action->application, + context => $action->context, + env => _cached($action->context->{environment}), + location => $action->context->{resourceLocation}, + layout => $this->layout + }; + + $this->view->display( + $model, + $template || $this->SelectView( $action, ref $model ), + $vars + ); + + my %responseParams = ( + type => $this->contentType, + charset => $this->contentCharset, + body => $this->view->display( + $model, + $template || $this->SelectView( $action, ref $model ), + $vars + ) + ); + + $responseParams{status} = $view->status if $view->status; + $responseParams{cookies} = $view->cookies if ref $view->cookies eq 'HASH'; + $responseParams{headers} = $view->headers if ref $view->headers eq 'HASH'; + + return HttpResponse->new( + %responseParams + ); +} + +sub _cached { + my $arg = shift; + + return $arg unless ref $arg eq 'CODE'; + + return sub { + ref $arg eq 'CODE' ? $arg = &$arg() : $arg; + } +} + +sub SelectView { + my ($this,$action) = @_; + + my @path; + + for(my $r = $action->context->{resource}; $r ; $r = $r->parent ) { + unshift @path, { + name => $r->id, + class => typeof($r->model) + }; + } + + @path = map { name => $_}, split /\/+/, $action->query->path_info() + unless (@path); + + return $this->MatchPath(\@path,$this->_selectorsCache) || $this->defaultDocument; +} + +sub ParseRule { + my ($this, $rule) = @_; + + my ($selector,$data) = split /\s+=>\s+/, $rule; + + my @parts; + my $first = 1; + my $weight = 0; + foreach my $part ( split /\//, $selector ) { + # если первым символом является / + # значит путь в селекторе абсолютный и не нужно + # добавлять "любой" элемент в начало + + if($part) { + $weight ++; + push @parts,{ any => 1 } if $first; + } else { + push @parts,{ any => 1 } unless $first; + next; + } + + my ($name,$class) = split /@/, $part; + + if ( my ( $varName, $rx ) = ( $name =~ m/^\{(?:(\w+)\:)?(.*)\}$/ ) ) { + #this is a regexp + + push @parts, { + rx => $rx, + var => $varName, + class => $class, + }; + } else { + push @parts, { + name => length($name) ? $name : undef, + class => $class, + }; + } + } continue { + $first = 0; + } + + return { selector => \@parts, data => $data, weight => $weight }; +} + +sub MatchPath { + my ($this,$path,$rules) = @_; + + $path ||= []; + $rules ||= []; + + my @next; + + foreach my $segment (@$path) { + foreach my $rule (@$rules) { + my @selector = @{$rule->{selector}}; + + my $part = shift @selector; + + # if this rule doesn't have a selector + next unless $part; + + if ($part->{any}) { + #keep the rule for the next try + push @next, $rule; + + $part = shift @selector while $part->{any}; + } + + my $newRule = { + selector => \@selector, + data => $rule->{data}, + weight => $rule->{weight}, + vars => { %{$rule->{vars} || {}} } + }; + + my $success = 1; + if (my $class = $part->{class}) { + $success = isclass($segment->{class},$class); + } + + if($success && (my $name = $part->{name})) { + $success = $segment->{name} eq $name; + } elsif ($success && (my $rx = $part->{rx})) { + if( my @captures = ($segment->{name} =~ m/($rx)/) ) { + $newRule->{vars}->{$part->{var}} = \@captures + if $part->{var}; + } else { + $success = 0; + } + } + + push @next, $newRule if $success; + + } + $rules = [@next]; + undef @next; + } + + my $result = ( + sort { + $b->{weight} <=> $a->{weight} + } + grep { + scalar(@{$_->{selector}}) == 0 + } + @$rules + )[0]; + + if($result) { + my $data = $result->{data}; + $data =~ s/{(\w+)(?:\:(\d+))?}/ + my ($name,$index) = ($1,$2 || 0); + + if ($result->{vars}{$name}) { + $result->{vars}{$name}[$index]; + } else { + ""; + } + /gex; + + return $data; + } else { + return; + } +} + +1; + +__END__ + +=pod + +=head1 NAME + +C - использует шаблоны для построения представления. + +=head1 SYNOPSIS + +=begin code xml + + + text/html + + + + IMPL::Config + view + + 1 + 1 + utf-8 + + .tt + global.tt + layouts + + default + + @HASH => dump + @My::Data::Product => product/info + {action:.*} @My::Data::Product => product/{action} + + + +=end code xml + +=head1 DESCRIPTION + +Подбирает шаблон для представления результата, полученного при выполнении следующего обработчика. При +выборе используется принцип похожий на селекторы C, основывающийся на именах ресурсов и их типах +данных. + +Данный обработчик понимает определенные свойства контекста: + +=over + +=item * C + +В данном свойстве может быть передана информация о текущем расположении ресурса, +для которого строится представление. Эта информация будет доступна в шаблоне +через свойство документа C. + +=item * C + +В данном совойстве контекста передается дополнительная информация об окружении +ресурса, например, которую задали родительские ресурсы. Использование данного +свойства позволяет не загромождать ресурс реализацией функциональности по +поддержке окружения. Это свойство может быть ссылкой на функцию, что позволяет +формировать контекст только по необходимости, при этом указанная функция будет +выполнена только один раз, при первом обращении. + +=back + +=head1 SELECTORS + +=begin text + +syntax::= selector => template + +selector::= ([>]segment-template[@class-name]) + +segment-template::= {'{'name:regular-expr'}'|segment-name} + +name::= \w+ + +segment-name::= \S+ + +class-name::= name[(::name)] + +url-template@class => template + +shoes => product/list +/shop//{action:*.}@My::Data::Product => product/{action} + +stuff >list => product/list +details => product/details + +=end text + + +=cut + diff -r 675cd1829255 -r feeb3bc4a818 Lib/IMPL/Web/View/TTContext.pm --- a/Lib/IMPL/Web/View/TTContext.pm Thu Oct 10 19:51:19 2013 +0400 +++ b/Lib/IMPL/Web/View/TTContext.pm Fri Oct 11 15:49:04 2013 +0400 @@ -110,6 +110,11 @@ $this->modules->{$file} = $info; return $cache->{$name} = $info; } else { + my $err = $@; + + for(my $t = $err; is($t,'Template::Exception'); $t = $t->info ) { + die $err unless $t->type eq Template::Constants::ERROR_FILE; + } $this->modules->{$file} = undef; } } diff -r 675cd1829255 -r feeb3bc4a818 Lib/IMPL/Web/View/TTControl.pm --- a/Lib/IMPL/Web/View/TTControl.pm Thu Oct 10 19:51:19 2013 +0400 +++ b/Lib/IMPL/Web/View/TTControl.pm Fri Oct 11 15:49:04 2013 +0400 @@ -14,7 +14,6 @@ props => [ context => PROP_RO, template => PROP_RO, - _stash => PROP_RO, id => { get => sub { shift->_stash->get('id') }, set => sub { shift->_stash->set('id',shift) } @@ -39,8 +38,10 @@ or die ArgException->new(context => 'A context is required'); $this->template($template) or die ArgException->new(template => 'A template is required'); - - $this->_stash($context->stash); +} + +sub _stash { + $_[0]->context->stash; } sub Render { @@ -52,17 +53,18 @@ sub AUTOLOAD { my ($prop) = ($AUTOLOAD =~ m/(\w+)$/); - die Exception->new("Control doesn't have method '$prop'") unless $prop=~/$AUTOLOAD_REGEX/; + die Exception->new("Method not found: $AUTOLOAD") unless $prop=~/$AUTOLOAD_REGEX/ and $_[0]; no strict 'refs'; my $method = sub { - if (@_ == 1) { - return shift->_stash->get($prop); - } elsif (@_ == 2) { - return shift->_stash->set($prop,shift); + my $that = shift; + if (@_ == 0) { + return $that->_stash->get($prop); + } elsif (@_ == 1) { + return $that->_stash->set($prop,shift); } else { - return shift->_stash->get([$prop,[@_]]); + return $that->_stash->get([$prop,[@_]]); } }; diff -r 675cd1829255 -r feeb3bc4a818 Lib/IMPL/Web/View/TTFactory.pm --- a/Lib/IMPL/Web/View/TTFactory.pm Thu Oct 10 19:51:19 2013 +0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,296 +0,0 @@ -package IMPL::Web::View::TTFactory; -use strict; - -use Template::Context(); - -use Carp qw(carp); -use IMPL::lang qw(:hash is); -use IMPL::Exception(); -use Scalar::Util qw(weaken); -use IMPL::Resources::Format qw(FormatMessage); -use IMPL::Resources::Strings(); - - -use IMPL::Const qw(:prop); -use IMPL::declare { - require => { - Loader => 'IMPL::Code::Loader', - Resources => 'IMPL::Resources', - OpException => '-IMPL::InvalidOperationException', - ArgException => '-IMPL::InvalidArgumentException' - }, - base => [ - 'IMPL::Object::Factory' => sub { - shift; - } - ], - props => [ - template => PROP_RW, - activation => PROP_RW, - context => PROP_RW, - baseLocation => PROP_RW, - base => PROP_RW, - registry => PROP_RO, - blocks => PROP_RO, - path => PROP_RO, - _instance => PROP_RW, - _labels => PROP_RW - ] -}; - -sub CTOR { - my ($this,$class,$template,$context,$path,$registry) = @_; - - die ArgException->new("A control class must be specified") - unless $class; - die ArgException->new("A template is required") unless $template; - - Loader->safe->Require($class) - unless ref $class ; - - $context ||= new Template::Context(); - my $baseLocation = join( '/', splice( @{[split(/\//,$path)]}, 0, -1 ) ); - - $this->activation($template->activation || 'new'); - $this->template($template); - $this->context($context); - $this->baseLocation($baseLocation); - $this->path($path); - $this->registry($registry); - - if (my $baseTplName = $template->extends) { - $baseTplName =~ s{^\./}{$baseLocation/}; - - my $base = $registry->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 $registry->Require($module); - } - }); - $this->LoadLabelsToContext($context); -} - -sub LoadLabelsToContext { - my ($this,$context) = @_; - - my %vars; - - %vars = %{$this->base->_labels || {}} - if $this->base; - $this->_labels(\%vars); - - if(my $fname = $this->registry->loader->ResolveFileName($this->path)) { - my $flabels = "$fname.labels"; - - if (-f $flabels) { - - my %labels; - $labels{default} = IMPL::Resources::Strings::ParseStringsMap($flabels); - - while(my($label,$text) = each %{$labels{default}}) { - $vars{$label} = sub { - my ($params) = @_; - my $locale = Resources->currentLocale; - - unless ($labels{$locale}) { - $labels{$locale} = -f "$fname.$locale" ? - IMPL::Resources::Strings::ParseStringsMap("$fname.$locale") : - {}; - } - - return FormatMessage(($labels{$locale}{$label} || $text),$params); - } - } - - } - } - $context->stash->update(\%vars); -} - -sub MergeParameters { - 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 $ctx = $this->context->clone(); - - return ($this->template, $ctx, $refProps); -} - -sub CreateObject { - my $this = shift; - - $this->activation eq 'singleton' ? - $this->_instance || $this->_instance($this->next::method(@_)) : - $this->next::method(@_); -} - -sub Render { - my ($this, $args) = @_; - - return $this->new()->Render($args); -} - -sub save { - die new IMPL::NotImplementedException("This class doesn't support serialization"); -} - -sub restore { - die new IMPL::NotImplementedException("This class doesn't support serialization"); -} - -1; - -__END__ - -=pod - -=head1 NAME - -C - фабрика элементов управления - -=head1 SYNOPSIS - -=begin code - -my $factory = new IMPL::Web::View::TTFactory( - 'IMPL::Web::View::TTControl', - $doc, - $context, - { - TRIM => 1 - }, - { - myprop => 'my value' - }, -); - -my $input1 = $factory->new('login', { class => "required" } ); - -my $propval = $input->nodeProperty('myprop'); # 'my value' - -=end code - -=begin text - -[% - this.appendChild( - my.org.input.new('login', class = this.errors('login') ? "invalid" : "" ) - ); -%] - -=end text - -=head1 DESCRIPTION - -C< Inherits L > - -Соединяет в себе шаблон и заранее подготовленный контекст, который будет базой -для создаваемых элементов управления. - -=head1 MEMBERS - -=over - -=item C<[get,set]template> - -Документ C который описывает элемент управления. См. C. - -=item C<[get,set]context> - -Контекст фабрики элементов управления, в этом контексте выполняет шаблон элемента управления при загрузке. -Далее в этом контексте будет выполнен блок инициализации при создании первого элемента управления. - -=item C<[get,set]opts> - -Параметры контекста элемента управления (ссылка на хеш). Каждый элемент управления при создании получает свой контекст, -который создает с данными параметрами и хранилищем переменных, дочерним к контексту фабрики. - -=item C<[get,set]nodeProperties> - -Ссылка на хеш со значениями свойств по умолчанию для создаваемого элемента управления. - -=item C<[get]instances> - -Количество созданных элементов управления данной фабрикой - -=item C<[override]MergeParameters($name,$nodeProps)> - -Превращает значения переданные методу C фабрики в параметры для создания элемента управления. - -=over - -=item C<$name> - -Имя создаваемого узла (C). - -=item C<$nodeProps> - -Ссылка на шех со значениями свойств узла. Данные значения будут совмещены со значениями из свойства C - -=back - -=item C<[override]CreateObject(@params)> - -Создает экземпляр элемента управления стандартным образом. Учитывает количество экземпляров и если это первый, -то производит дополнительную инициализацию контекста выполнив блок шаблона C. - -=item C<[inherited]new($name,$nodeProps)> - -Создает элемент управления с указанным именем и набором свойств. - -=back - -=cut \ No newline at end of file diff -r 675cd1829255 -r feeb3bc4a818 Lib/IMPL/Web/View/TTView.pm --- a/Lib/IMPL/Web/View/TTView.pm Thu Oct 10 19:51:19 2013 +0400 +++ b/Lib/IMPL/Web/View/TTView.pm Fri Oct 11 15:49:04 2013 +0400 @@ -1,6 +1,7 @@ package IMPL::Web::View::TTView; use strict; +use IMPL::lang qw(hashMerge); use IMPL::Const qw(:prop); use IMPL::declare { require => { @@ -13,10 +14,12 @@ ], props => [ options => PROP_RW, - view => PROP_RW, + viewBase => PROP_RW, + layoutBase => PROP_RW, layout => PROP_RW, tt_ext => PROP_RW, - includes => PROP_RW | PROP_LIST + includes => PROP_RW | PROP_LIST, + globals => PROP_RW ] }; @@ -30,7 +33,7 @@ my ($this,$model,$template,$args) = @_; my $context = Context->new($this->options); - my $layout = delete $args->{layout}; + my $layout = delete $args->{layout} || $this->layout; return $context->invoke_environment( sub { @@ -41,14 +44,14 @@ return $ctx->render($layout,$args); }, { - base => $this->layout, + base => $this->layoutBase, content => sub { $ctx->invoke_environment( sub { - return shift->display($model,$template,$args); + return shift->display_model($model,$template,$args); }, { - base => $this->view + base => $this->viewBase } ) } @@ -57,21 +60,24 @@ } else { return $ctx->invoke_environment( sub { - return $ctx->display($model,$template,$args); + return $ctx->display_model($model,$template,$args); }, { - base => $this->view + base => $this->viewBase } ); } - },{ - includes => scalar($this->includes), - tt_ext => 'tt', - document => {}, - debug => sub { - warn @_; - } - } + },hashMerge( + { + includes => scalar($this->includes), + tt_ext => $this->tt_ext, + document => {}, + debug => sub { + warn @_; + } + }, + $this->globals + ) ); } diff -r 675cd1829255 -r feeb3bc4a818 _test/temp.pl --- a/_test/temp.pl Thu Oct 10 19:51:19 2013 +0400 +++ b/_test/temp.pl Fri Oct 11 15:49:04 2013 +0400 @@ -16,8 +16,9 @@ RECURSION => 1000, COMPILE_DIR => '/tmp/ttc' }, - view => 'site', - layout => 'layout', + viewBase => 'site', + layoutBase => 'layout', + layout => 'default', includes => [ 'packages' ] @@ -42,8 +43,7 @@ print $view->display( $model, - 'product/view', - { layout => 'default' } + 'product/view' ), "\n"; print "render page: ",tv_interval($t,[gettimeofday]),"s\n"; @@ -52,8 +52,7 @@ $view->display( $model, - 'product/view', - { layout => 'default' } + 'product/view' ); print "2nd render page: ",tv_interval($t,[gettimeofday]),"s\n";