File Coverage

blib/lib/Catalyst/View/Base/JSON/_ClassInfo.pm
Criterion Covered Total %
statement 24 29 82.7
branch 1 6 16.6
condition 1 3 33.3
subroutine 5 6 83.3
pod 0 2 0.0
total 31 46 67.3


line stmt bran cond sub pod time code
1             package Catalyst::View::Base::JSON::_ClassInfo;
2              
3 1     1   427 use Moo;
  1         2  
  1         6  
4 1     1   1910 use Scalar::Util;
  1         2  
  1         30  
5 1     1   4 use Catalyst::Utils;
  1         1  
  1         562  
6              
7             our $DEFAULT_JSON_CLASS = 'JSON::MaybeXS';
8             our $DEFAULT_CONTENT_TYPE = 'application/json';
9             our %JSON_INIT_ARGS = (
10             utf8 => 1,
11             convert_blessed => 1);
12              
13             has [qw/_original_args _instance_class _fields/] => (is=>'ro', required=>1);
14              
15             has json => (
16             is=>'ro',
17             required=>1,
18             init_arg=>undef,
19             lazy=>1,
20             default=>sub {
21             my $self = shift;
22 1     1   6 eval "use ${\$self->json_class}; 1" ||
  1         1  
  1         50  
23             die "Can't use ${\$self->json_class}, $@";
24              
25             return $self->json_class->new(
26             $self->json_init_args);
27             });
28              
29             has content_type => (
30             is=>'ro',
31             required=>1,
32             default=>$DEFAULT_CONTENT_TYPE);
33              
34             has returns_status => (
35             is=>'ro',
36             predicate=>'has_returns_status');
37              
38             sub HANDLE_ENCODE_ERROR {
39 0     0 0 0 my ($self, $view, $unencodable_ref, $err) = @_;
40 0 0       0 if($self->has_handle_encode_error) {
41 0         0 $self->has_handle_encode_error->($view, $unencodable_ref, $err);
42             } else {
43 0 0       0 return $view->ctx->debug ?
44             $view->response(400, { error => "$err", original=>$unencodable_ref})->detach :
45             $view->response(400, { error => "$err"})->detach;
46             }
47             }
48              
49             has handle_encode_error => (
50             is=>'ro',
51             predicate=>'has_handle_encode_error');
52              
53             has json_class => (
54             is=>'ro',
55             require=>1,
56             default=>sub {
57             return $DEFAULT_JSON_CLASS;
58             });
59              
60             has json_init_args => (
61             is=>'ro',
62             required=>1,
63             lazy=>1,
64             default=>sub {
65             my $self = shift;
66             my %init = (%JSON_INIT_ARGS, $self->has_json_extra_init_args ?
67             %{$self->json_extra_init_args} : ());
68             return \%init;
69             });
70              
71             has json_extra_init_args => (
72             is=>'ro',
73             predicate=>'has_json_extra_init_args');
74              
75             has callback_param => ( is=>'ro', predicate=>'has_callback_param');
76              
77             my $get_stash_key = sub {
78             my $self = shift;
79             my $key = Scalar::Util::blessed($self) ?
80             Scalar::Util::refaddr($self) :
81             $self;
82             return "__Pure_${key}";
83             };
84              
85             my $prepare_args = sub {
86             my ($self, @args) = @_;
87             my %args = ();
88             if(scalar(@args) % 2) { # odd args means first one is an object.
89             my $proto = shift @args;
90             foreach my $field (@{$self->_fields||[]}) {
91             if(my $cb = $proto->can($field)) { # match object methods to available fields
92             $args{$field} = $proto->$field;
93             }
94             }
95             }
96             %args = (%args, @args);
97             return Catalyst::Utils::merge_hashes($self->_original_args, \%args);
98             };
99              
100             sub ACCEPT_CONTEXT {
101 1     1 0 79470 my ($self, $c, @args) = @_;
102 1 50       4 die "View ${\$self->_instance_class->catalyst_component_name} can only be called with a context"
  0         0  
103             unless Scalar::Util::blessed($c);
104              
105 1         3 my $stash_key = $self->$get_stash_key;
106 1   33     2 $c->stash->{$stash_key} ||= do {
107 1         53 my $args = $self->$prepare_args(@args);
108             my $new = $self->_instance_class->new(
109 1         2 %{$args},
110 1         17 %{$c->stash},
  1         3  
111             );
112 1         1263 $new->{__class_info} = $self;
113 1         3 $new->{__ctx} = $c;
114 1         4 $new;
115             };
116 1         3 return $c->stash->{$stash_key};
117             }
118              
119             1;
120              
121             =head1 NAME
122              
123             Catalyst::View::Base::JSON::_ClassInfo - Application Level Info for your View
124              
125             =head1 SYNOPSIS
126              
127             NA - Internal use only.
128              
129             =head1 DESCRIPTION
130              
131             This is used by the main class L<Catalyst::View::JSON::PerRequest> to hold
132             application level information, mostly configuration and a few computations you
133             would rather do once.
134              
135             No real public reusably bits here, just for your inspection.
136              
137             =head1 ATTRIBUTES
138              
139             This View defines the following attributes that can be set during configuration
140              
141             =head2 content_type
142              
143             Sets the response content type. Defaults to 'application/json'.
144              
145             =head2 returns_status
146              
147             An optional arrayref of L<HTTP::Status> codes that the view is allowed to generate.
148             Setting this will injection helper methods into your view:
149              
150             $view->http_ok;
151             $view->202;
152              
153             Both 'friendly' names and numeric codes are generated (I recommend you stick with one
154             style or the other in a project to avoid confusion. Helper methods return the view
155             object to make common chained calls easier:
156              
157             $view->http_bad_request->detach;
158              
159             =head2 callback_param
160              
161             Optional. If set, we use this to get a method name for JSONP from the query parameters.
162              
163             For example if 'callback_param' is 'callback' and the request is:
164              
165             localhost/foo/bar?callback=mymethod
166              
167             Then the JSON response will be wrapped in a function call similar to:
168              
169             mymethod({
170             'foo': 'bar',
171             'baz': 'bin});
172              
173             Which is a common technique for overcoming some cross-domain restrictions of
174             XMLHttpRequest.
175              
176             There are some restrictions to the value of the callback method, for security.
177             For more see: L<http://ajaxian.com/archives/jsonp-json-with-padding>
178              
179             =head2 json_class
180              
181             The class used to perform JSON encoding. Default is L<JSON::MaybeXS>
182              
183             =head2 json_init_args
184              
185             Arguments used to initialize the L</json_class>. Defaults to:
186              
187             our %JSON_INIT_ARGS = (
188             utf8 => 1,
189             convert_blessed => 1);
190              
191             =head2 json_extra_init_args
192              
193             Allows you to 'tack on' some arguments to the JSON initialization without
194             messing with the defaults. Unless you really need to override the defaults
195             this is the method you should use.
196              
197             =head2 handle_encode_error
198              
199             A reference to a subroutine that is called when there is a failure to encode
200             the data given into a JSON format. This can be used globally as an attribute
201             on the defined configuration for the view, and you can set it or overide the
202             global settings on a context basis.
203              
204             Setting this optional attribute will capture and handle error conditions. We
205             will NOT bubble the error up to the global L<Catalyst> error handling (we don't
206             set $c->error for example). If you want that you need to set it yourself in
207             a custom handler, or don't define one.
208              
209             The subroutine receives three arguments: the view object, the original reference
210             that failed to encode and the exception. You must setup a new, valid response.
211             For example:
212              
213             package MyApp::View::JSON;
214              
215             use Moo;
216             extends 'Catalyst::View::Base::JSON';
217              
218             package MyApp;
219              
220             use Catalyst;
221              
222             MyApp->config(
223             default_view =>'JSON',
224             'View::JSON' => {
225             handle_encode_error => sub {
226             my ($view, $original_bad_ref, $err) = @_;
227             $view->response(400, { error => "$err"})->detach;
228             },
229             },
230             );
231              
232             MyApp->setup;
233              
234              
235             B<NOTE> If you mess up the return value (you return something that can't be
236             encoded) a second exception will occur which will NOT be handled and will then
237             bubble up to the main application.
238              
239             B<NOTE> We define a rational default for this to get you started:
240              
241             sub HANDLE_ENCODE_ERROR {
242             my ($view, $orginal_bad_ref, $err) = @_;
243             $view->response(400, { error => "$err"})->detach;
244             }
245              
246             =head1 SEE ALSO
247              
248             L<Catalyst>, L<Catalyst::View>, L<Catalyst::View::JSON>,
249             L<JSON::MaybeXS>
250              
251             =head1 AUTHOR
252            
253             See L<Catalyst::View::Base::JSON>
254              
255             =head1 COPYRIGHT & LICENSE
256            
257             See L<Catalyst::View::Base::JSON>
258              
259             =cut