File Coverage

blib/lib/Mojolicious/Plugin/DefaultHelpers.pm
Criterion Covered Total %
statement 246 249 98.8
branch 91 96 94.7
condition 33 41 80.4
subroutine 65 65 100.0
pod 1 1 100.0
total 436 452 96.4


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::DefaultHelpers;
2 49     49   435 use Mojo::Base 'Mojolicious::Plugin';
  49         140  
  49         361  
3              
4 49     49   665 use Carp qw(croak);
  49         160  
  49         2686  
5 49     49   378 use Mojo::Asset::File;
  49         173  
  49         427  
6 49     49   357 use Mojo::ByteStream;
  49         182  
  49         2382  
7 49     49   450 use Mojo::Collection;
  49         665  
  49         2250  
8 49     49   383 use Mojo::Exception;
  49         215  
  49         2240  
9 49     49   440 use Mojo::IOLoop;
  49         199  
  49         705  
10 49     49   438 use Mojo::Promise;
  49         222  
  49         782  
11 49     49   397 use Mojo::Util qw(dumper hmac_sha1_sum steady_time);
  49         352  
  49         3730  
12 49     49   678 use Time::HiRes qw(gettimeofday tv_interval);
  49         146  
  49         1239  
13 49     49   6434 use Scalar::Util qw(blessed weaken);
  49         180  
  49         233452  
