Mercurial > pub > Impl
comparison 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 |
comparison
equal
deleted
inserted
replaced
300:bf3af33b9003 | 301:aeeb57a12046 |
---|---|
1 package IMPL::Web::View::TTFactory; | 1 package IMPL::Web::View::TTFactory; |
2 use strict; | 2 use strict; |
3 | 3 |
4 use Template::Context(); | 4 use Template::Context(); |
5 | 5 |
6 use Carp qw(carp); | |
6 use IMPL::lang qw(:hash); | 7 use IMPL::lang qw(:hash); |
7 use IMPL::Exception(); | 8 use IMPL::Exception(); |
8 use Scalar::Util qw(weaken); | 9 use Scalar::Util qw(weaken); |
9 | 10 |
10 | 11 |
11 use IMPL::Const qw(:prop); | 12 use IMPL::Const qw(:prop); |
12 use IMPL::declare { | 13 use IMPL::declare { |
13 require => { | 14 require => { |
14 Loader => 'IMPL::Code::Loader' | 15 Loader => 'IMPL::Code::Loader', |
16 OpException => '-IMPL::InvalidOperationException', | |
17 ArgException => '-IMPL::InvalidArgumentException' | |
15 }, | 18 }, |
16 base => [ | 19 base => [ |
17 'IMPL::Object::Factory' => sub { | 20 'IMPL::Object::Factory' => sub { |
18 shift; | 21 shift->class || 'IMPL::Web::View::TTControl'; |
19 } | 22 } |
20 ], | 23 ], |
21 props => [ | 24 props => [ |
22 template => PROP_RW, | 25 template => PROP_RW, |
23 context => PROP_RW, | 26 context => PROP_RW, |
24 instances => PROP_RW, | 27 instances => PROP_RW, |
25 baseLocation => PROP_RW, | 28 baseLocation => PROP_RW, |
26 require => PROP_RO | 29 base => PROP_RW, |
30 require => PROP_RO, | |
31 blocks => PROP_RO, | |
32 initialized => PROP_RO | |
27 ] | 33 ] |
28 }; | 34 }; |
29 | 35 |
30 sub CTOR { | 36 sub CTOR { |
31 my ($this,$class,$template,$context,$baseLocation,$require) = @_; | 37 my ($this,$template,$context,$baseLocation,$require) = @_; |
32 | 38 |
33 die IMPL::ArgumentException("A template is required") unless $template; | 39 die ArgException->new("A template is required") unless $template; |
34 | 40 |
35 Loader->safe->Require($class) | 41 Loader->safe->Require($this->factory) |
36 if $class and not ref $class; | 42 if $this->factory and not ref $this->factory; |
37 | 43 |
38 $context ||= new Template::Context(); | 44 $context ||= new Template::Context(); |
39 | 45 |
40 $this->template($template); | 46 $this->template($template); |
41 $this->context($context); | 47 $this->context($context); |
42 $this->baseLocation($baseLocation); | 48 $this->baseLocation($baseLocation); |
43 $this->instances(0); | 49 $this->instances(0); |
44 $this->require($require); | 50 $this->require($require); |
51 | |
52 if (my $baseTplName = $template->extends) { | |
53 $baseTplName =~ s{^\./}{$baseLocation/}; | |
54 | |
55 my $base = &$require($baseTplName) | |
56 or die OpException->new("The specified base template isn't found"); | |
57 | |
58 $this->base($base); | |
59 | |
60 $this->blocks(hashMerge($base->blocks, $template->blocks)); | |
61 | |
62 # блок BASE должен меняться в процессе выполнения, в зависимости от | |
63 # шаблона, который рендерится, по мере перехода в BASE | |
64 | |
65 my @baseStack; | |
66 | |
67 $this->blocks->{BASE} = sub { | |
68 my $ctx = shift; | |
69 | |
70 die OpException->new("This tamplate doesn't have a base template") | |
71 unless $base; | |
72 | |
73 push @baseStack, $base; | |
74 my $block = $base->template->block; | |
75 | |
76 $base = $base->base; | |
77 | |
78 my $result = eval { | |
79 $ctx->process($block); | |
80 }; | |
81 my $e = $@; | |
82 | |
83 $base = pop @baseStack; | |
84 | |
85 die $e if $e; | |
86 return $result; | |
87 }; | |
88 } else { | |
89 $this->blocks( $template->blocks ); | |
90 } | |
91 | |
92 if(my $blocks = $this->blocks) { | |
93 while (my ($name,$block) = each %$blocks) { | |
94 $context->define_block($name,$block); | |
95 } | |
96 } | |
97 | |
98 $context->stash->update({ require => sub { | |
99 my ($module) = @_; | |
100 | |
101 $module =~ s/^\.\//$baseLocation\//; | |
102 return $require->($module); | |
103 }}); | |
45 } | 104 } |
46 | 105 |
47 sub MergeParameters { | 106 sub MergeParameters { |
48 my ($this,$refProps) = @_; | 107 my $this = shift; |
108 my $refProps = shift || {}; | |
109 | |
110 unless (ref($refProps) eq 'HASH') { | |
111 carp "Passing control name through the first parameter is deprecated"; | |
112 my $name = $refProps; | |
113 $refProps = shift; | |
114 $refProps->{name} ||= $name; | |
115 } | |
49 | 116 |
50 $refProps->{factory} = $this; | 117 $refProps->{factory} = $this; |
51 | |
52 my $baseLocation = $this->baseLocation; | |
53 | |
54 my $ctx = $this->CloneContext(); | 118 my $ctx = $this->CloneContext(); |
55 | 119 |
56 my $require = $this->require; | |
57 | |
58 $ctx->stash->update({ | |
59 require => sub { | |
60 my ($module) = @_; | |
61 | |
62 $module =~ s/^\.\//$baseLocation\//; | |
63 return $require->($module); | |
64 } | |
65 }); | |
66 | |
67 return ($this->template, $ctx, $refProps); | 120 return ($this->template, $ctx, $refProps); |
68 } | 121 } |
69 | 122 |
70 sub CreateObject { | 123 sub CreateObject { |
71 my $this = shift; | 124 my $this = shift; |
72 | 125 |
73 my $count = $this->instances; | 126 $this->InitOnDemand(); |
74 | 127 |
75 unless($count) { | 128 my $instance = $this->SUPER::CreateObject(@_); |
76 # нужно выполнить именно блок INIT шаблона при создании первого экземпляра | 129 |
130 my $count = $this->instances; | |
131 $count++; | |
132 $this->instances($count); | |
133 | |
134 return $instance; | |
135 } | |
136 | |
137 sub InitOnDemand { | |
138 my ($this) = @_; | |
139 | |
140 unless ($this->initialized) { | |
141 $this->initialized(1); | |
142 | |
143 $this->base->InitOnDemand() | |
144 if $this->base; | |
145 | |
77 if (my $init = $this->template->blocks->{INIT}) { | 146 if (my $init = $this->template->blocks->{INIT}) { |
78 $this->context->process($init); | 147 $this->context->process($init); |
79 } | 148 } |
80 | 149 } |
81 $this->context->visit($this->template->blocks); | |
82 } | |
83 | |
84 my $instance = $this->SUPER::CreateObject(@_); | |
85 | |
86 $instance->InitInstance(); | |
87 | |
88 $count++; | |
89 $this->instances($count); | |
90 | |
91 return $instance; | |
92 } | 150 } |
93 | 151 |
94 sub CloneContext { | 152 sub CloneContext { |
95 my ($this) = @_; | 153 my ($this) = @_; |
96 | 154 |
100 delete $args->{CONFIG}; | 158 delete $args->{CONFIG}; |
101 | 159 |
102 $this->context->delocalise(); | 160 $this->context->delocalise(); |
103 | 161 |
104 return Template::Context->new($args); | 162 return Template::Context->new($args); |
163 } | |
164 | |
165 sub Render { | |
166 my ($this, $args) = @_; | |
167 | |
168 return $this->new()->Render($args); | |
105 } | 169 } |
106 | 170 |
107 sub save { | 171 sub save { |
108 die new IMPL::NotImplementedException("This class doesn't support serialization"); | 172 die new IMPL::NotImplementedException("This class doesn't support serialization"); |
109 } | 173 } |