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