Mercurial > pub > Impl
annotate Lib/IMPL/Web/TT/Document.pm @ 146:60fd224f3e3c
fixed cgi parameters processing
Added parameters inflating feature
Added preprocessed templates, reformatting output
author | wizard |
---|---|
date | Mon, 26 Jul 2010 03:19:06 +0400 |
parents | bd10093bb122 |
children | 4369d5458bb6 |
rev | line source |
---|---|
77 | 1 package IMPL::Web::TT::Document; |
49 | 2 use strict; |
3 use warnings; | |
4 | |
75 | 5 use base qw(IMPL::DOM::Document IMPL::Object::Disposable); |
49 | 6 use Template::Context; |
7 use Template::Provider; | |
8 use IMPL::Class::Property; | |
9 use File::Spec; | |
77 | 10 use Scalar::Util qw(blessed); |
107 | 11 use IMPL::Web::TT::Collection; |
12 use IMPL::Web::TT::Control; | |
109
ddf0f037d460
IMPL::DOM::Node updated to support TT, (added childNodesRef and selectNodesRef for using from TT)
wizard
parents:
108
diff
changeset
|
13 use Carp; |
49 | 14 |
15 BEGIN { | |
77 | 16 private property _provider => prop_all; |
17 private property _context => prop_all; | |
134 | 18 public property cache => prop_all; |
77 | 19 public property template => prop_get | owner_set; |
20 public property presenter => prop_all, { validate => \&_validatePresenter }; | |
146 | 21 public property preprocess => prop_all | prop_list, |
108 | 22 private property _controlClassMap => prop_all; |
49 | 23 } |
24 | |
25 our %CTOR = ( | |
75 | 26 'IMPL::DOM::Document' => sub { nodeName => 'document' } |
49 | 27 ); |
28 | |
107 | 29 sub CTOR { |
134 | 30 my ($this,%args) = @_; |
107 | 31 |
108 | 32 $this->_controlClassMap({}); |
33 $this->registerControlClass( Control => 'IMPL::Web::TT::Control' ); | |
34 $this->appendChild( $this->Create(body => 'IMPL::Web::TT::Collection') ); | |
35 $this->appendChild( $this->Create(head => 'IMPL::Web::TT::Collection') ); | |
134 | 36 $this->cache($args{cache}) if $args{cache}; |
146 | 37 $this->preprocess($args{preprocess}) if $args{preprocess}; |
108 | 38 } |
39 | |
40 sub CreateControl { | |
41 my ($this,$name,$class,$args) = @_; | |
42 | |
43 $args = {} unless ref $args eq 'HASH'; | |
44 | |
45 if (my $info = $this->_controlClassMap->{$class}) { | |
46 my %nodeArgs = (%{$info->{args}},%$args); | |
47 $nodeArgs{controlClass} = $class; | |
48 | |
49 return $this->Create($name,$info->{type},\%nodeArgs); | |
50 } else { | |
51 die new IMPL::Exception('A control is\'t registered', $class, $name); | |
52 } | |
107 | 53 } |
54 | |
77 | 55 sub provider { |
49 | 56 my ($this,%args) = @_; |
57 | |
77 | 58 if (my $provider = $this->_provider) { |
49 | 59 return $provider; |
60 } else { | |
77 | 61 return $this->_provider(new Template::Provider( |
49 | 62 \%args |
63 )); | |
64 } | |
65 } | |
66 | |
77 | 67 sub context { |
49 | 68 my ($this) = @_; |
69 | |
77 | 70 if (my $ctx = $this->_context) { |
49 | 71 return $ctx; |
72 } else { | |
77 | 73 return $this->_context ( |
49 | 74 new Template::Context( |
75 VARIABLES => { | |
77 | 76 document => $this, |
77 this => $this, | |
78 render => sub { | |
79 $this->_process(@_); | |
108 | 80 } |
49 | 81 }, |
82 RECURSION => 1, | |
77 | 83 LOAD_TEMPLATES => [$this->provider] |
49 | 84 ) |
85 ) | |
86 } | |
87 } | |
88 | |
127
0dce0470a3d8
In the IMPL::Web::ControllerUnit added the ability to notify a form about a wrong data from a transaction
wizard
parents:
121
diff
changeset
|
89 sub resolveVar { |
0dce0470a3d8
In the IMPL::Web::ControllerUnit added the ability to notify a form about a wrong data from a transaction
wizard
parents:
121
diff
changeset
|
90 my ($this,$var) = @_; |
0dce0470a3d8
In the IMPL::Web::ControllerUnit added the ability to notify a form about a wrong data from a transaction
wizard
parents:
121
diff
changeset
|
91 |
0dce0470a3d8
In the IMPL::Web::ControllerUnit added the ability to notify a form about a wrong data from a transaction
wizard
parents:
121
diff
changeset
|
92 return $this->context->stash->get($var); |
0dce0470a3d8
In the IMPL::Web::ControllerUnit added the ability to notify a form about a wrong data from a transaction
wizard
parents:
121
diff
changeset
|
93 } |
0dce0470a3d8
In the IMPL::Web::ControllerUnit added the ability to notify a form about a wrong data from a transaction
wizard
parents:
121
diff
changeset
|
94 |
108 | 95 sub registerControlClass { |
96 my ($this, $controlClass, $type, $args) = @_; | |
97 | |
98 $type ||= 'IMPL::Web::TT::Control'; | |
99 | |
100 die new IMPL::InvalidArgumentException("A controlClass must be a single word",$controlClass) unless $controlClass =~ /^\w+$/; | |
107 | 101 |
117 | 102 eval "require $type; 1;" or die new IMPL::Exception("Failed to load a module",$type,"$@") unless eval { $type->can('new') }; |
108 | 103 |
104 die new IMPL::InvalidArgumentException("A type must be subclass of IMPL::DOM::Node",$type) unless $type->isa('IMPL::DOM::Node'); | |
105 | |
146 | 106 # resolve template name to a real template |
107 $args->{template} = $this->context->template($args->{template}) if $args->{template}; | |
108 | |
108 | 109 $this->_controlClassMap->{$controlClass} = { |
110 controlClass => $controlClass, | |
111 type => $type, | |
112 args => ref $args eq 'HASH' ? $args : {} | |
113 }; | |
107 | 114 } |
115 | |
117 | 116 sub require { |
117 my ($this,$template) = @_; | |
118 | |
119 my $doc = $this->context->template($template); | |
120 | |
121 die new IMPL::InvalidOperationException("A specified template isn't a document",$template) unless eval{ $doc -> isa('Template::Document') }; | |
122 | |
123 my $controlClass = $doc->class; | |
124 my $type = $doc->nativeType; | |
125 my $controlTemplate; | |
126 my $out = ""; | |
127 | |
128 die new IMPL::InvalidOperationException("A specified template isn't a control",$template) unless $controlClass; | |
129 | |
130 if (not $this->isControlClass($controlClass)) { | |
131 if ($doc->template) { | |
132 $controlTemplate = $doc->blocks()->{$doc->template} || $this->context->template($doc->template); | |
146 | 133 $out = $this->context->include($doc); |
117 | 134 } else { |
135 $controlTemplate = $doc; | |
136 } | |
137 $this->registerControlClass($controlClass,$type,{ template => $controlTemplate } ); | |
138 } | |
139 | |
140 return $out; | |
141 } | |
142 | |
109
ddf0f037d460
IMPL::DOM::Node updated to support TT, (added childNodesRef and selectNodesRef for using from TT)
wizard
parents:
108
diff
changeset
|
143 sub isControlClass { |
ddf0f037d460
IMPL::DOM::Node updated to support TT, (added childNodesRef and selectNodesRef for using from TT)
wizard
parents:
108
diff
changeset
|
144 my ($this,$name) = @_; |
ddf0f037d460
IMPL::DOM::Node updated to support TT, (added childNodesRef and selectNodesRef for using from TT)
wizard
parents:
108
diff
changeset
|
145 return $this->_controlClassMap->{$name} ? 1 : 0; |
ddf0f037d460
IMPL::DOM::Node updated to support TT, (added childNodesRef and selectNodesRef for using from TT)
wizard
parents:
108
diff
changeset
|
146 } |
ddf0f037d460
IMPL::DOM::Node updated to support TT, (added childNodesRef and selectNodesRef for using from TT)
wizard
parents:
108
diff
changeset
|
147 |
107 | 148 sub _getControls { |
149 my ($this) = @_; | |
150 | |
151 my ($node) = $this->selectNodes('controls'); | |
152 return $node; | |
153 } | |
154 | |
77 | 155 sub _validatePresenter { |
156 my ($this,$value) = @_; | |
157 | |
158 die new IMPL::InvalidArgumentException("A view object is required") unless blessed($value) and $value->isa('Template::View'); | |
159 } | |
160 | |
161 sub LoadFile { | |
121 | 162 my ($this,$filePath,$encoding,@includes) = @_; |
49 | 163 |
164 die new IMPL::InvalidArgumentException("A filePath parameter is required") unless $filePath; | |
165 | |
166 $encoding ||= 'utf8'; | |
167 | |
77 | 168 $this->_context(undef); |
169 $this->_provider(undef); | |
49 | 170 |
171 my ($vol,$dir,$fileName) = File::Spec->splitpath($filePath); | |
172 | |
173 my $inc = File::Spec->catpath($vol,$dir,''); | |
174 | |
77 | 175 $this->provider( |
49 | 176 ENCODING => $encoding, |
177 INTERPOLATE => 1, | |
178 PRE_CHOMP => 1, | |
179 POST_CHOMP => 1, | |
146 | 180 TRIM => 0, |
134 | 181 COMPILE_EXT => $this->cache ? '.ttc' : undef, |
182 COMPILE_DIR => $this->cache, | |
121 | 183 INCLUDE_PATH => [$inc,@includes] |
49 | 184 ); |
185 | |
146 | 186 $this->context->process($_) foreach $this->preprocess; |
187 | |
77 | 188 $this->template($this->context->template($fileName)); |
49 | 189 } |
190 | |
97 | 191 sub AddVar { |
192 my ($this,$name,$value) = @_; | |
193 | |
194 $this->context->stash->set($name,$value); | |
195 } | |
196 | |
77 | 197 sub title { |
198 $_[0]->template->title; | |
49 | 199 } |
200 | |
201 sub Render { | |
202 my ($this) = @_; | |
203 | |
77 | 204 return $this->template->process($this->context); |
205 } | |
206 | |
207 # Формирует представление для произвольных объектов | |
208 sub _process { | |
209 my ($this,@items) = @_; | |
210 | |
211 my @result; | |
212 | |
213 foreach my $item (@items) { | |
214 if (blessed($item) and $item->isa('IMPL::Web::TT::Control')) { | |
215 push @result, $item->Render(); | |
216 } elsif(blessed($item)) { | |
217 if ($this->presenter) { | |
218 push @result, $this->presenter->print($item); | |
219 } else { | |
220 push @result, $this->toString; | |
221 } | |
222 } else { | |
223 push @result, $item; | |
224 } | |
225 } | |
226 | |
107 | 227 return join '',@result; |
49 | 228 } |
229 | |
108 | 230 our $AUTOLOAD; |
231 sub AUTOLOAD { | |
232 my $this = shift; | |
233 my ($method) = ($AUTOLOAD =~ /(\w+)$/); | |
234 | |
235 if($method =~ /^create(\w+)/) { | |
236 my ($name,$args) = @_; | |
237 return $this->CreateControl($name,$1,$args); | |
238 } | |
239 | |
240 my @result = $this->selectNodes($method); | |
241 | |
242 return $result[0] if @result; | |
109
ddf0f037d460
IMPL::DOM::Node updated to support TT, (added childNodesRef and selectNodesRef for using from TT)
wizard
parents:
108
diff
changeset
|
243 carp "Looks like you have a mistake, document doesn't have a such property or child: $method"; |
108 | 244 return; |
245 } | |
246 | |
247 sub as_list { | |
248 $_[0]->childNodes; | |
249 } | |
250 | |
49 | 251 sub Dispose { |
252 my ($this) = @_; | |
253 | |
77 | 254 $this->template(undef); |
255 $this->_context(undef); | |
256 $this->_provider(undef); | |
49 | 257 |
108 | 258 $this->supercall::Dispose(); |
49 | 259 } |
260 | |
261 1; | |
262 __END__ | |
263 =pod | |
264 | |
77 | 265 =head1 NAME |
266 | |
267 C<IMPL::Web::TT::Document> - Документ, позволяющий строить представление по шаблону | |
268 | |
49 | 269 =head1 SYNOPSIS |
270 | |
75 | 271 =begin code |
272 | |
49 | 273 // create new document |
77 | 274 my $doc = new IMPL::Web::TT::Document; |
49 | 275 |
276 // load template | |
277 $doc->loadFile('Templates/index.tt'); | |
278 | |
279 // render file | |
280 print $doc->Render(); | |
281 | |
75 | 282 =end code |
283 | |
49 | 284 =head1 DESCRIPTION |
285 | |
77 | 286 C<use base qw(IMPL::DOM::Document)> |
287 | |
49 | 288 Документ, основанный на шаблоне Template::Toolkit. Позволяет загрузить шаблон, |
289 и сформировать окончательный документ. Является наследником C<IMPL::DOM::Node>, | |
290 т.о. может быть использован для реализации DOM модели. | |
291 | |
292 Внутри шаблона переменная C<document> ссылается на объект документа. По этой | |
293 причине образуется циклическая ссылка между объектами шаблона и документом, что | |
294 требует вызова метода C<Dispose> для освобождения документа. | |
295 | |
296 =head1 METHODS | |
297 | |
77 | 298 =over |
49 | 299 |
77 | 300 =item C<CTOR()> |
49 | 301 |
77 | 302 Создает новый экземпляр документа, свойство C<nodeName> устанавливается в 'C<document>' |
49 | 303 |
77 | 304 =item C<$doc->LoadFile($fileName,$encoding)> |
49 | 305 |
306 Загружает шаблон из файла C<$fileName>, используя кодировку C<$encoding>. Если | |
307 кодировка не указана, использует utf-8. | |
308 | |
309 =item C<$doc->Render()> | |
310 | |
311 Возвращает данные построенные на основе загруженного шаблона. | |
312 | |
313 =item C<$doc->Dispose()> | |
314 | |
315 Освобождает ресурсы и помечает объект как освобожденный. | |
316 | |
317 =back | |
318 | |
76 | 319 =head1 DOM |
320 | |
108 | 321 Документ представляет собой DOM документ, состоящий из узлов, которые представляют собой данные |
322 для отображения. Для форматированого вывода используется C<template>. | |
323 | |
324 В качестве элементов документа могут присутсвовать специальные объекты C<IMPL::Web::TT::Control>, | |
325 которые внутри содержат шаблон для форматирования собственного содержимого. | |
326 | |
327 | |
328 | |
329 Документ предоставляет ряд фнукций для работы с элементами управления. | |
330 | |
331 =head1 TEMPLATE | |
332 | |
77 | 333 =begin code html |
76 | 334 |
108 | 335 [% CALL document.registerClass( 'Table', 'My::TableClass', template => 'tables/pretty.tt' ) %] |
336 [% CALL document.registerClass( 'Form' )%] | |
337 | |
338 [% table = document.сreateTable('env') %] | |
76 | 339 |
340 [% FOEACH item in document.result %] | |
341 [% table.rows.Add( item.get('name','value') ) %] | |
342 [% END %] | |
343 | |
108 | 344 [% form = document.createForm('login') %] |
77 | 345 [% form.template = 'LOGIN_FORM'%] |
346 | |
347 [% FOREACH item IN document.childNodes %] | |
348 [%render(item)%] | |
76 | 349 [% END %] |
350 | |
77 | 351 [% BLOCK LOGIN_FORM %] |
352 <form method="POST" action='/login.pl'> | |
353 user: [% render(this.item('name')) %] password: [% render(this.item('password')) %] <input type="submit"/> | |
354 </form> | |
355 [% END %] | |
76 | 356 |
77 | 357 =end code html |
76 | 358 |
49 | 359 =cut |