Mercurial > pub > Impl
annotate Lib/IMPL/Web/View/TTContext.pm @ 351:cfd7570c2af2
working on TTView: created TTView class for rendering models
author | cin |
---|---|
date | Tue, 08 Oct 2013 17:40:35 +0400 |
parents | 86b470004d47 |
children | 675cd1829255 |
rev | line source |
---|---|
343 | 1 package IMPL::Web::View::TTContext; |
2 use strict; | |
3 use Template::Base; | |
348 | 4 use Carp qw(carp); |
349 | 5 use File::Spec(); |
6 use IMPL::Resources::Format qw(FormatMessage); | |
7 use IMPL::Resources::Strings(); | |
343 | 8 |
348 | 9 use IMPL::Exception(); |
349 | 10 use IMPL::lang qw(is typeof hashApply hashMerge); |
343 | 11 use IMPL::declare { |
348 | 12 require => { |
13 Document => '-Template::Document', | |
14 TypeKeyedCollection => 'IMPL::TypeKeyedCollection', | |
349 | 15 ArgException => '-IMPL::InvalidArgumentException', |
16 Resources => 'IMPL::Resources' | |
348 | 17 }, |
18 base => [ | |
343 | 19 'Template::Context' => '@_' |
348 | 20 ] |
343 | 21 }; |
22 | |
347 | 23 BEGIN { |
348 | 24 no strict 'refs'; |
347 | 25 foreach my $prop (qw( |
348 | 26 root |
347 | 27 base |
28 tt_ext | |
348 | 29 tt_cache |
347 | 30 parent |
31 prefix | |
348 | 32 cache |
33 includes | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
34 modules |
347 | 35 )) { |
36 my $t = $prop; | |
37 | |
348 | 38 *{__PACKAGE__ . '::' . $prop} = sub { |
347 | 39 my $this = shift; |
40 return @_ ? $this->stash->set($t, @_) : $this->stash->get($t); | |
41 } | |
42 } | |
43 } | |
44 | |
343 | 45 sub clone { |
46 my $this = shift; | |
347 | 47 my $params = shift; |
343 | 48 |
49 $this->localise(); | |
50 | |
51 my $args = { %{$this} }; | |
52 | |
53 $this->delocalise(); | |
54 | |
348 | 55 my $class = ref($this); |
343 | 56 |
57 delete $args->{CONFIG}; | |
58 | |
347 | 59 my $clone = $class->new($args); |
60 | |
61 $clone->stash->update($params) if $params; | |
62 | |
63 return $clone; | |
345 | 64 } |
65 | |
66 sub find_template { | |
348 | 67 my ($this,$name) = @_; |
68 | |
69 my $cache = $this->tt_cache; | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
70 |
348 | 71 $this->tt_cache($cache = {}) unless $cache; |
345 | 72 |
348 | 73 if(my $tpl = $cache->{$name}) { |
74 return $tpl; | |
75 } | |
76 | |
77 my @inc = ($this->base, @{$this->includes || []}); | |
78 | |
345 | 79 my $ext = $this->tt_ext || ""; |
348 | 80 |
81 my $file; | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
82 |
345 | 83 foreach my $dir (@inc) { |
348 | 84 $file = $dir ? "$dir/$name" : $name; |
85 | |
86 my $base = join('/',splice([split(/\/+/,$file)],0,-1)); | |
87 | |
88 $file = $ext ? "$file.$ext" : $file; | |
89 | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
90 if (exists($this->modules->{$file})) { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
91 my $info = $this->modules->{$file}; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
92 return $cache->{$name} = $info |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
93 if $info; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
94 } else { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
95 if( my $tt = eval { $this->template($file) } ) { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
96 my $info = { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
97 base => $base, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
98 labels => $this->load_labels($file), |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
99 template => $tt, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
100 initialized => 0 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
101 }; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
102 $this->modules->{$file} = $info; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
103 return $cache->{$name} = $info; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
104 } else { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
105 $this->modules->{$file} = undef; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
106 } |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
107 } |
345 | 108 } |
109 | |
110 $this->throw(Template::Constants::ERROR_FILE, "$name: not found"); | |
111 } | |
112 | |
347 | 113 sub display { |
114 my $this = shift; | |
115 my $model = shift; | |
348 | 116 my ($template, $args); |
347 | 117 |
118 if (ref $_[0] eq 'HASH') { | |
119 $args = shift; | |
120 } else { | |
121 $template = shift; | |
122 $args = shift; | |
123 } | |
124 | |
348 | 125 my $prefix = $this->prefix; |
126 | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
127 if (not(($args and delete $args->{_no_resolve}) or ref $model)) { |
348 | 128 $prefix = $prefix ? "${prefix}.${model}" : $model; |
129 $model = $this->resolve_model($model); | |
130 } else { | |
131 $prefix = ""; | |
132 } | |
133 | |
134 $template = $template ? $this->find_template($template) : $this->find_template_for($model); | |
135 | |
136 return $this->render( | |
137 $template, | |
138 hashApply( | |
139 { | |
140 prefix => $prefix, | |
141 model => $model, | |
142 }, | |
143 $args | |
144 ) | |
145 ); | |
146 } | |
147 | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
148 sub display_model { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
149 my $this = shift; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
150 my $model = shift; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
151 my ($template, $args); |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
152 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
153 if (ref $_[0] eq 'HASH') { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
154 $args = shift; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
155 } else { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
156 $template = shift; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
157 $args = shift; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
158 } |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
159 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
160 $args ||= {}; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
161 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
162 my $prefix = delete $args->{prefix} || $this->prefix; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
163 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
164 if (my $rel = delete $args->{rel}) { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
165 $prefix = $prefix ? "${prefix}.${rel}" : $rel; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
166 } |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
167 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
168 $template = $template ? $this->find_template($template) : $this->find_template_for($model); |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
169 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
170 return $this->render( |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
171 $template, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
172 hashApply( |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
173 { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
174 prefix => $prefix, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
175 model => $model, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
176 }, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
177 $args |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
178 ) |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
179 ); |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
180 } |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
181 |
348 | 182 sub invoke_environment { |
183 my ($this,$code,$env) = @_; | |
184 | |
185 $env ||= {}; | |
186 | |
187 my $out = eval { | |
188 $this->localise( | |
189 hashApply( | |
190 { | |
191 root => $this->root || $this, | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
192 modules => $this->modules || {}, |
348 | 193 cache => TypeKeyedCollection->new(), |
194 display => sub { | |
195 $this->display(@_); | |
196 }, | |
197 render => sub { | |
198 $this->render(@_); | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
199 }, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
200 display_model => sub { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
201 $this->display_model(@_); |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
202 }, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
203 tt_cache => {} |
348 | 204 }, |
205 $env | |
206 ) | |
207 ); | |
208 | |
209 &$code($this); | |
210 }; | |
211 | |
212 my $e = $@; | |
213 $this->delocalise(); | |
214 | |
215 die $e if $e; | |
216 | |
217 return $out; | |
218 } | |
219 | |
220 sub render { | |
221 my ($this,$template,$args) = @_; | |
222 | |
223 $args ||= {}; | |
224 | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
225 my $info = ref $template ? $template : $this->find_template($template); |
348 | 226 |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
227 if (ref($info) ne 'HASH') { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
228 carp "got an invalid template object: $info (" . ref($info) . ")"; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
229 $info = { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
230 template => $info, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
231 base => $this->base, |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
232 initialized => 1 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
233 }; |
348 | 234 } |
235 | |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
236 return $this->invoke_environment( |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
237 sub { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
238 my $ctx = shift; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
239 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
240 unless($info->{initialized}) { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
241 if(my $init = $info->{template}->blocks->{INIT}) { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
242 $info->{initialized} = 1; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
243 eval { |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
244 $ctx->visit($info->{template}->blocks); |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
245 $ctx->include($init); |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
246 }; |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
247 $ctx->leave(); |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
248 } |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
249 } |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
250 |
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
251 return $ctx->include($info->{template},$args); |
348 | 252 }, |
349 | 253 hashMerge( |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
254 $info->{labels} || {}, |
349 | 255 { |
351
cfd7570c2af2
working on TTView: created TTView class for rendering models
cin
parents:
349
diff
changeset
|
256 base => $info->{base}, |
349 | 257 parent => $this |
258 } | |
259 ) | |
348 | 260 ) |
261 } | |
262 | |
263 sub resolve_model { | |
264 my ($this,$prefix) = @_; | |
265 | |
266 die ArgException->new(prefix => "the prefix must be specified") | |
267 unless defined $prefix; | |
268 | |
269 #TODO handle DOM models | |
270 | |
271 my @comp = map { $_, 0 } grep length($_), split(/\.|\[(\d+)\]/, $prefix); | |
347 | 272 |
348 | 273 return $this->stash->get(['model',0,@comp]); |
274 } | |
275 | |
276 sub find_template_for { | |
277 my ($this,$model) = @_; | |
347 | 278 |
348 | 279 my $type = typeof($model); |
347 | 280 |
348 | 281 return $this->find_template('templates/plain') unless $type; |
282 | |
283 if (my $template = $this->cache->Get($type)) { | |
284 return $template; | |
285 } else { | |
286 | |
287 no strict 'refs'; | |
288 | |
289 my @isa = $type; | |
290 | |
291 while (@isa) { | |
292 my $sclass = shift @isa; | |
293 | |
294 (my $name = $sclass) =~ s/:+/_/g; | |
295 | |
296 $template = $this->find_template("templates/$name"); | |
297 | |
298 if ($template) { | |
299 $this->cache->Set($sclass,$template); | |
300 return $template; | |
301 } | |
302 | |
303 push @isa, @{"${sclass}::ISA"}; | |
304 } | |
305 | |
306 } | |
307 | |
308 return; | |
347 | 309 } |
310 | |
349 | 311 sub get_real_file { |
312 my ($this,$fname) = @_; | |
313 | |
314 my @path = split(/\/+/,$fname); | |
315 | |
316 foreach my $provider (@{$this->load_templates || []}) { | |
317 foreach my $dir (@{$provider->paths || []}) { | |
318 my $realName = File::Spec->catfile($dir,@path); | |
319 return $realName if -f $realName; | |
320 } | |
321 } | |
322 } | |
323 | |
324 sub load_labels { | |
325 my ($this,$fname) = @_; | |
326 | |
327 $fname = $this->get_real_file($fname); | |
328 | |
329 my %vars; | |
330 | |
331 my $flabels = "$fname.labels"; | |
332 | |
333 if (-f $flabels) { | |
334 | |
335 my %labels; | |
336 $labels{default} = IMPL::Resources::Strings::ParseStringsMap($flabels); | |
337 | |
338 while(my($label,$text) = each %{$labels{default}}) { | |
339 $vars{$label} = sub { | |
340 my ($params) = @_; | |
341 my $locale = Resources->currentLocale; | |
342 | |
343 unless ($labels{$locale}) { | |
344 $labels{$locale} = -f "$fname.$locale" ? | |
345 IMPL::Resources::Strings::ParseStringsMap("$fname.$locale") : | |
346 {}; | |
347 } | |
348 | |
349 return FormatMessage(($labels{$locale}{$label} || $text),$params); | |
350 } | |
351 } | |
352 } | |
353 | |
354 return \%vars; | |
355 } | |
356 | |
345 | 357 1; |
358 | |
359 __END__ | |
360 | |
361 =pod | |
362 | |
363 =head1 NAME | |
364 | |
365 C<IMPL::Web::View::TTContext> - доработанная версия контекста | |
366 | |
367 =head1 DESCRIPTION | |
368 | |
369 Расширяет функции C<Template::Context> | |
370 | |
371 =begin plantuml | |
372 | |
373 @startuml | |
374 | |
348 | 375 object RootContext { |
345 | 376 document |
346 | 377 globals |
345 | 378 } |
379 | |
380 object DocumentContext { | |
346 | 381 base |
382 extends | |
383 } | |
384 | |
385 object ControlContext { | |
386 base | |
387 extends | |
345 | 388 } |
389 | |
348 | 390 RootContext o-- DocumentContext |
391 RootContext o-- ControlContext | |
345 | 392 |
346 | 393 Document -- DocumentContext |
394 Control - ControlContext | |
345 | 395 |
348 | 396 Loader . RootContext: <<creates>> |
346 | 397 Loader . Document: <<creates>> |
398 Loader -up- Registry | |
345 | 399 |
400 @enduml | |
401 | |
402 =end plantuml | |
403 | |
404 =head1 MEMBERS | |
405 | |
406 =head2 C<[get,set]base> | |
407 | |
408 Префикс пути для поиска шаблонов | |
409 | |
410 =head2 C<template($name)> | |
411 | |
412 Сначала пытается загрузить шаблон используя префикс C<base>, затем без префикса. | |
413 | |
414 =head2 C<clone()> | |
415 | |
416 Создает копию контекста, при этом C<stash> локализуется, таким образом | |
417 клонированный контекст имеет собственное пространство имен, вложенное в | |
418 пространство родительского контекста. | |
419 | |
420 =cut |