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