# HG changeset patch # User cin # Date 1380284907 -14400 # Node ID 72799d1211c5e8f262d1d0dc3574c3d9c3b9a929 # Parent f1d67615a5b1cc0f257c2bbe2b8eb0919a715fcd sync diff -r f1d67615a5b1 -r 72799d1211c5 Lib/IMPL/Web/View/TTContext.pm --- a/Lib/IMPL/Web/View/TTContext.pm Mon Sep 23 00:09:26 2013 +0400 +++ b/Lib/IMPL/Web/View/TTContext.pm Fri Sep 27 16:28:27 2013 +0400 @@ -2,7 +2,11 @@ use strict; use Template::Base; +use IMPL::lang qw(is); use IMPL::declare { + require => [ + Document => '-Template::Document' + ], base => { 'Template::Context' => '@_' } @@ -24,4 +28,138 @@ return $class->new($args); } -1; \ No newline at end of file +sub base { + my $this = shift; + + return @_ ? $this->stash->set('base', @_) : $this->stash->get('base'); +} + +sub tt_ext { + my $this = shift; + + return @_ ? $this->stash->set('tt_ext', @_) : $this->stash->get('tt_ext'); +} + +sub find_template { + my ($this,$name,@inc) = @_; + + push @inc, ""; + my $ext = $this->tt_ext || ""; + + foreach my $dir (@inc) { + my $file = "$dir/$name$ext"; + my $tt = eval { $this->template($file) }; + + return { + # if we load a block then we should use the current base directory + base => is($tt,Document) ? $dir : $this->base, + isDocument => is($tt,Document), + name => $name, + file => $file, + template => $tt + } if $tt; + } + + $this->throw(Template::Constants::ERROR_FILE, "$name: not found"); +} + +sub require { + my ($this,$name) = @_; + + return $this->stash->get([ 'require', [$name] ]); +} + +1; + +__END__ + +=pod + +=head1 NAME + +C - доработанная версия контекста + +=head1 DESCRIPTION + +Расширяет функции C + +=begin plantuml + +@startuml + +object FooContext { + document + this + model +} + +object BarContext { + document + this + model +} + +object FooFactoryContext { + require = function(){} + include = function(){} + labels = {...} + base = "my/app/view" + extends = "my/app/view/bar" +} + +object BarFactoryContext { + require = function(){} + include = function(){} + base = "my/app/view" + labels = {...} + extends = undefined +} + +object RegistryContext { + registry +} + +object DocumentFactoryContext { + require = function() {} + include = function() {} + labels = {...} + base = "" + extends = undefined +} + +object DocumentContext { + this +} + +FooFactoryContext --o BarFactoryContext +BarFactoryContext --o RegistryContext + +FooContext -right-o FooFactoryContext +BarContext -right-o BarFactoryContext + +DocumentFactoryContext -up-o RegistryContext +DocumentContext -left-o DocumentFactoryContext + +Document --> DocumentContext + +@enduml + +=end plantuml + +=head1 MEMBERS + +=head2 C<[get,set]base> + +Префикс пути для поиска шаблонов + +=head2 C + +Сначала пытается загрузить шаблон используя префикс C, затем без префикса. + +=head2 C + +Создает копию контекста, при этом C локализуется, таким образом +клонированный контекст имеет собственное пространство имен, вложенное в +пространство родительского контекста. + +=cut \ No newline at end of file diff -r f1d67615a5b1 -r 72799d1211c5 Lib/IMPL/Web/View/TTDocument.pm --- a/Lib/IMPL/Web/View/TTDocument.pm Mon Sep 23 00:09:26 2013 +0400 +++ b/Lib/IMPL/Web/View/TTDocument.pm Fri Sep 27 16:28:27 2013 +0400 @@ -21,12 +21,6 @@ $ctx ||= Template::Context->new(); return $template, $ctx, $vars; # context } - ], - props => [ - layout => PROP_RW, - layoutBase => PROP_RW, - registry => PROP_RW, - baseLocation => PROP_RW ] }; @@ -35,22 +29,26 @@ $this->layout( $template->layout ) unless $this->layout; $this->title( $template->title ) unless $this->title; - my $doc = $this; - weaken($doc); - $this->registry->context->stash->update({ - document => sub { $doc } - }); } sub Render { my ($this,$args) = @_; + + my $ctx = $this->context; $args ||= {}; $args->{document} = $this; + $args->{render} = sub { + my ($model,$factory) = @_; + + $factory = $ctx->require($factory) unless is($factory,TTFactory); + + return $factory->new({document => $this, model => $model})->Render(); + }; if ($this->layout) { - my $layout = $this->registry->Require(join('/',$this->layoutBase, $this->layout))->new(); + my $layout = $this->registry->Require($this->layout)->new(); my $next = $this->next::can(); @@ -124,19 +122,21 @@ @startuml -namespace IMPL::Web::View { +object "doc: TTDocument" as doc +object "docCtx: TTContext" as docctx +object "factory: TTFactory" as factory +object "registry: TTRegistry" as registry +object "control: TTControl" as ctl +object "ctlCtx: TTContext" as ctlctx - TTDocument "0 .. *" .. TTLoader - TTDocument --|> TTControl - - TTControl .. TTContext - - TTFactory -- TTControl - TTFactory .. TTContext - - class TTRegistry - -} +doc -up-> docctx +registry --> "0..*" factory +factory .> doc: <> +factory .up.> ctl: <> +docctx -up-> registry +ctl -> ctlctx +ctlctx --> registry +ctlctx --> doc @enduml diff -r f1d67615a5b1 -r 72799d1211c5 Lib/IMPL/Web/View/TTLoader.pm --- a/Lib/IMPL/Web/View/TTLoader.pm Mon Sep 23 00:09:26 2013 +0400 +++ b/Lib/IMPL/Web/View/TTLoader.pm Fri Sep 27 16:28:27 2013 +0400 @@ -5,6 +5,8 @@ use File::Spec(); use IMPL::Const qw(:prop); +use Scalar::Util qw(weaken); + use IMPL::declare { require => { Provider => 'Template::Provider', @@ -21,13 +23,9 @@ 'IMPL::Object::Serializable' => undef ], props => [ - options => PROP_RO, provider => PROP_RO, context => PROP_RO, - ext => PROP_RO, - layoutBase => PROP_RO, - isInitialized => PROP_RO, - initializer => PROP_RO, + registry => PROP_RO, _globals => PROP_RW ] }; @@ -58,17 +56,44 @@ $refOpts ||= {}; - $this->ext($args{ext}) if $args{ext}; - $this->initializer($args{initializer}) if $args{initializer}; + # to aviod cyclic references we need to do a copy of $refOpts + $refOpts->{LOAD_TEMPLATES} = Provider->new( { %$refOpts } ); + + my $ctx = Context->new( { %$refOpts } ); + $this->context($ctx); + $this->_globals(ref $args{globals} eq 'HASH' ? $args{globals} : {}); - $this->options($refOpts); - $this->layoutBase($args{layoutBase}) if $args{layoutBase}; + $ctx->tt_ext($args{ext} || '.tt'); + + $this->registry(TTRegitry->new($ctx)); - # to aviod cyclic references we need to do a copy of $refOpts - $refOpts->{LOAD_TEMPLATES} = $this->provider(Provider->new( { %$refOpts } )); + weaken($ctx); + weaken($this); + $ctx->stash->update({ + require => sub { + my ($modname) = @_; + + my @inc; + push @inc, $ctx->base if $ctx->base; + + my $ti = $ctx->find_template($name,@inc); + + require $this->registry->Require($ti); + }, + inclue => sub { + my ($name) = @_; + + my @inc; + push @inc, $ctx->base if $ctx->base; + + my $ti = $ctx->find_template($name,@inc); + + return $ctx->include($ti->{template}, {base => $ti->{base}} ); + } + }); - $this->context(Context->new($refOpts)); + } sub document { @@ -76,83 +101,9 @@ $vars ||= {}; - my $tt = $this->template($name); - - $this->_init(); - - my $opts = { %{ $this->options } }; - - my $ctx = $this->context->clone(); - - $ctx->stash->update($vars); - - my $registry = TTRegistry->new($this, $ctx); - - my $factory = TTFactory->new($tt->class || TTDocument, $tt, $ctx, $name, $registry); - - $vars->{registry} = $registry; - $vars->{layoutBase} = $this->layoutBase; - - return $factory->new( $vars ); -} - - -sub template { - my ($this,$name) = @_; - - $name =~ s/^\s+|\s+$//g; - - die ArgumentException->new("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 KeyNotFoundException->new($name); - } elsif (defined $error and $error == STATUS_ERROR) { - die Exception->new("Failed to load a template", $name, $tt); - } + my $factory = $this->registry->Require($name); - return $tt; -} - -sub ResolveFileName { - my ($this,$fname) = @_; - - $fname = $this->_appendExt($fname); - - my @files = grep -f , map File::Spec->catfile($_,$fname), @{$this->provider->paths()}; - return shift @files; -} - -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 Exception->new("Failed to process an initializer", $this->initializer, $e); - } - - $this->isInitialized(1); - } + return $factory->new(hashMerge($vars, $this->_globals)); } 1; @@ -179,7 +130,9 @@ ] }, ext => '.tt', - initializer => 'shared/global' + globals => { + images => '//cdn.mysite.net/static/images' + } ); diff -r f1d67615a5b1 -r 72799d1211c5 Lib/IMPL/Web/View/TTRegistry.pm --- a/Lib/IMPL/Web/View/TTRegistry.pm Mon Sep 23 00:09:26 2013 +0400 +++ b/Lib/IMPL/Web/View/TTRegistry.pm Fri Sep 27 16:28:27 2013 +0400 @@ -9,20 +9,17 @@ TTControl => '-IMPL::Web::View::TTControl' }, base => [ - 'IMPL::Object' => undef, - 'IMPL::Object::Disposable' => undef + 'IMPL::Object' => undef ], props => [ - loader => PROP_RW, context => PROP_RW, _cache => PROP_RW, ] }; sub CTOR { - my ($this,$loader,$context) = @_; - - $this->loader($loader); + my ($this,$context) = @_; + $this->context($context); $this->_cache({}); } @@ -39,7 +36,7 @@ $factory = TTFactory->new( $template->class || TTControl, $template, - $this->context, + $this->context->clone, $name, $this ); @@ -49,16 +46,6 @@ } } -sub Dispose { - my ($this) = @_; - - $this->_cache(undef); - $this->context(undef); - $this->loader(undef); - - $this->next::method(); -} - 1; __END__