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 }