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