line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::View::HTML::Zoom; |
2
|
|
|
|
|
|
|
BEGIN { |
3
|
1
|
|
|
1
|
|
1606
|
$Catalyst::View::HTML::Zoom::VERSION = '0.003'; |
4
|
|
|
|
|
|
|
} |
5
|
|
|
|
|
|
|
# ABSTRACT: Catalyst view to HTML::Zoom |
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
1008
|
use Moose; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
use Class::MOP; |
9
|
|
|
|
|
|
|
use HTML::Zoom; |
10
|
|
|
|
|
|
|
use Path::Class (); |
11
|
|
|
|
|
|
|
use namespace::autoclean; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
extends 'Catalyst::View'; |
14
|
|
|
|
|
|
|
with 'Catalyst::Component::ApplicationAttribute'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
has template_extension => ( |
17
|
|
|
|
|
|
|
is => 'ro', |
18
|
|
|
|
|
|
|
isa => 'Str', |
19
|
|
|
|
|
|
|
predicate => 'has_template_extension', |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
has content_type => ( |
23
|
|
|
|
|
|
|
is => 'ro', |
24
|
|
|
|
|
|
|
isa => 'Str', |
25
|
|
|
|
|
|
|
required => 1, |
26
|
|
|
|
|
|
|
default => 'text/html; charset=utf-8', |
27
|
|
|
|
|
|
|
); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
has root => ( |
30
|
|
|
|
|
|
|
is => 'ro', |
31
|
|
|
|
|
|
|
lazy_build => 1, |
32
|
|
|
|
|
|
|
); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub _build_root { |
35
|
|
|
|
|
|
|
shift->_application->config->{root}; |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub process { |
39
|
|
|
|
|
|
|
my ($self, $c) = @_; |
40
|
|
|
|
|
|
|
my $template_path_part = $self->_template_path_part_from_context($c); |
41
|
|
|
|
|
|
|
if(my $out = $self->render($c, $template_path_part)) { |
42
|
|
|
|
|
|
|
$c->response->body($out); |
43
|
|
|
|
|
|
|
$c->response->content_type($self->content_type) |
44
|
|
|
|
|
|
|
unless ($c->response->content_type); |
45
|
|
|
|
|
|
|
return 1; |
46
|
|
|
|
|
|
|
} else { |
47
|
|
|
|
|
|
|
$c->log->error("The template: $template_path_part returned no response"); |
48
|
|
|
|
|
|
|
return 0; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub _template_path_part_from_context { |
53
|
|
|
|
|
|
|
my ($self, $c) = @_; |
54
|
|
|
|
|
|
|
my $template_path_part = $c->stash->{template} || $c->action->private_path; |
55
|
|
|
|
|
|
|
if ($self->has_template_extension) { |
56
|
|
|
|
|
|
|
my $ext = $self->template_extension; |
57
|
|
|
|
|
|
|
$template_path_part = $template_path_part . '.' . $ext |
58
|
|
|
|
|
|
|
unless $template_path_part =~ /\.$ext$/ || ref $template_path_part; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
return $template_path_part; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub render { |
64
|
|
|
|
|
|
|
my ($self, $c, $template_path_part, $args, $code) = @_; |
65
|
|
|
|
|
|
|
my $vars = {$args ? %{ $args } : %{ $c->stash }}; |
66
|
|
|
|
|
|
|
my $zoom = $self->_build_zoom_from($template_path_part); |
67
|
|
|
|
|
|
|
my $renderer = $self->_build_renderer_from($c, $code); |
68
|
|
|
|
|
|
|
return $renderer->($zoom, $vars)->to_html; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub _build_renderer_from { |
72
|
|
|
|
|
|
|
my ($self, $c, $code) = @_; |
73
|
|
|
|
|
|
|
if($code = $code ? $code : $c->stash->{zoom_do}) { |
74
|
|
|
|
|
|
|
$self->_build_renderer_from_coderef($code); |
75
|
|
|
|
|
|
|
} else { |
76
|
|
|
|
|
|
|
$self->_build_renderer_from_zoomer_class($c); |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub _build_renderer_from_coderef { |
81
|
|
|
|
|
|
|
my ($self, $code) = @_; |
82
|
|
|
|
|
|
|
return sub { |
83
|
|
|
|
|
|
|
my ($zoom, $vars) = @_; |
84
|
|
|
|
|
|
|
return $code->($zoom, %$vars); |
85
|
|
|
|
|
|
|
}; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub _build_renderer_from_zoomer_class { |
89
|
|
|
|
|
|
|
my ($self, $c) = @_; |
90
|
|
|
|
|
|
|
my $zoomer_class = $self->_zoomer_class_from_context($c); |
91
|
|
|
|
|
|
|
my $zoomer = $self->_build_zoomer_from($zoomer_class); |
92
|
|
|
|
|
|
|
my $action = $self->_target_action_from_context($c); |
93
|
|
|
|
|
|
|
return sub { |
94
|
|
|
|
|
|
|
my ($zoom, $vars) = @_; |
95
|
|
|
|
|
|
|
local $_ = $zoom; |
96
|
|
|
|
|
|
|
return $zoomer->$action($vars); |
97
|
|
|
|
|
|
|
}; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _build_zoom_from { |
101
|
|
|
|
|
|
|
my ($self, $template_path_part) = @_; |
102
|
|
|
|
|
|
|
if(ref $template_path_part) { |
103
|
|
|
|
|
|
|
return $self->_build_zoom_from_html($$template_path_part); |
104
|
|
|
|
|
|
|
} else { |
105
|
|
|
|
|
|
|
my $template_abs_path = $self->_template_abs_path_from($template_path_part); |
106
|
|
|
|
|
|
|
return $self->_build_zoom_from_file($template_abs_path); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub _build_zoom_from_html { |
111
|
|
|
|
|
|
|
my ($self, $html) = @_; |
112
|
|
|
|
|
|
|
$self->_debug_log("Building HTML::Zoom from direct HTML"); |
113
|
|
|
|
|
|
|
HTML::Zoom->from_html($html); |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub _build_zoom_from_file { |
117
|
|
|
|
|
|
|
my ($self, $file) = @_; |
118
|
|
|
|
|
|
|
$self->_debug_log("Building HTML::Zoom from file $file"); |
119
|
|
|
|
|
|
|
HTML::Zoom->from_file($file); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub _template_abs_path_from { |
123
|
|
|
|
|
|
|
my ($self, $template_path_part) = @_; |
124
|
|
|
|
|
|
|
Path::Class::dir($self->root, $template_path_part); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub _zoomer_class_from_context { |
128
|
|
|
|
|
|
|
my ($self, $c) = @_; |
129
|
|
|
|
|
|
|
my $controller = $c->controller->meta->name; |
130
|
|
|
|
|
|
|
$controller =~ s/^.*::Controller::(.*)$/$1/; |
131
|
|
|
|
|
|
|
my $zoomer_class = do { |
132
|
|
|
|
|
|
|
$c->stash->{zoom_class} || |
133
|
|
|
|
|
|
|
join('::', ($self->meta->name, $controller)); |
134
|
|
|
|
|
|
|
}; |
135
|
|
|
|
|
|
|
$zoomer_class = ref($self) . $zoomer_class |
136
|
|
|
|
|
|
|
if $zoomer_class=~m/^::/; |
137
|
|
|
|
|
|
|
$self->_debug_log("Using View Class: $zoomer_class"); |
138
|
|
|
|
|
|
|
Class::MOP::load_class($zoomer_class); |
139
|
|
|
|
|
|
|
return $zoomer_class; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub _build_zoomer_from { |
143
|
|
|
|
|
|
|
my ($self, $zoomer_class) = @_; |
144
|
|
|
|
|
|
|
my $key = $zoomer_class; |
145
|
|
|
|
|
|
|
$key =~s/^.+::(View)/$1/; |
146
|
|
|
|
|
|
|
my %args = %{$self->_application->config->{$key} || {}}; |
147
|
|
|
|
|
|
|
return $zoomer_class->new(%args); |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub _target_action_from_context { |
151
|
|
|
|
|
|
|
my ($self, $c) = @_; |
152
|
|
|
|
|
|
|
return $c->stash->{zoom_action} |
153
|
|
|
|
|
|
|
|| $c->action->name; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub _debug_log { |
157
|
|
|
|
|
|
|
my ($self, $message) = @_; |
158
|
|
|
|
|
|
|
$self->_application->log->debug($message) |
159
|
|
|
|
|
|
|
if $self->_application->debug; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
__END__ |
167
|
|
|
|
|
|
|
=pod |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=head1 NAME |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
Catalyst::View::HTML::Zoom - Catalyst view to HTML::Zoom |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head1 VERSION |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
version 0.003 |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=head1 SYNOPSIS |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
package MyApp::View::HTML; |
180
|
|
|
|
|
|
|
use Moose; |
181
|
|
|
|
|
|
|
extends 'Catalyst::View::HTML::Zoom'; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
package MyApp::Controller::Wobble; |
184
|
|
|
|
|
|
|
use Moose; BEGIN { extends 'Catalyst::Controller' } |
185
|
|
|
|
|
|
|
sub dance : Local { |
186
|
|
|
|
|
|
|
my ($self, $c) = @_; |
187
|
|
|
|
|
|
|
$c->stash( shaking => 'hips' ); |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
package MyApp::View::HTML::Wobble; |
191
|
|
|
|
|
|
|
use Moose; |
192
|
|
|
|
|
|
|
sub dance { |
193
|
|
|
|
|
|
|
my ($self, $stash) = @_; |
194
|
|
|
|
|
|
|
$_->select('#shake')->replace_content($stash->{shaking}); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
#root/wobble/dance |
198
|
|
|
|
|
|
|
<p>Shake those <span id="shake" />!</p> |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
GET /wobble/dance => "<p>Shake those <span id="shake">hips</span>!</p>"; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=head1 DESCRIPTION |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
This is our first pass attempt at bringing L<HTML::Zoom> to L<Catalyst>. You |
205
|
|
|
|
|
|
|
should be familiar with L<HTML::Zoom> before using this. Additionally, as this |
206
|
|
|
|
|
|
|
is an early attempt to envision how this will work we say: |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
L<"Danger, Will Robinson!"|http://en.wikipedia.org/wiki/Danger,_Will_Robinson> |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
The following is a list of configuration attributes you can set in your global |
213
|
|
|
|
|
|
|
L<Catalyst> configuration or locally as in: |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
package MyApp::View::HTML; |
216
|
|
|
|
|
|
|
use Moose; |
217
|
|
|
|
|
|
|
extends 'Catalyst::View::HTML::Zoom'; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
__PACKAGE__->config({ |
220
|
|
|
|
|
|
|
content_type => 'text/plain', |
221
|
|
|
|
|
|
|
}); |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head2 template_extension |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
Optionally set the filename extension of your zoomable templates. Common |
226
|
|
|
|
|
|
|
values might be C<html> or C<xhtml>. Should be a scalar. |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head2 content_type |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
Sets the default C<content-type> of the response body. Should be a scalar. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head2 root |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
Used at the prefix path for where yout templates are stored. Defaults to |
235
|
|
|
|
|
|
|
C<< $c->config->{root} >>. Should be a scalar. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=head1 METHODS |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
This class contains the following methods available for public use. |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=head2 process |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
args: ($c) |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
Renders the template specified in C<< $c->stash->{template} >> or |
246
|
|
|
|
|
|
|
C<< $c->namespace/$c->action >> (the private name of the matched action). Stash |
247
|
|
|
|
|
|
|
contents are passed to the underlying view object. |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
Output is stored in C<< $c->response->body >> and we set the value of |
250
|
|
|
|
|
|
|
C<< $c->response->content_type >> to C<text/html; charset=utf-8> or whatever you |
251
|
|
|
|
|
|
|
configured as the L</content_type> attribute unless this header has previously |
252
|
|
|
|
|
|
|
been set. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head2 render |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
args: ($c, $template || \$template, ?\%args, ?$coderef) |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
Renders the given template and returns output. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
If C<$template> is a simple scalar, we assume this is a path part that combines |
261
|
|
|
|
|
|
|
with the value of L</root> to discover a file that lives on your local |
262
|
|
|
|
|
|
|
filesystem. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
However, if C<$template> is a ref, we assume this is a scalar ref containing |
265
|
|
|
|
|
|
|
some html you wish to render directly. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
If C<\%args> is not defined we use the value of C<$c->stash>. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
If C<$coderef> is defined and is a subroutine reference, we use is the same way |
270
|
|
|
|
|
|
|
we use L<zoom_do>. |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=head1 STASH KEYS |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
This View uses the following stash keys as hints to the processor. Currently |
275
|
|
|
|
|
|
|
these keys are passed on in the stash to the underlying templates. |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=head2 template |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
This overrides which template file is parsed by L<HTML::Zoom>. If the value |
280
|
|
|
|
|
|
|
is a plain scalar then we assume it is a file off the template L</root>. If it |
281
|
|
|
|
|
|
|
is a scalar ref, we assume it is the actual body of the template we wish to |
282
|
|
|
|
|
|
|
parse. |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
If this value is not set, we infer a template via C<< $c->action->private_path >> |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=head2 zoom_class |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
This is the View class which is responsible for containing actions that converts |
289
|
|
|
|
|
|
|
a L</template> into a rendered body suitable for returning to a user agent. By |
290
|
|
|
|
|
|
|
default we infer from the controller name such that if your controller is called |
291
|
|
|
|
|
|
|
C<MyApp::Web::Controller::Foo> and your base View class is C<MyApp::Web::View::HTML>, |
292
|
|
|
|
|
|
|
the C<zoom_class> is called C<MyApp::Web::View::HTML::Foo>. |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
If you override this default you can either give a full package name, such as |
295
|
|
|
|
|
|
|
C<MyApp::CommonStuff::View::Foo> or a relative package name such as C<::Foo>, in |
296
|
|
|
|
|
|
|
which case we will automatically prefix the base View (like C<MyApp::Web::View::HTML>) |
297
|
|
|
|
|
|
|
to create a full path (C<MyApp::Web::View::HTML::Foo>). |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 zoom action |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
This refers to a method name in your L</zoom_class> which does the actual work of |
302
|
|
|
|
|
|
|
processing a template into something we return as the body of an HTTP response. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
package MyApp::View::HTML::Foo; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
sub fill_name { |
307
|
|
|
|
|
|
|
my ($self, $args) = @_; |
308
|
|
|
|
|
|
|
$_->select("#name")->replace_content($args->{name}); |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=head2 zoom_do |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
This is a subroutine reference which is optionally used to provide L<HTML::Zoom> |
314
|
|
|
|
|
|
|
directives directly in a controller. Useful for simple templates and rapid |
315
|
|
|
|
|
|
|
prototyping. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sub example_zoom_do :Local { |
318
|
|
|
|
|
|
|
my ($self, $c) = @_; |
319
|
|
|
|
|
|
|
$c->stash( |
320
|
|
|
|
|
|
|
name => 'John', |
321
|
|
|
|
|
|
|
zoom_do => sub { |
322
|
|
|
|
|
|
|
my ($zoom, %args) = @_; |
323
|
|
|
|
|
|
|
$zoom->select("#name")->replace_content($args{name}); |
324
|
|
|
|
|
|
|
}, |
325
|
|
|
|
|
|
|
); |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
If this key is not defined, we assume you want to use a class as above. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=head1 WARNING: VOLATILE! |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
This is the first version of a Catalyst view to L<HTML::Zoom> - and we might |
333
|
|
|
|
|
|
|
have got it wrong. Please be aware that this is still in early stages, and the |
334
|
|
|
|
|
|
|
API is not at all stable. You have been warned (but encouraged to break it and |
335
|
|
|
|
|
|
|
submit bug reports and patches :). |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=head1 THANKS |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
Thanks to Thomas Doran for the initial starting point. |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=head1 AUTHOR |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
Oliver Charles <oliver.g.charles@googlemail.com> |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
This software is copyright (c) 2011 by Oliver Charles. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
350
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=cut |
353
|
|
|
|
|
|
|
|