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