14              
15             sub register {
16 105     105 1 390 my ($self, $app) = @_;
17              
18             # Controller alias helpers
19 105         363 for my $name (qw(app param stash session url_for url_for_asset url_for_file)) {
20 735     796   3842 $app->helper($name => sub { shift->$name(@_) });
  796         3072  
21             }
22              
23             # Stash key shortcuts (should not generate log messages)
24 105         409 for my $name (qw(extends layout title)) {
25 315     67   1502 $app->helper($name => sub { shift->stash($name, @_) });
  67         233  
26             }
27              
28 105     29   842 $app->helper(accepts => sub { $_[0]->app->renderer->accepts(@_) });
  29         101  
29 105     6   4244 $app->helper(b => sub { shift; Mojo::ByteStream->new(@_) });
  6         11  
  6         29  
30 105     13   734 $app->helper(c => sub { shift; Mojo::Collection->new(@_) });
  13         30  
  13         75  
31 105     70   717 $app->helper(config => sub { shift->app->config(@_) });
  70         211  
32              
33 105     74   724 $app->helper(content => sub { _content(0, 0, @_) });
  74         227  
34 105     46   863 $app->helper(content_for => sub { _content(1, 0, @_) });
  46         156  
35 105     5   778 $app->helper(content_with => sub { _content(0, 1, @_) });
  5         12  
36              
37             $app->helper($_ => $self->can("_$_"))
38 105         1665 for qw(csrf_token current_route exception_format flash inactivity_timeout is_fresh),
39             qw(redirect_to respond_to url_with validation);
40              
41 105     293   948 $app->helper(dumper => sub { shift; dumper @_ });
  293         521  
  293         779  
42 105     17   1198 $app->helper(include => sub { shift->render_to_string(@_) });
  17         101  
43              
44 105         661 $app->helper(log => \&_log);
45              
46 105     19   803 $app->helper('proxy.get_p' => sub { _proxy_method_p('GET', @_) });
  19         69  
47 105     1   961 $app->helper('proxy.post_p' => sub { _proxy_method_p('POST', @_) });
  1         6  
48 105         804 $app->helper('proxy.start_p' => \&_proxy_start_p);
49              
50 105         988 $app->helper("reply.$_" => $self->can("_$_")) for qw(asset file static);
51              
52 105     63   880 $app->helper('reply.exception', => sub { shift->helpers->reply->http_exception(@_) });
  63         238  
53 105     63   742 $app->helper('reply.not_found', => sub { shift->helpers->reply->http_not_found() });
  63         269  
54 105         619 $app->helper('reply.http_exception', => \&_http_exception);
55 105         647 $app->helper('reply.http_not_found', => \&_http_not_found);
56 105     57   770 $app->helper('reply.html_exception' => sub { _development('exception', @_) });
  57         244  
57 105     59   748 $app->helper('reply.html_not_found' => sub { _development('not_found', @_) });
  59         305  
58 105         569 $app->helper('reply.json_exception', => \&_json_exception);
59 105         629 $app->helper('reply.json_not_found', => \&_json_not_found);
60 105         704 $app->helper('reply.txt_exception', => \&_txt_exception);
61 105         602 $app->helper('reply.txt_not_found', => \&_txt_not_found);
62              
63 105         607 $app->helper('timing.begin' => \&_timing_begin);
64 105         852 $app->helper('timing.elapsed' => \&_timing_elapsed);
65 105         629 $app->helper('timing.rps' => \&_timing_rps);
66 105         577 $app->helper('timing.server_timing' => \&_timing_server_timing);
67              
68 105     56   3294 $app->helper(ua => sub { shift->app->ua });
  56         185  
69             }
70              
71             sub _asset {
72 4     4   13 my $c = shift;
73 4         12 $c->app->static->serve_asset($c, @_);
74 4         17 $c->rendered;
75             }
76              
77 65 100   65   232 sub _block { ref $_[0] eq 'CODE' ? $_[0]() : $_[0] }
78              
79             sub _content {
80 125     125   342 my ($append, $replace, $c, $name, $content) = @_;
81 125   100     353 $name ||= 'content';
82              
83 125   100     361 my $hash = $c->stash->{'mojo.content'} //= {};
84 125 100       334 if (defined $content) {
85 77 100       176 if ($append) { $hash->{$name} .= _block($content) }
  42         142  
86 77 100       150 if ($replace) { $hash->{$name} = _block($content) }
  2         4  
87 75   100     241 else { $hash->{$name} //= _block($content) }
88             }
89              
90 125   100     569 return Mojo::ByteStream->new($hash->{$name} // '');
91             }
92              
93             sub _convert_to_exception {
94 63     63   177 my $e = shift;
95 63 100 100     944 return (blessed $e && $e->isa('Mojo::Exception')) ? $e : Mojo::Exception->new($e);
96             }
97              
98 18   66 18   58 sub _csrf_token { $_[0]->session->{csrf_token} ||= hmac_sha1_sum($$ . steady_time . rand, $_[0]->app->secrets->[0]) }
99              
100             sub _current_route {
101 17 100   17   65 return '' unless my $route = shift->match->endpoint;
102 16 100       103 return @_ ? $route->name eq shift : $route->name;
103             }
104              
105             sub _development {
106 116     116   396 my ($page, $c, $e) = @_;
107              
108 116 100       519 $c->helpers->log->error(($e = _convert_to_exception($e))->inspect) if $page eq 'exception';
109              
110             # Filtered stash snapshot
111 116         539 my $stash = $c->stash;
112 116         641 %{$stash->{snapshot} = {}}
113 116 100       686 = map { $_ => $_ eq 'app' ? 'DUMMY' : $stash->{$_} } grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash;
  131 100       550  
  488         2254  
114 116 100       578 $stash->{exception} = $page eq 'exception' ? $e : undef;
115              
116             # Render with fallbacks
117 116         470 my $app = $c->app;
118 116         547 my $mode = $app->mode;
119             my $options = {
120 116 100 66     724 format => $stash->{format} || $app->renderer->default_format,
121             handler => undef,
122             status => $page eq 'exception' ? 500 : 404,
123             template => "$page.$mode"
124             };
125 116 100       501 my $bundled = 'mojo/' . ($mode eq 'development' ? 'debug' : $page);
126 116 100       407 return $c if _fallbacks($c, $options, $page, $bundled);
127 4         35 _fallbacks($c, {%$options, format => 'html'}, $page, $bundled);
128 4         65 return $c;
129             }
130              
131             sub _exception_format {
132 130     130   292 my $c = shift;
133 130         424 my $stash = $c->stash;
134 130   66     1006 $stash->{'mojo.exception_format'} ||= $c->app->exception_format;
135 130 100       656 return $stash->{'mojo.exception_format'} unless @_;
136 2         31 $stash->{'mojo.exception_format'} = shift;
137 2         15 return $c;
138             }
139              
140             sub _fallbacks {
141 120     120   458 my ($c, $options, $template, $bundled) = @_;
142              
143             # Mode specific template
144 120 100       717 return 1 if $c->render_maybe(%$options);
145              
146             # Normal template
147 102 100       679 return 1 if $c->render_maybe(%$options, template => $template);
148              
149             # Inline template
150 46         275 my $stash = $c->stash;
151 46 100       320 return undef unless $options->{format} eq 'html';
152 42         198 delete @$stash{qw(extends layout)};
153 42         235 return $c->render_maybe($bundled, %$options, handler => 'ep');
154             }
155              
156 1     1   17 sub _file { _asset(shift, Mojo::Asset::File->new(path => shift)) }
157              
158             sub _flash {
159 25     25   57 my $c = shift;
160              
161             # Check old flash
162 25         72 my $session = $c->session;
163 25 100 100     174 return $session->{flash} ? $session->{flash}{$_[0]} : undef if @_ == 1 && !ref $_[0];
    100          
164              
165             # Initialize new flash and merge values
166 7 100       52 my $values = ref $_[0] ? $_[0] : {@_};
167 7   100     31 @{$session->{new_flash} //= {}}{keys %$values} = values %$values;
  7         57  
168              
169 7         41 return $c;
170             }
171              
172             sub _http_exception {
173 63     63   241 my ($c, $e) = @_;
174 63         304 my $format = $c->exception_format;
175 63 100       334 return $c->helpers->reply->txt_exception($e) if $format eq 'txt';
176 59 100       203 return $c->helpers->reply->json_exception($e) if $format eq 'json';
177 57         218 return $c->helpers->reply->html_exception($e);
178             }
179              
180             sub _http_not_found {
181 63     63   176 my $c = shift;
182 63         359 my $format = $c->exception_format;
183 63 100       255 return $c->helpers->reply->txt_not_found if $format eq 'txt';
184 61 100       213 return $c->helpers->reply->json_not_found if $format eq 'json';
185 59         249 return $c->helpers->reply->html_not_found;
186             }
187              
188             sub _inactivity_timeout {
189 7     7   27 my ($c, $timeout) = @_;
190 7   100     33 my $stream = Mojo::IOLoop->stream($c->tx->connection // '');
191 7 100       55 $stream->timeout($timeout) if $stream;
192 7         45 return $c;
193             }
194              
195             sub _is_fresh {
196 20     20   62 my ($c, %options) = @_;
197 20         65 return $c->app->static->is_fresh($c, \%options);
198             }
199              
200             sub _json_exception {
201 2     2   12 my ($c, $e) = @_;
202 2         10 $c->stash->{exception} = _convert_to_exception($e);
203 2 100       12 return $c->render(json => {error => $e}, status => 500) if $c->app->mode eq 'development';
204 1         7 return $c->render(json => {error => 'Internal Server Error'}, status => 500);
205             }
206              
207 2     2   16 sub _json_not_found { shift->render(json => {error => 'Not Found'}, status => 404) }
208              
209 3589   66 3589   9983 sub _log { $_[0]->stash->{'mojo.log'} ||= $_[0]->app->log->context('[' . $_[0]->req->request_id . ']') }
210              
211             sub _proxy_method_p {
212 20     20   48 my ($method, $c) = (shift, shift);
213 20         71 return _proxy_start_p($c, $c->ua->build_tx($method, @_));
214             }
215              
216             sub _proxy_start_p {
217 22     22   50 my ($c, $source_tx) = @_;
218 22         88 my $tx = $c->render_later->tx;
219              
220 22         140 my $promise = Mojo::Promise->new;
221             $source_tx->res->content->auto_upgrade(0)->auto_decompress(0)->once(
222             body => sub {
223 21     21   42 my $source_content = shift;
224              
225 21         83 my $source_res = $source_tx->res;
226 21         59 my $res = $tx->res;
227 21         57 my $content = $res->content;
228 21         60 $res->code($source_res->code)->message($source_res->message);
229 21         97 my $headers = $source_res->headers->clone->dehop;
230 21         84 $content->headers($headers);
231 21         99 $promise->resolve;
232              
233 21         80 my $source_stream = Mojo::IOLoop->stream($source_tx->connection);
234 21 50       66 return unless my $stream = Mojo::IOLoop->stream($tx->connection);
235              
236 21 100       97 my $write = $source_content->is_chunked ? 'write_chunk' : 'write';
237             $source_content->unsubscribe('read')->on(
238             read => sub {
239 10         34 my $data = pop;
240 10 100       66 $content->$write(length $data ? $data : ()) and $tx->resume;
    50          
241              
242             # Throttle transparently when backpressure rises
243 10 50       53 return if $stream->can_write;
244 0         0 $source_stream->stop;
245 0         0 $stream->once(drain => sub { $source_stream->start });
  0         0  
246             }
247 21         85 );
248              
249 21 50       102 $source_res->once(finish => sub { $content->$write('') and $tx->resume });
  21         101  
250             }
251 22         77 );
252 22         94 weaken $source_tx;
253 22     22   153 $source_tx->once(finish => sub { $promise->reject(_tx_error(@_)) });
  22         109  
254              
255 22     1   82 $c->ua->start_p($source_tx)->catch(sub { });
256              
257 22         142 return $promise;
258             }
259              
260             sub _redirect_to {
261 26     26   60 my $c = shift;
262              
263             # Don't override 3xx status
264 26         80 my $res = $c->res;
265 26         107 $res->headers->location($c->url_for(@_));
266 26 100       154 return $c->rendered($res->is_redirect ? () : 302);
267             }
268              
269             sub _respond_to {
270 83 50   83   386 my ($c, $args) = (shift, ref $_[0] ? $_[0] : {@_});
271              
272             # Find target
273 83         141 my $target;
274 83         195 my $renderer = $c->app->renderer;
275 83         135 my @formats = @{$renderer->accepts($c)};
  83         281  
276 83 100       326 for my $format (@formats ? @formats : ($renderer->default_format)) {
277 89 100       285 next unless $target = $args->{$format};
278 77         278 $c->stash->{format} = $format;
279 77         167 last;
280             }
281              
282             # Fallback
283 83 100       194 unless ($target) {
284 6 100       40 return $c->rendered(204) unless $target = $args->{any};
285 4         15 delete $c->stash->{format};
286             }
287              
288             # Dispatch
289 81 100       486 ref $target eq 'CODE' ? $target->($c) : $c->render(%$target);
290              
291 81         449 return $c;
292             }
293              
294             sub _static {
295 7     7   30 my ($c, $file) = @_;
296 7 100       36 croak qq{Static file "$file" not found} unless $c->app->static->serve($c, $file);
297 6         47 return $c->rendered;
298             }
299              
300 261     261   1567 sub _timing_begin { shift->stash->{'mojo.timing'}{shift()} = [gettimeofday] }
301              
302             sub _timing_elapsed {
303 258     258   668 my ($c, $name) = @_;
304 258 100       657 return undef unless my $started = $c->stash->{'mojo.timing'}{$name};
305 257         1427 return tv_interval($started, [gettimeofday()]);
306             }
307              
308 259 100   259   3257 sub _timing_rps { $_[1] == 0 ? undef : sprintf '%.3f', 1 / $_[1] }
309              
310             sub _timing_server_timing {
311 4     4   11 my ($c, $metric, $desc, $dur) = @_;
312 4         10 my $value = $metric;
313 4 100       12 $value .= qq{;desc="$desc"} if defined $desc;
314 4 100       13 $value .= ";dur=$dur" if defined $dur;
315 4         12 $c->res->headers->append('Server-Timing' => $value);
316             }
317              
318 22   50 22   71 sub _tx_error { (shift->error // {})->{message} // 'Unknown error' }
      50        
319              
320             sub _txt_exception {
321 4     4   18 my ($c, $e) = @_;
322 4         14 $c->stash->{exception} = _convert_to_exception($e);
323 4 100       17 return $c->render(text => $e, format => 'txt', status => 500) if $c->app->mode eq 'development';
324 1         15 return $c->render(text => 'Internal Server Error', format => 'txt', status => 500);
325             }
326              
327 2     2   10 sub _txt_not_found { shift->render(text => 'Not Found', format => 'txt', status => 404) }
328              
329             sub _url_with {
330 5     5   11 my $c = shift;
331 5         19 return $c->url_for(@_)->query($c->req->url->query->clone);
332             }
333              
334             sub _validation {
335 254     254   453 my $c = shift;
336              
337 254         739 my $stash = $c->stash;
338 254 100       1134 return $stash->{'mojo.validation'} if $stash->{'mojo.validation'};
339              
340 108         338 my $req = $c->req;
341 108         467 my $token = $c->session->{csrf_token};
342 108         362 my $header = $req->headers->header('X-CSRF-Token');
343 108         388 my $hash = $req->params->to_hash;
344 108 100 33     474 $hash->{csrf_token} //= $header if $token && $header;
      100        
345 108         237 $hash->{$_} = $req->every_upload($_) for map { $_->name } @{$req->uploads};
  3         10  
  108         335  
346 108         443 my $v = $c->app->validator->validation->input($hash);
347 108         474 return $stash->{'mojo.validation'} = $v->csrf_token($token);
348             }
349              
350             1;
351              
352             =encoding utf8
353              
354             =head1 NAME
355              
356             Mojolicious::Plugin::DefaultHelpers - Default helpers plugin
357              
358             =head1 SYNOPSIS
359              
360             # Mojolicious
361             $app->plugin('DefaultHelpers');
362              
363             # Mojolicious::Lite
364             plugin 'DefaultHelpers';
365              
366             =head1 DESCRIPTION
367              
368             L is a collection of helpers for L.
369              
370             This is a core plugin, that means it is always enabled and its code a good example for learning to build new plugins,
371             you're welcome to fork it.
372              
373             See L for a list of plugins that are available by default.
374              
375             =head1 HELPERS
376              
377             L implements the following helpers.
378              
379             =head2 accepts
380              
381             my $formats = $c->accepts;
382             my $format = $c->accepts('html', 'json', 'txt');
383              
384             Select best possible representation for resource from C C/C parameter, C stash value or
385             C request header with L, defaults to returning the first extension if no
386             preference could be detected.
387              
388             # Check if JSON is acceptable
389             $c->render(json => {hello => 'world'}) if $c->accepts('json');
390              
391             # Check if JSON was specifically requested
392             $c->render(json => {hello => 'world'}) if $c->accepts('', 'json');
393              
394             # Unsupported representation
395             $c->render(data => '', status => 204)
396             unless my $format = $c->accepts('html', 'json');
397              
398             # Detected representations to select from
399             my @formats = @{$c->accepts};
400              
401             =head2 app
402              
403             %= app->secrets->[0]
404              
405             Alias for L.
406              
407             =head2 b
408              
409             %= b('Joel is a slug')->slugify
410              
411             Turn string into a L object.
412              
413             =head2 c
414              
415             %= c('a', 'b', 'c')->shuffle->join
416              
417             Turn list into a L object.
418              
419             =head2 config
420              
421             %= config 'something'
422              
423             Alias for L.
424              
425             =head2 content
426              
427             %= content foo => begin
428             test
429             % end
430             %= content bar => 'Hello World!'
431             %= content 'foo'
432             %= content 'bar'
433             %= content
434              
435             Store partial rendered content in a named buffer and retrieve it later, defaults to retrieving the named buffer
436             C, which is used by the renderer for the C and C features. New content will be ignored if the
437             named buffer is already in use.
438              
439             =head2 content_for
440              
441             % content_for foo => begin
442             test
443             % end
444             %= content_for 'foo'
445              
446             Same as L, but appends content to named buffers if they are already in use.
447              
448             % content_for message => begin
449             Hello
450             % end
451             % content_for message => begin
452             world!
453             % end
454             %= content 'message'
455              
456             =head2 content_with
457              
458             % content_with foo => begin
459             test
460             % end
461             %= content_with 'foo'
462              
463             Same as L, but replaces content of named buffers if they are already in use.
464              
465             % content message => begin
466             world!
467             % end
468             % content_with message => begin
469             Hello <%= content 'message' %>
470             % end
471             %= content 'message'
472              
473             =head2 csrf_token
474              
475             %= csrf_token
476              
477             Get CSRF token from L, and generate one if none exists.
478              
479             =head2 current_route
480              
481             % if (current_route 'login') {
482             Welcome to Mojolicious!
483             % }
484             %= current_route
485              
486             Check or get name of current route.
487              
488             =head2 dumper
489              
490             %= dumper {some => 'data'}
491              
492             Dump a Perl data structure with L, very useful for debugging.
493              
494             =head2 exception_format
495              
496             my $format = $c->exception_format;
497             $c = $c->exception_format('txt');
498              
499             Format for HTTP exceptions (C, C, or C), defaults to the value of L.
500              
501             =head2 extends
502              
503             % extends 'blue';
504             % extends 'blue', title => 'Blue!';
505              
506             Set C stash value, all additional key/value pairs get merged into the L.
507              
508             =head2 flash
509              
510             my $foo = $c->flash('foo');
511             $c = $c->flash({foo => 'bar'});
512             $c = $c->flash(foo => 'bar');
513             %= flash 'foo'
514              
515             Data storage persistent only for the next request, stored in the L.
516              
517             # Show message after redirect
518             $c->flash(message => 'User created successfully!');
519             $c->redirect_to('show_user', id => 23);
520              
521             =head2 inactivity_timeout
522              
523             $c = $c->inactivity_timeout(3600);
524              
525             Use L to find the current connection and increase timeout if possible.
526              
527             # Longer version
528             Mojo::IOLoop->stream($c->tx->connection)->timeout(3600);
529              
530             =head2 include
531              
532             %= include 'menubar'
533             %= include 'menubar', format => 'txt'
534              
535             Alias for L.
536              
537             =head2 is_fresh
538              
539             my $bool = $c->is_fresh;
540             my $bool = $c->is_fresh(etag => 'abc');
541             my $bool = $c->is_fresh(etag => 'W/"def"');
542             my $bool = $c->is_fresh(last_modified => $epoch);
543              
544             Check freshness of request by comparing the C and C request headers to the C
545             and C response headers with L.
546              
547             # Add ETag/Last-Modified headers and check freshness before rendering
548             $c->is_fresh(etag => 'abc', last_modified => 1424985708)
549             ? $c->rendered(304)
550             : $c->render(text => 'I ♥ Mojolicious!');
551              
552             =head2 layout
553              
554             % layout 'green';
555             % layout 'green', title => 'Green!';
556              
557             Set C stash value, all additional key/value pairs get merged into the L.
558              
559             =head2 log
560              
561             my $log = $c->log;
562              
563             Alternative to L that includes L with every log message.
564              
565             # Log message with context
566             $c->log->debug('This is a log message with request id');
567              
568             # Pass logger with context to model
569             my $log = $c->log;
570             $c->some_model->create({foo => $foo}, $log);
571              
572             =head2 param
573              
574             %= param 'foo'
575              
576             Alias for L.
577              
578             =head2 proxy->get_p
579              
580             my $promise = $c->proxy->get_p('http://example.com' => {Accept => '*/*'});
581              
582             Perform non-blocking C request and forward response as efficiently as possible, takes the same arguments as
583             L and returns a L object.
584              
585             # Forward with exception handling
586             $c->proxy->get_p('http://mojolicious.org')->catch(sub ($err) {
587             $c->log->debug("Proxy error: $err");
588             $c->render(text => 'Something went wrong!', status => 400);
589             });
590              
591             =head2 proxy->post_p
592              
593             my $promise = $c->proxy->post_p('http://example.com' => {Accept => '*/*'});
594              
595             Perform non-blocking C request and forward response as efficiently as possible, takes the same arguments as
596             L and returns a L object.
597              
598             # Forward with exception handling
599             $c->proxy->post_p('example.com' => form => {test => 'pass'})->catch(sub ($err) {
600             $c->log->debug("Proxy error: $err");
601             $c->render(text => 'Something went wrong!', status => 400);
602             });
603              
604             =head2 proxy->start_p
605              
606             my $promise = $c->proxy->start_p(Mojo::Transaction::HTTP->new);
607              
608             Perform non-blocking request for a custom L object and forward response as efficiently as
609             possible, returns a L object.
610              
611             # Forward with exception handling
612             my $tx = $c->ua->build_tx(GET => 'http://mojolicious.org');
613             $c->proxy->start_p($tx)->catch(sub ($err) {
614             $c->log->debug("Proxy error: $err");
615             $c->render(text => 'Something went wrong!', status => 400);
616             });
617              
618             # Forward with custom request and response headers
619             my $headers = $c->req->headers->clone->dehop;
620             $headers->header('X-Proxy' => 'Mojo');
621             my $tx = $c->ua->build_tx(GET => 'http://example.com' => $headers->to_hash);
622             $c->proxy->start_p($tx);
623             $tx->res->content->once(body => sub ($content) { $c->res->headers->header('X-Proxy' => 'Mojo') });
624              
625             =head2 redirect_to
626              
627             $c = $c->redirect_to('named', foo => 'bar');
628             $c = $c->redirect_to('named', {foo => 'bar'});
629             $c = $c->redirect_to('/index.html');
630             $c = $c->redirect_to('http://example.com/index.html');
631              
632             Prepare a C<302> (if the status code is not already C<3xx>) redirect response with C header, takes the same
633             arguments as L.
634              
635             # Moved Permanently
636             $c->res->code(301);
637             $c->redirect_to('some_route');
638              
639             # Temporary Redirect
640             $c->res->code(307);
641             $c->redirect_to('some_route');
642              
643             =head2 reply->asset
644              
645             $c->reply->asset(Mojo::Asset::File->new);
646              
647             Reply with a L or L object using L, and
648             perform content negotiation with C, C and C headers.
649              
650             # Serve asset with custom modification time
651             my $asset = Mojo::Asset::Memory->new;
652             $asset->add_chunk('Hello World!')->mtime(784111777);
653             $c->res->headers->content_type('text/plain');
654             $c->reply->asset($asset);
655              
656             # Serve static file if it exists
657             if (my $asset = $c->app->static->file('images/logo.png')) {
658             $c->res->headers->content_type('image/png');
659             $c->reply->asset($asset);
660             }
661              
662             =head2 reply->exception
663              
664             $c = $c->reply->exception('Oops!');
665             $c = $c->reply->exception(Mojo::Exception->new);
666              
667             Render an exception response in the appropriate format by delegating to more specific exception helpers.
668              
669             =head2 reply->file
670              
671             $c->reply->file('/etc/passwd');
672              
673             Reply with a static file from an absolute path anywhere on the file system using L.
674              
675             # Longer version
676             $c->reply->asset(Mojo::Asset::File->new(path => '/etc/passwd'));
677              
678             # Serve file from an absolute path with a custom content type
679             $c->res->headers->content_type('application/myapp');
680             $c->reply->file('/home/sri/foo.txt');
681              
682             # Serve file from a secret application directory
683             $c->reply->file($c->app->home->child('secret', 'file.txt'));
684              
685             =head2 reply->html_exception
686              
687             $c = $c->reply->html_exception('Oops!');
688             $c = $c->reply->html_exception(Mojo::Exception->new);
689              
690             Render the exception template C or C and set the response status code
691             to C<500>. Also sets the stash values C to a L object and C to a copy of the
692             L for use in the templates.
693              
694             =head2 reply->html_not_found
695              
696             $c = $c->reply->html_not_found;
697              
698             Render the not found template C or C and set the response status code
699             to C<404>. Also sets the stash value C to a copy of the L for use in the templates.
700              
701             =head2 reply->json_exception
702              
703             $c = $c->reply->json_exception('Oops!');
704             $c = $c->reply->json_exception(Mojo::Exception->new);
705              
706             Render a JSON response and set the response status to C<500>.
707              
708             =head2 reply->json_not_found
709              
710             $c = $c->reply->json_not_found;
711              
712             Render a JSON response and set the response status to C<404>.
713              
714             =head2 reply->not_found
715              
716             $c = $c->reply->not_found;
717              
718             Render a not found response in the appropriate format by delegating to more specific exception helpers.
719              
720             =head2 reply->static
721              
722             $c->reply->static('images/logo.png');
723             $c->reply->static('../lib/MyApp.pm');
724              
725             Reply with a static file using L, usually from the C directories or C sections of
726             your application. Note that this helper uses a relative path, but does not protect from traversing to parent
727             directories.
728              
729             # Serve file from a relative path with a custom content type
730             $c->res->headers->content_type('application/myapp');
731             $c->reply->static('foo.txt');
732              
733             =head2 reply->txt_exception
734              
735             $c = $c->reply->txt_exception('Oops!');
736             $c = $c->reply->txt_exception(Mojo::Exception->new);
737              
738             Render a plain text response and set the response status to C<500>.
739              
740             =head2 reply->txt_not_found
741              
742             $c = $c->reply->txt_not_found;
743              
744             Render a plain text response and set the response status to C<404>.
745              
746             =head2 respond_to
747              
748             $c = $c->respond_to(
749             json => {json => {message => 'Welcome!'}},
750             html => {template => 'welcome'},
751             any => sub {...}
752             );
753              
754             Automatically select best possible representation for resource from C C/C parameter, C stash
755             value or C request header, defaults to L or rendering an empty C<204>
756             response. Each representation can be handled with a callback or a hash reference containing arguments to be passed to
757             L.
758              
759             # Everything else than "json" and "xml" gets a 204 response
760             $c->respond_to(
761             json => sub { $c->render(json => {just => 'works'}) },
762             xml => {text => 'works'},
763             any => {data => '', status => 204}
764             );
765              
766             For more advanced negotiation logic you can also use L.
767              
768             =head2 session
769              
770             %= session 'foo'
771              
772             Alias for L.
773              
774             =head2 stash
775              
776             %= stash 'foo'
777             % stash foo => 'bar';
778              
779             Alias for L.
780              
781             %= stash('name') // 'Somebody'
782              
783             =head2 timing->begin
784              
785             $c->timing->begin('foo');
786              
787             Create named timestamp for L<"timing-Eelapsed">.
788              
789             =head2 timing->elapsed
790              
791             my $elapsed = $c->timing->elapsed('foo');
792              
793             Return fractional amount of time in seconds since named timstamp has been created with Lbegin"> or
794             C if no such timestamp exists.
795              
796             # Log timing information
797             $c->timing->begin('database_stuff');
798             ...
799             my $elapsed = $c->timing->elapsed('database_stuff');
800             $c->app->log->debug("Database stuff took $elapsed seconds");
801              
802             =head2 timing->rps
803              
804             my $rps = $c->timing->rps('0.001');
805              
806             Return fractional number of requests that could be performed in one second if every singe one took the given amount of
807             time in seconds or C if the number is too low.
808              
809             # Log more timing information
810             $c->timing->begin('web_stuff');
811             ...
812             my $elapsed = $c->timing->elapsed('web_stuff');
813             my $rps = $c->timing->rps($elapsed);
814             $c->app->log->debug("Web stuff took $elapsed seconds ($rps per second)");
815              
816             =head2 timing->server_timing
817              
818             $c->timing->server_timing('metric');
819             $c->timing->server_timing('metric', 'Some Description');
820             $c->timing->server_timing('metric', 'Some Description', '0.001');
821              
822             Create C header with optional description and duration.
823              
824             # "Server-Timing: miss"
825             $c->timing->server_timing('miss');
826              
827             # "Server-Timing: dc;desc=atl"
828             $c->timing->server_timing('dc', 'atl');
829              
830             # "Server-Timing: db;desc=Database;dur=0.0001"
831             $c->timing->begin('database_stuff');
832             ...
833             my $elapsed = $c->timing->elapsed('database_stuff');
834             $c->timing->server_timing('db', 'Database', $elapsed);
835              
836             # "Server-Timing: miss, dc;desc=atl"
837             $c->timing->server_timing('miss');
838             $c->timing->server_timing('dc', 'atl');
839              
840             =head2 title
841              
842             %= title
843             % title 'Welcome!';
844             % title 'Welcome!', foo => 'bar';
845              
846             Get or set C stash value, all additional key/value pairs get merged into the L</"stash">. </td> </tr> <tr> <td class="h" > <a name="847">847</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="848">848</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 ua </td> </tr> <tr> <td class="h" > <a name="849">849</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="850">850</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %= ua->get('mojolicious.org')->result->dom->at('title')->text </td> </tr> <tr> <td class="h" > <a name="851">851</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="852">852</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Alias for L<Mojolicious/"ua">. </td> </tr> <tr> <td class="h" > <a name="853">853</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="854">854</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 url_for </td> </tr> <tr> <td class="h" > <a name="855">855</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="856">856</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %= url_for 'named', foo => 'bar', baz => 'yada' </td> </tr> <tr> <td class="h" > <a name="857">857</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="858">858</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Alias for L<Mojolicious::Controller/"url_for">. </td> </tr> <tr> <td class="h" > <a name="859">859</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="860">860</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %= url_for('/index.html')->query(foo => 'bar') </td> </tr> <tr> <td class="h" > <a name="861">861</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="862">862</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 url_for_asset </td> </tr> <tr> <td class="h" > <a name="863">863</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="864">864</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %= url_for_asset('/app.js'); </td> </tr> <tr> <td class="h" > <a name="865">865</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="866">866</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Alias for L<Mojolicious::Controller/"url_for_asset">. </td> </tr> <tr> <td class="h" > <a name="867">867</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="868">868</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 url_for_file </td> </tr> <tr> <td class="h" > <a name="869">869</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="870">870</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %= url_for_file('/index.html'); </td> </tr> <tr> <td class="h" > <a name="871">871</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="872">872</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Alias for L<Mojolicious::Controller/"url_for_file">. </td> </tr> <tr> <td class="h" > <a name="873">873</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="874">874</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 url_with </td> </tr> <tr> <td class="h" > <a name="875">875</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="876">876</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %= url_with 'named', foo => 'bar', baz => 'yada' </td> </tr> <tr> <td class="h" > <a name="877">877</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="878">878</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Does the same as L</"url_for">, but inherits query parameters from the current request. </td> </tr> <tr> <td class="h" > <a name="879">879</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="880">880</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> %= url_with->query({page => 2}) </td> </tr> <tr> <td class="h" > <a name="881">881</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="882">882</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 validation </td> </tr> <tr> <td class="h" > <a name="883">883</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="884">884</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $v = $c->validation; </td> </tr> <tr> <td class="h" > <a name="885">885</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="886">886</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Get L<Mojolicious::Validator::Validation> object for current request to validate file uploads as well as C<GET> and </td> </tr> <tr> <td class="h" > <a name="887">887</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> C<POST> parameters extracted from the query string and C<application/x-www-form-urlencoded> or C<multipart/form-data> </td> </tr> <tr> <td class="h" > <a name="888">888</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> message body. Parts of the request body need to be loaded into memory to parse C<POST> parameters, so you have to make </td> </tr> <tr> <td class="h" > <a name="889">889</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> sure it is not excessively large. There's a 16MiB limit for requests by default. </td> </tr> <tr> <td class="h" > <a name="890">890</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="891">891</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> # Validate GET/POST parameter </td> </tr> <tr> <td class="h" > <a name="892">892</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $v = $c->validation; </td> </tr> <tr> <td class="h" > <a name="893">893</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $v->required('title', 'trim')->size(3, 50); </td> </tr> <tr> <td class="h" > <a name="894">894</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $title = $v->param('title'); </td> </tr> <tr> <td class="h" > <a name="895">895</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="896">896</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> # Validate file upload </td> </tr> <tr> <td class="h" > <a name="897">897</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $v = $c->validation; </td> </tr> <tr> <td class="h" > <a name="898">898</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $v->required('tarball')->upload->size(1, 1048576); </td> </tr> <tr> <td class="h" > <a name="899">899</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> my $tarball = $v->param('tarball'); </td> </tr> <tr> <td class="h" > <a name="900">900</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="901">901</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head1 METHODS </td> </tr> <tr> <td class="h" > <a name="902">902</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="903">903</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> L<Mojolicious::Plugin::DefaultHelpers> inherits all methods from L<Mojolicious::Plugin> and implements the following </td> </tr> <tr> <td class="h" > <a name="904">904</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> new ones. </td> </tr> <tr> <td class="h" > <a name="905">905</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="906">906</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head2 register </td> </tr> <tr> <td class="h" > <a name="907">907</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="908">908</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> $plugin->register(Mojolicious->new); </td> </tr> <tr> <td class="h" > <a name="909">909</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="910">910</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> Register helpers in L<Mojolicious> application. </td> </tr> <tr> <td class="h" > <a name="911">911</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="912">912</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =head1 SEE ALSO </td> </tr> <tr> <td class="h" > <a name="913">913</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="914">914</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>. </td> </tr> <tr> <td class="h" > <a name="915">915</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s">   </td> </tr> <tr> <td class="h" > <a name="916">916</a> </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td >   </td> <td class="s"> =cut </td> </tr> </table> </body> </html>