File Coverage

blib/lib/Catalyst/View/HTML/Zoom.pm
Criterion Covered Total %
statement 2 4 50.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 4 6 66.6


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