File Coverage

blib/lib/Catalyst/View/JSON/PerRequest.pm
Criterion Covered Total %
statement 20 20 100.0
branch 1 2 50.0
condition n/a
subroutine 7 7 100.0
pod n/a
total 28 29 96.5


line stmt bran cond sub pod time code
1             package Catalyst::View::JSON::PerRequest;
2              
3 1     1   37813 use Moo;
  1         2  
  1         7  
4 1     1   3124 use CatalystX::InjectComponent;
  1         4344  
  1         30  
5 1     1   563 use Catalyst::View::JSON::_PerRequest;
  1         4  
  1         566  
6              
7             our $VERSION = 0.004;
8             our $DEFAULT_JSON_CLASS = 'JSON::MaybeXS';
9             our $DEFAULT_VIEW_MODEL = 'JSON::ViewData';
10             our %JSON_INIT_ARGS = (
11             utf8 => 1,
12             convert_blessed => 1);
13              
14             extends 'Catalyst::View';
15             with 'Catalyst::Component::InstancePerContext';
16              
17             has json => (
18             is=>'ro',
19             required=>1,
20             init_arg=>undef,
21             lazy=>1,
22             default=>sub {
23             my $self = shift;
24 1     1   115 eval "use ${\$self->json_class}; 1" ||
  1         3  
  1         98  
25             die "Can't use ${\$self->json_class}, $@";
26              
27             return $self->json_class->new(
28             $self->json_init_args);
29             });
30              
31             has default_view_model => (
32             is=>'ro',
33             required=>1,
34             default=>sub {
35             return $DEFAULT_VIEW_MODEL;
36             });
37              
38             has json_class => (
39             is=>'ro',
40             require=>1,
41             default=>sub {
42             return $DEFAULT_JSON_CLASS;
43             });
44              
45             has json_init_args => (
46             is=>'ro',
47             required=>1,
48             lazy=>1,
49             default=>sub {
50             my $self = shift;
51             my %init = (%JSON_INIT_ARGS, $self->has_json_extra_init_args ?
52             %{$self->json_extra_init_args} : ());
53              
54             return \%init;
55             });
56              
57             has json_extra_init_args => (
58             is=>'ro',
59             predicate=>'has_json_extra_init_args');
60              
61             has callback_param => ( is=>'ro', predicate=>'has_callback_param');
62              
63             sub COMPONENT {
64 1     1   300184 my ($class, $app, $args) = @_;
65 1         10 $args = $class->merge_config_hashes($class->config, $args);
66 1         42910 $class->_inject_default_view_model_into($app);
67 1         9074 return $class->new($app, $args);
68             }
69              
70             sub _inject_default_view_model_into {
71 1     1   3 my ($class, $app) = @_;
72 1         13 CatalystX::InjectComponent->inject(
73             into => $app,
74             component => 'Catalyst::Model::JSON::ViewData',
75             as => 'Model::JSON::ViewData' );
76             }
77              
78             sub build_per_context_instance {
79 4     4   332941 my ($self, $c, @args) = @_;
80 4 50       33 return bless +{
81             ctx=>$c,
82             parent=>$self,
83             json=>$self->json,
84             ($self->has_callback_param ? (callback_param=>$self->callback_param) :())
85             }, 'Catalyst::View::JSON::_PerRequest';
86             }
87              
88             1;
89              
90             =head1 NAME
91              
92             Catalyst::View::JSON::PerRequest - JSON View that owns its data
93              
94             =head1 SYNOPSIS
95              
96             MyApp->inject_components(
97             'View::JSON' => { from_component => 'Catalyst::View::JSON::PerRequest' }
98             );
99              
100             # In a controller...
101              
102             sub root :Chained(/) CaptureArgs(0) {
103             my ($self, $c) = @_;
104             $c->view('JSON')->data->set(z=>1);
105             }
106              
107             sub midpoint :Chained(root) CaptureArgs(0) {
108             my ($self, $c) = @_;
109             $c->view('JSON')->data->set(y=>1);
110             }
111              
112             sub endpoint :Chained(midpoint) Args(0) {
113             my ($self, $c) = @_;
114             $c->view('JSON')->created({
115             a => 1,
116             b => 2,
117             c => 3,
118             });
119             }
120              
121             =head1 DESCRIPTION
122              
123             This is a L<Catalyst::View> that produces JSON response from a given model.
124             It differs from some of the more classic JSON producing views (such as
125             L<Catalyst::View::JSON> in that is is a per request view (one view for each
126             request) and it defines a 'data' method to hold information to use to produce
127             a view.
128              
129             It also generates some local response helpers. You may or may not find this
130             approach leads to cleaner code.
131              
132             =head1 METHODS
133              
134             This view defines the following methods
135              
136             =head2 data (?$model)
137              
138             Used to set the view data model, and/or to called methods on it (for example
139             to set attributes that will later be used in the JSON response.).
140              
141             The default is an injected model based on L<Catalyst::Model::JSON::ViewData>
142             which you should review for basic usage. I recommend setting it to a custom
143             model that better encapsulates your view data. You may use any model in your
144             L<Catalyst> application as long as it does the method "TO_JSON".
145              
146             You may only set the view data model once. If you don't set it and just call
147             methods on it, the default view model is automatically used.
148              
149             =head2 res
150              
151             =head2 response
152              
153             $view->response($status, @headers, \%data||$object);
154             $view->response($status, \%data||$object);
155             $view->response(\%data||$object);
156             $view->response($status);
157             $view->response($status, @headers);
158              
159             Used to setup a response. Calling this method will setup an http status, finalize
160             headers and set a body response for the JSON. Content type will be set to
161             'application/json' automatically (you don't need to set this in a header).
162              
163             =head2 Method '->response' Helpers
164              
165             We map status codes from L<HTTP::Status> into methods to make sending common
166             request types more simple and more descriptive. The following are the same:
167              
168             $c->view->response(200, @args);
169             $c->view->ok(@args);
170              
171             do { $c->view->response(200, @args); $c->detach };
172             $c->view->detach_ok(@args);
173              
174             See L<HTTP::Status> for a full list of all the status code helpers.
175              
176             =head2 render ($data)
177              
178             Given a Perl data will return the JSON encoded version.
179              
180             my $json = $c->view->render(\%data);
181              
182             Should be a reference or object that does 'TO_JSON'
183              
184             =head2 process
185              
186             used as a target for $c->forward. This is mostly here for compatibility with some
187             existing methodology. For example allows using this view with the Renderview action
188             class (common practice). I'd consider it a depracated approach, personally.
189              
190             =head1 ATTRIBUTES
191              
192             This View defines the following attributes that can be set during configuration
193              
194             =head2 callback_param
195              
196             Optional. If set, we use this to get a method name for JSONP from the query parameters.
197              
198             For example if 'callback_param' is 'callback' and the request is:
199              
200             localhost/foo/bar?callback=mymethod
201              
202             Then the JSON response will be wrapped in a function call similar to:
203              
204             mymethod({
205             'foo': 'bar',
206             'baz': 'bin});
207              
208             Which is a common technique for overcoming some cross-domain restrictions of
209             XMLHttpRequest.
210              
211             There are some restrictions to the value of the callback method, for security.
212             For more see: L<http://ajaxian.com/archives/jsonp-json-with-padding>
213              
214             =head2 default_view_model
215              
216             The L<Catalyst> model that is the default model for your JSON return. The
217             default is set to a local instance of L<Catalyst::Model::JSON::ViewData>
218              
219             =head2 json_class
220              
221             The class used to perform JSON encoding. Default is L<JSON::MaybeXS>
222              
223             =head2 json_init_args
224              
225             Arguments used to initialize the L</json_class>. Defaults to:
226              
227             our %JSON_INIT_ARGS = (
228             utf8 => 1,
229             convert_blessed => 1);
230              
231             =head2 json_extra_init_args
232              
233             Allows you to 'tack on' some arguments to the JSON initialization without
234             messing with the defaults. Unless you really need to override the defaults
235             this is the method you should use.
236              
237             =head1 UTF-8 NOTES
238              
239             Generally a view should not do any encoding since the core L<Catalyst>
240             framework handles all this for you. However, historically the popular
241             Catalyst JSON views and related ecosystem (such as L<Catalyst::Action::REST>)
242             have done UTF8 encoding and as a result for compatibility core Catalyst code
243             will assume a response content type of 'application/json' is already UTF8
244             encoded. So even though this is a new module, we will continue to maintain this
245             historical situation for compatibility reasons. As a result the UTF8 encoding
246             flags will be enabled and expect the contents of $c->res->body to be encoded
247             as expected. If you set your own JSON class for encoding, or set your own
248             initialization arguments, please keep in mind this expectation.
249              
250             =head1 SEE ALSO
251              
252             L<Catalyst>, L<Catalyst::View>, L<Catalyst::View::JSON>,
253             L<CatalystX::InjectComponent>, L<Catalyst::Component::InstancePerContext>,
254             L<JSON::MaybeXS>
255              
256             =head1 AUTHOR
257            
258             John Napiorkowski L<email:jjnapiork@cpan.org>
259            
260             =head1 COPYRIGHT & LICENSE
261            
262             Copyright 2015, John Napiorkowski L<email:jjnapiork@cpan.org>
263            
264             This library is free software; you can redistribute it and/or modify it under
265             the same terms as Perl itself.
266              
267             =cut