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
|
97
|
89 sub AddVar {
|
|
90 my ($this,$name,$value) = @_;
|
|
91
|
|
92 $this->context->stash->set($name,$value);
|
|
93 }
|
|
94
|
77
|
95 sub title {
|
|
96 $_[0]->template->title;
|
49
|
97 }
|
|
98
|
|
99 sub Render {
|
|
100 my ($this) = @_;
|
|
101
|
77
|
102 return $this->template->process($this->context);
|
|
103 }
|
|
104
|
|
105 # Формирует представление для произвольных объектов
|
|
106 sub _process {
|
|
107 my ($this,@items) = @_;
|
|
108
|
|
109 my @result;
|
|
110
|
|
111 foreach my $item (@items) {
|
|
112 if (blessed($item) and $item->isa('IMPL::Web::TT::Control')) {
|
|
113 push @result, $item->Render();
|
|
114 } elsif(blessed($item)) {
|
|
115 if ($this->presenter) {
|
|
116 push @result, $this->presenter->print($item);
|
|
117 } else {
|
|
118 push @result, $this->toString;
|
|
119 }
|
|
120 } else {
|
|
121 push @result, $item;
|
|
122 }
|
|
123 }
|
|
124
|
|
125 return join '',@items;
|
49
|
126 }
|
|
127
|
|
128 sub Dispose {
|
|
129 my ($this) = @_;
|
|
130
|
77
|
131 $this->template(undef);
|
|
132 $this->_context(undef);
|
|
133 $this->_provider(undef);
|
49
|
134
|
|
135 $this->SUPER::Dispose();
|
|
136 }
|
|
137
|
|
138 1;
|
|
139 __END__
|
|
140 =pod
|
|
141
|
77
|
142 =head1 NAME
|
|
143
|
|
144 C<IMPL::Web::TT::Document> - Документ, позволяющий строить представление по шаблону
|
|
145
|
49
|
146 =head1 SYNOPSIS
|
|
147
|
75
|
148 =begin code
|
|
149
|
49
|
150 // create new document
|
77
|
151 my $doc = new IMPL::Web::TT::Document;
|
49
|
152
|
|
153 // load template
|
|
154 $doc->loadFile('Templates/index.tt');
|
|
155
|
|
156 // render file
|
|
157 print $doc->Render();
|
|
158
|
75
|
159 =end code
|
|
160
|
49
|
161 =head1 DESCRIPTION
|
|
162
|
77
|
163 C<use base qw(IMPL::DOM::Document)>
|
|
164
|
49
|
165 Документ, основанный на шаблоне Template::Toolkit. Позволяет загрузить шаблон,
|
|
166 и сформировать окончательный документ. Является наследником C<IMPL::DOM::Node>,
|
|
167 т.о. может быть использован для реализации DOM модели.
|
|
168
|
|
169 Внутри шаблона переменная C<document> ссылается на объект документа. По этой
|
|
170 причине образуется циклическая ссылка между объектами шаблона и документом, что
|
|
171 требует вызова метода C<Dispose> для освобождения документа.
|
|
172
|
|
173 =head1 METHODS
|
|
174
|
77
|
175 =over
|
49
|
176
|
77
|
177 =item C<CTOR()>
|
49
|
178
|
77
|
179 Создает новый экземпляр документа, свойство C<nodeName> устанавливается в 'C<document>'
|
49
|
180
|
77
|
181 =item C<$doc->LoadFile($fileName,$encoding)>
|
49
|
182
|
|
183 Загружает шаблон из файла C<$fileName>, используя кодировку C<$encoding>. Если
|
|
184 кодировка не указана, использует utf-8.
|
|
185
|
|
186 =item C<$doc->Render()>
|
|
187
|
|
188 Возвращает данные построенные на основе загруженного шаблона.
|
|
189
|
|
190 =item C<$doc->Dispose()>
|
|
191
|
|
192 Освобождает ресурсы и помечает объект как освобожденный.
|
|
193
|
|
194 =back
|
|
195
|
76
|
196 =head1 DOM
|
|
197
|
77
|
198 =begin code html
|
76
|
199
|
77
|
200 [% table = document.Create('env','table') %]
|
76
|
201
|
|
202 [% FOEACH item in document.result %]
|
|
203 [% table.rows.Add( item.get('name','value') ) %]
|
|
204 [% END %]
|
|
205
|
77
|
206 [% form = document.Create('login','form') %]
|
76
|
207
|
|
208
|
77
|
209 [% form.template = 'LOGIN_FORM'%]
|
|
210
|
|
211 [% FOREACH item IN document.childNodes %]
|
|
212 [%render(item)%]
|
76
|
213 [% END %]
|
|
214
|
77
|
215 [% BLOCK LOGIN_FORM %]
|
|
216 <form method="POST" action='/login.pl'>
|
|
217 user: [% render(this.item('name')) %] password: [% render(this.item('password')) %] <input type="submit"/>
|
|
218 </form>
|
|
219 [% END %]
|
76
|
220
|
77
|
221 =end code html
|
76
|
222
|
|
223
|
49
|
224 =cut
|