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