File Coverage

blib/lib/PlackX/Framework/Response.pm
Criterion Covered Total %
statement 108 162 66.6
branch 21 28 75.0
condition 11 14 78.5
subroutine 18 27 66.6
pod 19 23 82.6
total 177 254 69.6


line stmt bran cond sub pod time code
1 8     8   298658 use v5.36;
  8         33  
2             package PlackX::Framework::Response {
3 8     8   57 use parent 'Plack::Response';
  8         18  
  8         51  
4 8     8   32070 use PXF::Util ();
  8         37  
  8         241  
5              
6 8     8   43 use Plack::Util::Accessor qw(stash cleanup_callbacks template stream stream_writer);
  8         14  
  8         49  
7 0     0 1 0 sub GlobalResponse ($class) { ($class->app_namespace.'::Handler')->global_response }
  0         0  
  0         0  
  0         0  
8 1     1 1 5 sub next { return; }
9 1 50   1 1 1651 sub stop { $_[0] || 1 }
10 3     3 0 6 sub add_cleanup_callback($self, $sub) { push @{$self->{cleanup_callbacks}}, $sub }
  3         5  
  3         5  
  3         5  
  3         6  
  3         9  
11 2     2 0 4 sub flash_cookie_name ($self) { PlackX::Framework::flash_cookie_name($self->app_namespace) }
  2         3  
  2         4  
  2         7  
12 0     0 1 0 sub render_json ($self, $dat) { $self->render_content('application/json', PXF::Util::encode_json($dat)) }
  0         0  
  0         0  
  0         0  
  0         0  
13 0     0 1 0 sub render_text ($self, $str) { $self->render_content('text/plain' , $str ) }
  0         0  
  0         0  
  0         0  
  0         0  
14 0     0 1 0 sub render_html ($self, $str) { $self->render_content('text/html' , $str ) }
  0         0  
  0         0  
  0         0  
  0         0  
15 2     2 1 6 sub render_stream ($self, $sub) { $self->stream($sub); $self }
  2         4  
  2         4  
  2         4  
  2         11  
  2         17  
16 0     0 1 0 sub render_template ($self, @ops) { $self->{template}->render(@ops); $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
17 27 50   27 1 1571 sub finalize ($self) { $self->stream ? $self->finalize_sb : $self->SUPER::finalize }
  27         47  
  27         42  
  27         87  
18              
19 47     47 1 94438 sub new ($class, @args) {
  47         89  
  47         87  
  47         75  
20 47         294 my $self = $class->SUPER::new(@args);
21 47   50     952 $self->{cleanup_callbacks} //= [];
22 47   50     245 $self->{body} //= [];
23 47         189 return bless $self, $class;
24             }
25              
26 41     41 1 70 sub set_defaults ($self) {
  41         92  
  41         69  
27 41         149 $self->charset('utf8');
28 41         122 $self->content_type('text/html');
29 41         1560 $self->status(200);
30 41         376 return $self;
31             }
32              
33 45     45 1 1613 sub charset ($self, $newval=undef) {
  45         101  
  45         82  
  45         71  
34 45 100       162 if (!defined $newval) {
35 2         5 my (@conttype) = $self->SUPER::content_type;
36 2         63 foreach my $el (@conttype) {
37 4 100       21 $newval = $1 if $el =~ m/^charset=(.*)$/;
38             }
39             }
40 45 50       121 if (defined $newval) {
41 45         109 $self->{charset} = $newval;
42 45         150 $self->content_type;
43             }
44 45         919 return $self->{charset};
45             }
46              
47 106     106 1 913 sub content_type ($self, $newval=undef) {
  106         189  
  106         188  
  106         174  
48             return $self->SUPER::content_type(defined $newval ? $newval : ())
49 106 100 100     735 if !$self->{charset} or ($newval and $newval =~ m/charset=/i);
    100 100        
50              
51             # The way content_type is handled by HTTP::Headers(::Fast) is a bit weird.
52             # The getter returns an array with the elements split up.
53             # But the setter won't take an array, only a string.
54 103 100       414 my (@ct) = defined $newval ? ($newval,) : ($self->SUPER::content_type,);
55 103         3085 @ct = grep { $_ !~ m/^charset=/i } @ct;
  122         568  
56 103         302 push @ct, 'charset='.$self->{charset};
57              
58 103         427 return $self->SUPER::content_type(join '; ', @ct);
59             }
60              
61 1     1 0 2 sub finalize_sb ($self) {
  1         3  
  1         2  
62             # Finalize Streaming Body
63 1         5 my $original_body = $self->body;
64 1         10 $self->body(undef);
65 1         9 my $aref = $self->SUPER::finalize;
66 1         119 $self->body($original_body);
67 1         7 pop @$aref;
68 1         5 return $aref;
69             }
70              
71 82     82 1 29897 sub print ($self, @lines) {
  82         163  
  82         224  
  82         149  
72 82 100       328 if ($self->stream_writer) {
73 1         40 unshift @lines, @{$self->{body}} and $self->{body} = undef
74 7 100 50     106 if $self->body;
75 7         68 $self->stream_writer->write($_) for @lines; # write() does not take a list!
76 7         385 return $self;
77             }
78 75         530 push @{$self->{body}}, @lines;
  75         278  
79 75         263 return $self;
80             }
81              
82 0     0 1 0 sub no_cache ($self, $bool) {
  0         0  
  0         0  
  0         0  
83 0 0       0 my $val = $bool ? 'no-cache' : undef;
84 0         0 $self->header('Pragma' => $val, 'Cache-control' => $val);
85             }
86              
87 4     4 1 1124 sub flash ($self, $value = undef) {
  4         10  
  4         10  
  4         7  
88             # Note: String values are automatically url-encoded by Cookie::Baker
89             # If value is false we delete the cookie, so set max-age negative
90             # Otherwise set it to 20 minutes (it will be deleted on next request)
91 4 100       15 my $max_age = $value ? 60*20 : -1;
92 4         48 my $cname = $self->flash_cookie_name;
93 4   100     24 $value //= '';
94              
95 4 100       20 $value = "$cname-ju64-" . PXF::Util::encode_ju64($value) if ref $value;
96 4         35 $self->cookies->{$cname} = { value=>$value, path=>'/', 'max-age'=>$max_age, samesite=>'strict' };
97 4         45 return $self;
98             }
99              
100 0     0 1 0 sub flash_redirect ($self, $flashval, $url) {
  0         0  
  0         0  
  0         0  
  0         0  
101 0         0 return $self->flash($flashval)->redirect($url, 303);
102             }
103              
104 2     2 1 20 sub redirect ($self, $url, $code=303) {
  2         5  
  2         5  
  2         7  
  2         4  
105 2         20 $self->SUPER::redirect($url, $code);
106 2         276 return $self;
107             }
108              
109 0     0 0   sub render_content ($self, $content_type, $content) {
  0            
  0            
  0            
  0            
110 0           $self->status(200);
111 0           $self->content_type($content_type);
112 0           $self->print($content);
113 0           return $self;
114             }
115              
116 0     0 1   sub render ($self, $type, @params) {
  0            
  0            
  0            
  0            
117 0 0         if (my $sub = $self->can("render_$type")) {
118 0           return $sub->($self, @params);
119             }
120 0           die "$self does not know how to render_$type";
121             }
122             }
123              
124             1;
125              
126             =pod
127              
128             =head1 NAME
129              
130             PlackX::Framework::Response - Subclass of Plack::Response for PlackX::Framework
131              
132              
133             =head1 CLASS METHODS
134              
135             =over 4
136              
137             =item new()
138              
139             Returns a new object. This is done for you by the framework.
140              
141             =item GlobalResponse()
142              
143             If your app's subclass of PlackX::Framework::Handler's
144             use_global_request_response method returns a true value, PlackX::Framework
145             will set up a global response object for you, which can be retrieved via this
146             class method.
147              
148             This feature is turned off by default to avoid action-at-a-distance bugs. It
149             is preferred to use the request object instance passed to each route's
150             subroutine.
151              
152             =back
153              
154              
155             =head1 OBJECT METHODS
156              
157             =over 4
158              
159             =item charset(), charset($newval)
160              
161             Get or set the charset portion of the content-type header.
162              
163             =item content_type(), content_type($newval)
164              
165             Like Plack::Response, this is a shortcut for HTTP::Headers::Fast->content_type;
166             however, if a charset has been set with the charset() method, it will add the
167             charset to the content-type header, if no charset is specified in $newval.
168              
169             =item flash(value)
170              
171             Sets the flash cookie to the value specified, or clears it if the value is
172             false. PXF automatically clears the cookie on the subsequent request, unless
173             you set a different one.
174              
175             =item flash_redirect(value, url)
176              
177             Combines flash(value) and redirect(url) with a 303 (SEE OTHER) response code.
178              
179             =item next()
180              
181             Syntactic sugar for returning a false value. Indicates to PlackX::Framework
182             to execute the next matching filter.
183              
184             return $response->next; # equivalent to return;
185              
186             See also the stop() method below.
187              
188             =item no_cache(BOOL)
189              
190             If passed a true value, adds HTTP Pragma and Cache-Control headers to "no-cache".
191             If passed a false value, sets these headers to empty string.
192              
193             =item print($string), print(@strings)
194              
195             Adds $string or @strings to the response body, or write them to the PSGI
196             output stream if streaming mode has been activated (see the render_stream()
197             and stream() methods below).
198              
199             When streaming is activated, you should append your print strings with newlines
200             to encourage the server (and browser) to flush the buffer.
201              
202             Unfortunately, the PSGI specification does not provide a way to flush the
203             buffer, but if you are using a server that allows this, perhaps you could do:
204              
205             $response->print($string);
206             $response->stream_writer->flush_buffer();
207              
208             Or override print in your subclass:
209              
210             package MyApp::Response {
211             use parent 'PlackX::Framework::Response';
212             sub print ($self, @lines) {
213             $self->SUPER::print(@lines);
214             $self->stream_writer->flush_buffer() if $self->stream_writer;
215             }
216             }
217              
218             =item redirect($url, $http_status)
219              
220             Like Plack::Response->redirect, except the default http status is 303 See Other
221             instead of 302 Found. This matches the more common type of redirect in a web
222             app, which is directing the user to another page after a prevous request was
223             processed (such as a log in form).
224              
225             =item render($key => @values)
226              
227             An alias for $obj->render_$key(@values). For example, instead of calling
228             render_html(...), you could call render(html => ...). Used by PXF's Router
229             module to implement shortcuts to the appropriate $response->render_*() method.
230              
231             =item render_html($string)
232              
233             Sets the content-type to text/html and sets the response body to $string.
234              
235             =item render_json($ref)
236              
237             Sets the content-type to application/json and encodes $ref to JSON, setting
238             the response body to the resulting string.
239              
240             =item render_stream(CODE)
241              
242             Call stream() with CODE and return the response object.
243              
244             Example:
245              
246             route '/stream-example' => sub ($request, $response) {
247             $response->print(...); # html header
248             return $response->render_stream(sub {
249             # Do some slow actions
250             $response->print(...);
251             # Do more slow actions
252             });
253             };
254              
255             =item render_template(@args)
256              
257             Shortcut for $obj->template->render(@ags)
258              
259             =item render_text($string)
260              
261             Sets the content-type to text/plain and sets the response body to $string.
262              
263             =item set_defaults()
264              
265             Set defaults for the response object. This method is called automatically by
266             PlackX::Framework::Handler.
267              
268             =item stash(), stash($hashref)
269              
270             Returns the current stash hashref, optionally setting it to a new one.
271              
272             =item stream(CODE)
273              
274             Get or set a code reference for PSGI streaming. It is recommended you simply
275             return render_stream(CODE) as described above, but you can use this directly
276             like the below example.
277              
278              
279             Example:
280              
281             route '/stream-example' => sub ($request, $response) {
282             $response->stream(sub {
283             $response->print('I am in a stream!');
284             });
285             # Anything you do here will be executed BEFORE the stream code!
286             $response->print('I am before the stream!');
287             return $response;
288             };
289              
290              
291             =item stop()
292              
293             Syntactic sugar for returning the object itself. Indicates to PlackX::Framework
294             that it should render the response. Useful for semantics in filter actions. The
295             default router engine does not support multiple matching routes, so not so
296             useful in route actions. See also the equivalent inverse method, next().
297              
298             # In a filter
299             return $response->stop; # equivalent to return $response;
300              
301             =item template()
302              
303             Returns the PlackX::Framework::Template object, or undef if templating has not
304             been set up.
305              
306             =back
307              
308              
309             =head1 META
310              
311             For author, copyright, and license, see PlackX::Framework.