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);
|
49
|
11
|
|
12 BEGIN {
|
77
|
13 private property _provider => prop_all;
|
|
14 private property _context => prop_all;
|
|
15 public property template => prop_get | owner_set;
|
|
16 public property presenter => prop_all, { validate => \&_validatePresenter };
|
49
|
17 }
|
|
18
|
|
19 our %CTOR = (
|
75
|
20 'IMPL::DOM::Document' => sub { nodeName => 'document' }
|
49
|
21 );
|
|
22
|
77
|
23 sub provider {
|
49
|
24 my ($this,%args) = @_;
|
|
25
|
77
|
26 if (my $provider = $this->_provider) {
|
49
|
27 return $provider;
|
|
28 } else {
|
77
|
29 return $this->_provider(new Template::Provider(
|
49
|
30 \%args
|
|
31 ));
|
|
32 }
|
|
33 }
|
|
34
|
77
|
35 sub context {
|
49
|
36 my ($this) = @_;
|
|
37
|
77
|
38 if (my $ctx = $this->_context) {
|
49
|
39 return $ctx;
|
|
40 } else {
|
77
|
41 return $this->_context (
|
49
|
42 new Template::Context(
|
|
43 VARIABLES => {
|
77
|
44 document => $this,
|
|
45 this => $this,
|
|
46 render => sub {
|
|
47 $this->_process(@_);
|
|
48 }
|
49
|
49 },
|
|
50 TRIM => 1,
|
|
51 RECURSION => 1,
|
77
|
52 LOAD_TEMPLATES => [$this->provider]
|
49
|
53 )
|
|
54 )
|
|
55 }
|
|
56 }
|
|
57
|
77
|
58 sub _validatePresenter {
|
|
59 my ($this,$value) = @_;
|
|
60
|
|
61 die new IMPL::InvalidArgumentException("A view object is required") unless blessed($value) and $value->isa('Template::View');
|
|
62 }
|
|
63
|
|
64 sub LoadFile {
|
49
|
65 my ($this,$filePath,$encoding) = @_;
|
|
66
|
|
67 die new IMPL::InvalidArgumentException("A filePath parameter is required") unless $filePath;
|
|
68
|
|
69 $encoding ||= 'utf8';
|
|
70
|
77
|
71 $this->_context(undef);
|
|
72 $this->_provider(undef);
|
49
|
73
|
|
74 my ($vol,$dir,$fileName) = File::Spec->splitpath($filePath);
|
|
75
|
|
76 my $inc = File::Spec->catpath($vol,$dir,'');
|
|
77
|
77
|
78 $this->provider(
|
49
|
79 ENCODING => $encoding,
|
|
80 INTERPOLATE => 1,
|
|
81 PRE_CHOMP => 1,
|
|
82 POST_CHOMP => 1,
|
|
83 INCLUDE_PATH => $inc
|
|
84 );
|
|
85
|
77
|
86 $this->template($this->context->template($fileName));
|
49
|
87 }
|
|
88
|
77
|
89 sub title {
|
|
90 $_[0]->template->title;
|
49
|
91 }
|
|
92
|
|
93 sub Render {
|
|
94 my ($this) = @_;
|
|
95
|
77
|
96 return $this->template->process($this->context);
|
|
97 }
|
|
98
|
|
99 # Формирует представление для произвольных объектов
|
|
100 sub _process {
|
|
101 my ($this,@items) = @_;
|
|
102
|
|
103 my @result;
|
|
104
|
|
105 foreach my $item (@items) {
|
|
106 if (blessed($item) and $item->isa('IMPL::Web::TT::Control')) {
|
|
107 push @result, $item->Render();
|
|
108 } elsif(blessed($item)) {
|
|
109 if ($this->presenter) {
|
|
110 push @result, $this->presenter->print($item);
|
|
111 } else {
|
|
112 push @result, $this->toString;
|
|
113 }
|
|
114 } else {
|
|
115 push @result, $item;
|
|
116 }
|
|
117 }
|
|
118
|
|
119 return join '',@items;
|
49
|
120 }
|
|
121
|
|
122 sub Dispose {
|
|
123 my ($this) = @_;
|
|
124
|
77
|
125 $this->template(undef);
|
|
126 $this->_context(undef);
|
|
127 $this->_provider(undef);
|
49
|
128
|
|
129 $this->SUPER::Dispose();
|
|
130 }
|
|
131
|
|
132 1;
|
|
133 __END__
|
|
134 =pod
|
|
135
|
77
|
136 =head1 NAME
|
|
137
|
|
138 C<IMPL::Web::TT::Document> - Документ, позволяющий строить представление по шаблону
|
|
139
|
49
|
140 =head1 SYNOPSIS
|
|
141
|
75
|
142 =begin code
|
|
143
|
49
|
144 // create new document
|
77
|
145 my $doc = new IMPL::Web::TT::Document;
|
49
|
146
|
|
147 // load template
|
|
148 $doc->loadFile('Templates/index.tt');
|
|
149
|
|
150 // render file
|
|
151 print $doc->Render();
|
|
152
|
75
|
153 =end code
|
|
154
|
49
|
155 =head1 DESCRIPTION
|
|
156
|
77
|
157 C<use base qw(IMPL::DOM::Document)>
|
|
158
|
49
|
159 Документ, основанный на шаблоне Template::Toolkit. Позволяет загрузить шаблон,
|
|
160 и сформировать окончательный документ. Является наследником C<IMPL::DOM::Node>,
|
|
161 т.о. может быть использован для реализации DOM модели.
|
|
162
|
|
163 Внутри шаблона переменная C<document> ссылается на объект документа. По этой
|
|
164 причине образуется циклическая ссылка между объектами шаблона и документом, что
|
|
165 требует вызова метода C<Dispose> для освобождения документа.
|
|
166
|
|
167 =head1 METHODS
|
|
168
|
77
|
169 =over
|
49
|
170
|
77
|
171 =item C<CTOR()>
|
49
|
172
|
77
|
173 Создает новый экземпляр документа, свойство C<nodeName> устанавливается в 'C<document>'
|
49
|
174
|
77
|
175 =item C<$doc->LoadFile($fileName,$encoding)>
|
49
|
176
|
|
177 Загружает шаблон из файла C<$fileName>, используя кодировку C<$encoding>. Если
|
|
178 кодировка не указана, использует utf-8.
|
|
179
|
|
180 =item C<$doc->Render()>
|
|
181
|
|
182 Возвращает данные построенные на основе загруженного шаблона.
|
|
183
|
|
184 =item C<$doc->Dispose()>
|
|
185
|
|
186 Освобождает ресурсы и помечает объект как освобожденный.
|
|
187
|
|
188 =back
|
|
189
|
76
|
190 =head1 DOM
|
|
191
|
77
|
192 =begin code html
|
76
|
193
|
77
|
194 [% table = document.Create('env','table') %]
|
76
|
195
|
|
196 [% FOEACH item in document.result %]
|
|
197 [% table.rows.Add( item.get('name','value') ) %]
|
|
198 [% END %]
|
|
199
|
77
|
200 [% form = document.Create('login','form') %]
|
76
|
201
|
|
202
|
77
|
203 [% form.template = 'LOGIN_FORM'%]
|
|
204
|
|
205 [% FOREACH item IN document.childNodes %]
|
|
206 [%render(item)%]
|
76
|
207 [% END %]
|
|
208
|
77
|
209 [% BLOCK LOGIN_FORM %]
|
|
210 <form method="POST" action='/login.pl'>
|
|
211 user: [% render(this.item('name')) %] password: [% render(this.item('password')) %] <input type="submit"/>
|
|
212 </form>
|
|
213 [% END %]
|
76
|
214
|
77
|
215 =end code html
|
76
|
216
|
|
217
|
49
|
218 =cut
|