comparison Lib/IMPL/Web/View/TTContext.pm @ 348:f116cd9fe7d9

working on TTView: pre-alpha version
author cin
date Thu, 03 Oct 2013 19:48:57 +0400
parents 3eafa6fefa9f
children 86b470004d47
comparison
equal deleted inserted replaced
347:3eafa6fefa9f 348:f116cd9fe7d9
1 package IMPL::Web::View::TTContext; 1 package IMPL::Web::View::TTContext;
2 use strict; 2 use strict;
3 use Template::Base; 3 use Template::Base;
4 4 use Carp qw(carp);
5 use IMPL::lang qw(is); 5
6 use IMPL::Exception();
7 use IMPL::lang qw(is typeof hashApply);
6 use IMPL::declare { 8 use IMPL::declare {
7 require => [ 9 require => {
8 Document => '-Template::Document' 10 Document => '-Template::Document',
9 ], 11 TypeKeyedCollection => 'IMPL::TypeKeyedCollection',
10 base => { 12 ArgException => "-IMPL::InvalidArgumentException"
13 },
14 base => [
11 'Template::Context' => '@_' 15 'Template::Context' => '@_'
12 } 16 ]
13 }; 17 };
14 18
15 BEGIN { 19 BEGIN {
16 no strict 'ref'; 20 no strict 'refs';
17 foreach my $prop (qw( 21 foreach my $prop (qw(
22 root
18 base 23 base
19 tt_ext 24 tt_ext
20 shared 25 tt_cache
21 parent 26 parent
22 prefix 27 prefix
28 cache
29 includes
23 )) { 30 )) {
24 my $t = $prop; 31 my $t = $prop;
25 32
26 *{__PACKAGE__ . '::' . $name} = sub { 33 *{__PACKAGE__ . '::' . $prop} = sub {
27 my $this = shift; 34 my $this = shift;
28 return @_ ? $this->stash->set($t, @_) : $this->stash->get($t); 35 return @_ ? $this->stash->set($t, @_) : $this->stash->get($t);
29 } 36 }
30 } 37 }
31 } 38 }
38 45
39 my $args = { %{$this} }; 46 my $args = { %{$this} };
40 47
41 $this->delocalise(); 48 $this->delocalise();
42 49
43 my $class = typeof($this); 50 my $class = ref($this);
44 51
45 delete $args->{CONFIG}; 52 delete $args->{CONFIG};
46 53
47 my $clone = $class->new($args); 54 my $clone = $class->new($args);
48 55
50 57
51 return $clone; 58 return $clone;
52 } 59 }
53 60
54 sub find_template { 61 sub find_template {
55 my ($this,$name,@inc) = @_; 62 my ($this,$name) = @_;
56 63
57 push @inc, ""; 64 my $cache = $this->tt_cache;
65 $this->tt_cache($cache = {}) unless $cache;
66
67 if(my $tpl = $cache->{$name}) {
68 return $tpl;
69 }
70
71 my @inc = ($this->base, @{$this->includes || []});
72
58 my $ext = $this->tt_ext || ""; 73 my $ext = $this->tt_ext || "";
74
75 my $file;
59 76
60 foreach my $dir (@inc) { 77 foreach my $dir (@inc) {
61 my $file = "$dir/$name$ext"; 78 $file = $dir ? "$dir/$name" : $name;
79
80 my $base = join('/',splice([split(/\/+/,$file)],0,-1));
81
82 $file = $ext ? "$file.$ext" : $file;
83
84 warn "lookup: $file";
85
62 my $tt = eval { $this->template($file) }; 86 my $tt = eval { $this->template($file) };
63 87
64 return { 88 return $cache->{$name} = {
65 # if we load a block then we should use the current base directory 89 base => $base,
66 base => is($tt,Document) ? $dir : $this->base, 90 template => $tt,
67 isDocument => is($tt,Document),
68 name => $name,
69 file => $file,
70 template => $tt
71 } if $tt; 91 } if $tt;
72 } 92 }
73 93
74 $this->throw(Template::Constants::ERROR_FILE, "$name: not found"); 94 $this->throw(Template::Constants::ERROR_FILE, "$name: not found");
75 }
76
77 sub require {
78 my ($this,$name) = @_;
79
80 return $this->stash->get([ 'require', [$name] ]);
81 } 95 }
82 96
83 sub display { 97 sub display {
84 my $this = shift; 98 my $this = shift;
85 my $model = shift; 99 my $model = shift;
86 my $template, $args; 100 my ($template, $args);
87 101
88 if (ref $_[0] eq 'HASH') { 102 if (ref $_[0] eq 'HASH') {
89 $args = shift; 103 $args = shift;
90 } else { 104 } else {
91 $template = shift; 105 $template = shift;
92 $args = shift; 106 $args = shift;
93 } 107 }
94 108
95 my $prefix = $this->prefix 109 my $prefix = $this->prefix;
96 110
97 my $cloned = $this->clone({ 111 if (not(($args and delete $args->{'-no-resolve'}) or ref $model)) {
98 prefix => $prefix, 112 $prefix = $prefix ? "${prefix}.${model}" : $model;
99 shared => $this->shared || $this, 113 $model = $this->resolve_model($model);
100 parent => $this 114 } else {
101 }); 115 $prefix = "";
102 116 }
103 117
104 118 $template = $template ? $this->find_template($template) : $this->find_template_for($model);
119
120 return $this->render(
121 $template,
122 hashApply(
123 {
124 prefix => $prefix,
125 model => $model,
126 },
127 $args
128 )
129 );
130 }
131
132 sub invoke_environment {
133 my ($this,$code,$env) = @_;
134
135 $env ||= {};
136
137 my $out = eval {
138 $this->localise(
139 hashApply(
140 {
141 root => $this->root || $this,
142 cache => TypeKeyedCollection->new(),
143 display => sub {
144 $this->display(@_);
145 },
146 render => sub {
147 $this->render(@_);
148 }
149 },
150 $env
151 )
152 );
153
154 &$code($this);
155 };
156
157 my $e = $@;
158 $this->delocalise();
159
160 die $e if $e;
161
162 return $out;
163 }
164
165 sub render {
166 my ($this,$template,$args) = @_;
167
168 $args ||= {};
169
170 #TODO handle classes
171
172 my $base;
173
174 $template = $this->find_template($template) unless ref $template;
175
176 if (ref $template eq 'HASH') {
177 $base = $template->{base};
178 $template = $template->{template};
179 } else {
180 carp "got an invalid template object: $template";
181 $base = $this->base;
182 }
183
184 return $this->invoke_environment(
185 sub {
186 return shift->include($template,$args);
187 },
188 {
189 base => $base,
190 parent => $this
191 }
192 )
193 }
194
195 sub resolve_model {
196 my ($this,$prefix) = @_;
197
198 die ArgException->new(prefix => "the prefix must be specified")
199 unless defined $prefix;
200
201 #TODO handle DOM models
202
203 my @comp = map { $_, 0 } grep length($_), split(/\.|\[(\d+)\]/, $prefix);
204
205 return $this->stash->get(['model',0,@comp]);
206 }
207
208 sub find_template_for {
209 my ($this,$model) = @_;
210
211 my $type = typeof($model);
212
213 return $this->find_template('templates/plain') unless $type;
214
215 if (my $template = $this->cache->Get($type)) {
216 return $template;
217 } else {
218
219 no strict 'refs';
220
221 my @isa = $type;
222
223 while (@isa) {
224 my $sclass = shift @isa;
225
226 (my $name = $sclass) =~ s/:+/_/g;
227
228 $template = $this->find_template("templates/$name");
229
230 if ($template) {
231 $this->cache->Set($sclass,$template);
232 return $template;
233 }
234
235 push @isa, @{"${sclass}::ISA"};
236 }
237
238 }
239
240 return;
105 } 241 }
106 242
107 1; 243 1;
108 244
109 __END__ 245 __END__
120 256
121 =begin plantuml 257 =begin plantuml
122 258
123 @startuml 259 @startuml
124 260
125 object SharedContext { 261 object RootContext {
126 document 262 document
127 globals 263 globals
128 } 264 }
129 265
130 object DocumentContext { 266 object DocumentContext {
135 object ControlContext { 271 object ControlContext {
136 base 272 base
137 extends 273 extends
138 } 274 }
139 275
140 SharedContext o-- DocumentContext 276 RootContext o-- DocumentContext
141 SharedContext o-- ControlContext 277 RootContext o-- ControlContext
142 278
143 Document -- DocumentContext 279 Document -- DocumentContext
144 Control - ControlContext 280 Control - ControlContext
145 281
146 Loader . SharedContext: <<creates>> 282 Loader . RootContext: <<creates>>
147 Loader . Document: <<creates>> 283 Loader . Document: <<creates>>
148 Loader -up- Registry 284 Loader -up- Registry
149 285
150 @enduml 286 @enduml
151 287