line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojolicious::Plugin::DefaultHelpers; |
2
|
48
|
|
|
48
|
|
597
|
use Mojo::Base 'Mojolicious::Plugin'; |
|
48
|
|
|
|
|
163
|
|
|
48
|
|
|
|
|
335
|
|
3
|
|
|
|
|
|
|
|
4
|
48
|
|
|
48
|
|
373
|
use Carp qw(croak); |
|
48
|
|
|
|
|
143
|
|
|
48
|
|
|
|
|
2177
|
|
5
|
48
|
|
|
48
|
|
353
|
use Mojo::Asset::File; |
|
48
|
|
|
|
|
365
|
|
|
48
|
|
|
|
|
402
|
|
6
|
48
|
|
|
48
|
|
573
|
use Mojo::ByteStream; |
|
48
|
|
|
|
|
134
|
|
|
48
|
|
|
|
|
1946
|
|
7
|
48
|
|
|
48
|
|
425
|
use Mojo::Collection; |
|
48
|
|
|
|
|
150
|
|
|
48
|
|
|
|
|
2035
|
|
8
|
48
|
|
|
48
|
|
530
|
use Mojo::Exception; |
|
48
|
|
|
|
|
163
|
|
|
48
|
|
|
|
|
2513
|
|
9
|
48
|
|
|
48
|
|
359
|
use Mojo::IOLoop; |
|
48
|
|
|
|
|
204
|
|
|
48
|
|
|
|
|
599
|
|
10
|
48
|
|
|
48
|
|
334
|
use Mojo::Promise; |
|
48
|
|
|
|
|
139
|
|
|
48
|
|
|
|
|
645
|
|
11
|
48
|
|
|
48
|
|
319
|
use Mojo::Util qw(dumper hmac_sha1_sum steady_time); |
|
48
|
|
|
|
|
164
|
|
|
48
|
|
|
|
|
3170
|
|
12
|
48
|
|
|
48
|
|
336
|
use Time::HiRes qw(gettimeofday tv_interval); |
|
48
|
|
|
|
|
145
|
|
|
48
|
|
|
|
|
726
|
|
13
|
48
|
|
|
48
|
|
6193
|
use Scalar::Util qw(blessed weaken); |
|
48
|
|
|
|
|
352
|
|
|
48
|
|
|
|
|
213085
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub register { |
16
|
104
|
|
|
104
|
1
|
382
|
my ($self, $app) = @_; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# Controller alias helpers |
19
|
104
|
|
|
|
|
332
|
for my $name (qw(app param stash session url_for)) { |
20
|
520
|
|
|
728
|
|
2657
|
$app->helper($name => sub { shift->$name(@_) }); |
|
728
|
|
|
|
|
2556
|
|
21
|
|
|
|
|
|
|
} |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# Stash key shortcuts (should not generate log messages) |
24
|
104
|
|
|
|
|
325
|
for my $name (qw(extends layout title)) { |
25
|
312
|
|
|
67
|
|
1502
|
$app->helper($name => sub { shift->stash($name, @_) }); |
|
67
|
|
|
|
|
209
|
|
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
|
28
|
104
|
|
|
29
|
|
709
|
$app->helper(accepts => sub { $_[0]->app->renderer->accepts(@_) }); |
|
29
|
|
|
|
|
101
|
|
29
|
104
|
|
|
6
|
|
666
|
$app->helper(b => sub { shift; Mojo::ByteStream->new(@_) }); |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
29
|
|
30
|
104
|
|
|
13
|
|
619
|
$app->helper(c => sub { shift; Mojo::Collection->new(@_) }); |
|
13
|
|
|
|
|
33
|
|
|
13
|
|
|
|
|
104
|
|
31
|
104
|
|
|
70
|
|
667
|
$app->helper(config => sub { shift->app->config(@_) }); |
|
70
|
|
|
|
|
210
|
|
32
|
|
|
|
|
|
|
|
33
|
104
|
|
|
74
|
|
654
|
$app->helper(content => sub { _content(0, 0, @_) }); |
|
74
|
|
|
|
|
249
|
|
34
|
104
|
|
|
46
|
|
3737
|
$app->helper(content_for => sub { _content(1, 0, @_) }); |
|
46
|
|
|
|
|
117
|
|
35
|
104
|
|
|
5
|
|
644
|
$app->helper(content_with => sub { _content(0, 1, @_) }); |
|
5
|
|
|
|
|
12
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$app->helper($_ => $self->can("_$_")) |
38
|
104
|
|
|
|
|
1026
|
for qw(csrf_token current_route exception_format flash inactivity_timeout is_fresh), |
39
|
|
|
|
|
|
|
qw(redirect_to respond_to url_with validation); |
40
|
|
|
|
|
|
|
|
41
|
104
|
|
|
257
|
|
757
|
$app->helper(dumper => sub { shift; dumper @_ }); |
|
257
|
|
|
|
|
405
|
|
|
257
|
|
|
|
|
667
|
|
42
|
104
|
|
|
17
|
|
709
|
$app->helper(include => sub { shift->render_to_string(@_) }); |
|
17
|
|
|
|
|
115
|
|
43
|
|
|
|
|
|
|
|
44
|
104
|
|
|
|
|
509
|
$app->helper(log => \&_log); |
45
|
|
|
|
|
|
|
|
46
|
104
|
|
|
19
|
|
1139
|
$app->helper('proxy.get_p' => sub { _proxy_method_p('GET', @_) }); |
|
19
|
|
|
|
|
68
|
|
47
|
104
|
|
|
1
|
|
830
|
$app->helper('proxy.post_p' => sub { _proxy_method_p('POST', @_) }); |
|
1
|
|
|
|
|
6
|
|
48
|
104
|
|
|
|
|
597
|
$app->helper('proxy.start_p' => \&_proxy_start_p); |
49
|
|
|
|
|
|
|
|
50
|
104
|
|
|
|
|
930
|
$app->helper("reply.$_" => $self->can("_$_")) for qw(asset file static); |
51
|
|
|
|
|
|
|
|
52
|
104
|
|
|
63
|
|
705
|
$app->helper('reply.exception', => sub { shift->helpers->reply->http_exception(@_) }); |
|
63
|
|
|
|
|
275
|
|
53
|
104
|
|
|
59
|
|
711
|
$app->helper('reply.not_found', => sub { shift->helpers->reply->http_not_found() }); |
|
59
|
|
|
|
|
288
|
|
54
|
104
|
|
|
|
|
753
|
$app->helper('reply.http_exception', => \&_http_exception); |
55
|
104
|
|
|
|
|
567
|
$app->helper('reply.http_not_found', => \&_http_not_found); |
56
|
104
|
|
|
57
|
|
680
|
$app->helper('reply.html_exception' => sub { _development('exception', @_) }); |
|
57
|
|
|
|
|
229
|
|
57
|
104
|
|
|
55
|
|
716
|
$app->helper('reply.html_not_found' => sub { _development('not_found', @_) }); |
|
55
|
|
|
|
|
247
|
|
58
|
104
|
|
|
|
|
542
|
$app->helper('reply.json_exception', => \&_json_exception); |
59
|
104
|
|
|
|
|
516
|
$app->helper('reply.json_not_found', => \&_json_not_found); |
60
|
104
|
|
|
|
|
567
|
$app->helper('reply.txt_exception', => \&_txt_exception); |
61
|
104
|
|
|
|
|
565
|
$app->helper('reply.txt_not_found', => \&_txt_not_found); |
62
|
|
|
|
|
|
|
|
63
|
104
|
|
|
|
|
609
|
$app->helper('timing.begin' => \&_timing_begin); |
64
|
104
|
|
|
|
|
617
|
$app->helper('timing.elapsed' => \&_timing_elapsed); |
65
|
104
|
|
|
|
|
636
|
$app->helper('timing.rps' => \&_timing_rps); |
66
|
104
|
|
|
|
|
600
|
$app->helper('timing.server_timing' => \&_timing_server_timing); |
67
|
|
|
|
|
|
|
|
68
|
104
|
|
|
56
|
|
636
|
$app->helper(ua => sub { shift->app->ua }); |
|
56
|
|
|
|
|
178
|
|
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub _asset { |
72
|
4
|
|
|
4
|
|
23
|
my $c = shift; |
73
|
4
|
|
|
|
|
13
|
$c->app->static->serve_asset($c, @_); |
74
|
4
|
|
|
|
|
22
|
$c->rendered; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
65
|
100
|
|
65
|
|
233
|
sub _block { ref $_[0] eq 'CODE' ? $_[0]() : $_[0] } |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub _content { |
80
|
125
|
|
|
125
|
|
296
|
my ($append, $replace, $c, $name, $content) = @_; |
81
|
125
|
|
100
|
|
|
358
|
$name ||= 'content'; |
82
|
|
|
|
|
|
|
|
83
|
125
|
|
100
|
|
|
316
|
my $hash = $c->stash->{'mojo.content'} //= {}; |
84
|
125
|
100
|
|
|
|
303
|
if (defined $content) { |
85
|
77
|
100
|
|
|
|
149
|
if ($append) { $hash->{$name} .= _block($content) } |
|
42
|
|
|
|
|
122
|
|
86
|
77
|
100
|
|
|
|
157
|
if ($replace) { $hash->{$name} = _block($content) } |
|
2
|
|
|
|
|
5
|
|
87
|
75
|
|
100
|
|
|
210
|
else { $hash->{$name} //= _block($content) } |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
125
|
|
100
|
|
|
564
|
return Mojo::ByteStream->new($hash->{$name} // ''); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub _convert_to_exception { |
94
|
63
|
|
|
63
|
|
172
|
my $e = shift; |
95
|
63
|
100
|
100
|
|
|
898
|
return (blessed $e && $e->isa('Mojo::Exception')) ? $e : Mojo::Exception->new($e); |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
18
|
|
66
|
18
|
|
55
|
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
|
|
55
|
return '' unless my $route = shift->match->endpoint; |
102
|
16
|
100
|
|
|
|
66
|
return @_ ? $route->name eq shift : $route->name; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub _development { |
106
|
112
|
|
|
112
|
|
357
|
my ($page, $c, $e) = @_; |
107
|
|
|
|
|
|
|
|
108
|
112
|
100
|
|
|
|
452
|
$c->helpers->log->error(($e = _convert_to_exception($e))->inspect) if $page eq 'exception'; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# Filtered stash snapshot |
111
|
112
|
|
|
|
|
441
|
my $stash = $c->stash; |
112
|
112
|
|
|
|
|
569
|
%{$stash->{snapshot} = {}} |
113
|
112
|
100
|
|
|
|
627
|
= map { $_ => $_ eq 'app' ? 'DUMMY' : $stash->{$_} } grep { !/^mojo\./ and defined $stash->{$_} } keys %$stash; |
|
131
|
100
|
|
|
|
527
|
|
|
480
|
|
|
|
|
2187
|
|
114
|
112
|
100
|
|
|
|
516
|
$stash->{exception} = $page eq 'exception' ? $e : undef; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# Render with fallbacks |
117
|
112
|
|
|
|
|
409
|
my $app = $c->app; |
118
|
112
|
|
|
|
|
476
|
my $mode = $app->mode; |
119
|
|
|
|
|
|
|
my $options = { |
120
|
112
|
100
|
66
|
|
|
718
|
format => $stash->{format} || $app->renderer->default_format, |
121
|
|
|
|
|
|
|
handler => undef, |
122
|
|
|
|
|
|
|
status => $page eq 'exception' ? 500 : 404, |
123
|
|
|
|
|
|
|
template => "$page.$mode" |
124
|
|
|
|
|
|
|
}; |
125
|
112
|
100
|
|
|
|
443
|
my $bundled = 'mojo/' . ($mode eq 'development' ? 'debug' : $page); |
126
|
112
|
100
|
|
|
|
358
|
return $c if _fallbacks($c, $options, $page, $bundled); |
127
|
4
|
|
|
|
|
37
|
_fallbacks($c, {%$options, format => 'html'}, $page, $bundled); |
128
|
4
|
|
|
|
|
64
|
return $c; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub _exception_format { |
132
|
126
|
|
|
126
|
|
269
|
my $c = shift; |
133
|
126
|
|
|
|
|
416
|
my $stash = $c->stash; |
134
|
126
|
|
66
|
|
|
855
|
$stash->{'mojo.exception_format'} ||= $c->app->exception_format; |
135
|
126
|
100
|
|
|
|
590
|
return $stash->{'mojo.exception_format'} unless @_; |
136
|
2
|
|
|
|
|
7
|
$stash->{'mojo.exception_format'} = shift; |
137
|
2
|
|
|
|
|
10
|
return $c; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub _fallbacks { |
141
|
116
|
|
|
116
|
|
354
|
my ($c, $options, $template, $bundled) = @_; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# Mode specific template |
144
|
116
|
100
|
|
|
|
599
|
return 1 if $c->render_maybe(%$options); |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# Normal template |
147
|
98
|
100
|
|
|
|
515
|
return 1 if $c->render_maybe(%$options, template => $template); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# Inline template |
150
|
42
|
|
|
|
|
204
|
my $stash = $c->stash; |
151
|
42
|
100
|
|
|
|
237
|
return undef unless $options->{format} eq 'html'; |
152
|
38
|
|
|
|
|
145
|
delete @$stash{qw(extends layout)}; |
153
|
38
|
|
|
|
|
206
|
return $c->render_maybe($bundled, %$options, handler => 'ep'); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
1
|
|
|
1
|
|
11
|
sub _file { _asset(shift, Mojo::Asset::File->new(path => shift)) } |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub _flash { |
159
|
25
|
|
|
25
|
|
44
|
my $c = shift; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# Check old flash |
162
|
25
|
|
|
|
|
70
|
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
|
|
|
|
37
|
my $values = ref $_[0] ? $_[0] : {@_}; |
167
|
7
|
|
100
|
|
|
27
|
@{$session->{new_flash} //= {}}{keys %$values} = values %$values; |
|
7
|
|
|
|
|
58
|
|
168
|
|
|
|
|
|
|
|
169
|
7
|
|
|
|
|
31
|
return $c; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub _http_exception { |
173
|
63
|
|
|
63
|
|
209
|
my ($c, $e) = @_; |
174
|
63
|
|
|
|
|
280
|
my $format = $c->exception_format; |
175
|
63
|
100
|
|
|
|
247
|
return $c->helpers->reply->txt_exception($e) if $format eq 'txt'; |
176
|
59
|
100
|
|
|
|
218
|
return $c->helpers->reply->json_exception($e) if $format eq 'json'; |
177
|
57
|
|
|
|
|
188
|
return $c->helpers->reply->html_exception($e); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub _http_not_found { |
181
|
59
|
|
|
59
|
|
155
|
my $c = shift; |
182
|
59
|
|
|
|
|
313
|
my $format = $c->exception_format; |
183
|
59
|
100
|
|
|
|
258
|
return $c->helpers->reply->txt_not_found if $format eq 'txt'; |
184
|
57
|
100
|
|
|
|
193
|
return $c->helpers->reply->json_not_found if $format eq 'json'; |
185
|
55
|
|
|
|
|
176
|
return $c->helpers->reply->html_not_found; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub _inactivity_timeout { |
189
|
7
|
|
|
7
|
|
26
|
my ($c, $timeout) = @_; |
190
|
7
|
|
100
|
|
|
32
|
my $stream = Mojo::IOLoop->stream($c->tx->connection // ''); |
191
|
7
|
100
|
|
|
|
53
|
$stream->timeout($timeout) if $stream; |
192
|
7
|
|
|
|
|
28
|
return $c; |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub _is_fresh { |
196
|
20
|
|
|
20
|
|
63
|
my ($c, %options) = @_; |
197
|
20
|
|
|
|
|
60
|
return $c->app->static->is_fresh($c, \%options); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub _json_exception { |
201
|
2
|
|
|
2
|
|
9
|
my ($c, $e) = @_; |
202
|
2
|
|
|
|
|
8
|
$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
|
|
12
|
sub _json_not_found { shift->render(json => {error => 'Not Found'}, status => 404) } |
208
|
|
|
|
|
|
|
|
209
|
3558
|
|
66
|
3558
|
|
9485
|
sub _log { $_[0]->stash->{'mojo.log'} ||= $_[0]->app->log->context('[' . $_[0]->req->request_id . ']') } |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _proxy_method_p { |
212
|
20
|
|
|
20
|
|
51
|
my ($method, $c) = (shift, shift); |
213
|
20
|
|
|
|
|
75
|
return _proxy_start_p($c, $c->ua->build_tx($method, @_)); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
sub _proxy_start_p { |
217
|
22
|
|
|
22
|
|
54
|
my ($c, $source_tx) = @_; |
218
|
22
|
|
|
|
|
75
|
my $tx = $c->render_later->tx; |
219
|
|
|
|
|
|
|
|
220
|
22
|
|
|
|
|
142
|
my $promise = Mojo::Promise->new; |
221
|
|
|
|
|
|
|
$source_tx->res->content->auto_upgrade(0)->auto_decompress(0)->once( |
222
|
|
|
|
|
|
|
body => sub { |
223
|
21
|
|
|
21
|
|
38
|
my $source_content = shift; |
224
|
|
|
|
|
|
|
|
225
|
21
|
|
|
|
|
71
|
my $source_res = $source_tx->res; |
226
|
21
|
|
|
|
|
56
|
my $res = $tx->res; |
227
|
21
|
|
|
|
|
57
|
my $content = $res->content; |
228
|
21
|
|
|
|
|
66
|
$res->code($source_res->code)->message($source_res->message); |
229
|
21
|
|
|
|
|
66
|
my $headers = $source_res->headers->clone->dehop; |
230
|
21
|
|
|
|
|
63
|
$content->headers($headers); |
231
|
21
|
|
|
|
|
90
|
$promise->resolve; |
232
|
|
|
|
|
|
|
|
233
|
21
|
|
|
|
|
80
|
my $source_stream = Mojo::IOLoop->stream($source_tx->connection); |
234
|
21
|
50
|
|
|
|
68
|
return unless my $stream = Mojo::IOLoop->stream($tx->connection); |
235
|
|
|
|
|
|
|
|
236
|
21
|
100
|
|
|
|
72
|
my $write = $source_content->is_chunked ? 'write_chunk' : 'write'; |
237
|
|
|
|
|
|
|
$source_content->unsubscribe('read')->on( |
238
|
|
|
|
|
|
|
read => sub { |
239
|
10
|
|
|
|
|
33
|
my $data = pop; |
240
|
10
|
100
|
|
|
|
87
|
$content->$write(length $data ? $data : ()) and $tx->resume; |
|
|
50
|
|
|
|
|
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# Throttle transparently when backpressure rises |
243
|
10
|
50
|
|
|
|
50
|
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
|
|
|
|
|
73
|
); |
248
|
|
|
|
|
|
|
|
249
|
21
|
50
|
|
|
|
113
|
$source_res->once(finish => sub { $content->$write('') and $tx->resume }); |
|
21
|
|
|
|
|
96
|
|
250
|
|
|
|
|
|
|
} |
251
|
22
|
|
|
|
|
73
|
); |
252
|
22
|
|
|
|
|
73
|
weaken $source_tx; |
253
|
22
|
|
|
22
|
|
138
|
$source_tx->once(finish => sub { $promise->reject(_tx_error(@_)) }); |
|
22
|
|
|
|
|
112
|
|
254
|
|
|
|
|
|
|
|
255
|
22
|
|
|
1
|
|
75
|
$c->ua->start_p($source_tx)->catch(sub { }); |
256
|
|
|
|
|
|
|
|
257
|
22
|
|
|
|
|
153
|
return $promise; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub _redirect_to { |
261
|
26
|
|
|
26
|
|
59
|
my $c = shift; |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# Don't override 3xx status |
264
|
26
|
|
|
|
|
89
|
my $res = $c->res; |
265
|
26
|
|
|
|
|
105
|
$res->headers->location($c->url_for(@_)); |
266
|
26
|
100
|
|
|
|
132
|
return $c->rendered($res->is_redirect ? () : 302); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
sub _respond_to { |
270
|
83
|
50
|
|
83
|
|
553
|
my ($c, $args) = (shift, ref $_[0] ? $_[0] : {@_}); |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# Find target |
273
|
83
|
|
|
|
|
170
|
my $target; |
274
|
83
|
|
|
|
|
239
|
my $renderer = $c->app->renderer; |
275
|
83
|
|
|
|
|
183
|
my @formats = @{$renderer->accepts($c)}; |
|
83
|
|
|
|
|
363
|
|
276
|
83
|
100
|
|
|
|
329
|
for my $format (@formats ? @formats : ($renderer->default_format)) { |
277
|
89
|
100
|
|
|
|
335
|
next unless $target = $args->{$format}; |
278
|
77
|
|
|
|
|
311
|
$c->stash->{format} = $format; |
279
|
77
|
|
|
|
|
188
|
last; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# Fallback |
283
|
83
|
100
|
|
|
|
258
|
unless ($target) { |
284
|
6
|
100
|
|
|
|
28
|
return $c->rendered(204) unless $target = $args->{any}; |
285
|
4
|
|
|
|
|
13
|
delete $c->stash->{format}; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# Dispatch |
289
|
81
|
100
|
|
|
|
634
|
ref $target eq 'CODE' ? $target->($c) : $c->render(%$target); |
290
|
|
|
|
|
|
|
|
291
|
81
|
|
|
|
|
499
|
return $c; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
sub _static { |
295
|
7
|
|
|
7
|
|
28
|
my ($c, $file) = @_; |
296
|
7
|
100
|
|
|
|
29
|
croak qq{Static file "$file" not found} unless $c->app->static->serve($c, $file); |
297
|
6
|
|
|
|
|
33
|
return $c->rendered; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
261
|
|
|
261
|
|
1453
|
sub _timing_begin { shift->stash->{'mojo.timing'}{shift()} = [gettimeofday] } |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub _timing_elapsed { |
303
|
258
|
|
|
258
|
|
555
|
my ($c, $name) = @_; |
304
|
258
|
100
|
|
|
|
641
|
return undef unless my $started = $c->stash->{'mojo.timing'}{$name}; |
305
|
257
|
|
|
|
|
1409
|
return tv_interval($started, [gettimeofday()]); |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
259
|
100
|
|
259
|
|
3091
|
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
|
|
|
|
|
7
|
my $value = $metric; |
313
|
4
|
100
|
|
|
|
15
|
$value .= qq{;desc="$desc"} if defined $desc; |
314
|
4
|
100
|
|
|
|
11
|
$value .= ";dur=$dur" if defined $dur; |
315
|
4
|
|
|
|
|
13
|
$c->res->headers->append('Server-Timing' => $value); |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
22
|
|
50
|
22
|
|
62
|
sub _tx_error { (shift->error // {})->{message} // 'Unknown error' } |
|
|
|
50
|
|
|
|
|
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub _txt_exception { |
321
|
4
|
|
|
4
|
|
13
|
my ($c, $e) = @_; |
322
|
4
|
|
|
|
|
17
|
$c->stash->{exception} = _convert_to_exception($e); |
323
|
4
|
100
|
|
|
|
19
|
return $c->render(text => $e, format => 'txt', status => 500) if $c->app->mode eq 'development'; |
324
|
1
|
|
|
|
|
9
|
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
|
|
10
|
my $c = shift; |
331
|
5
|
|
|
|
|
17
|
return $c->url_for(@_)->query($c->req->url->query->clone); |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
sub _validation { |
335
|
250
|
|
|
250
|
|
438
|
my $c = shift; |
336
|
|
|
|
|
|
|
|
337
|
250
|
|
|
|
|
631
|
my $stash = $c->stash; |
338
|
250
|
100
|
|
|
|
1038
|
return $stash->{'mojo.validation'} if $stash->{'mojo.validation'}; |
339
|
|
|
|
|
|
|
|
340
|
104
|
|
|
|
|
331
|
my $req = $c->req; |
341
|
104
|
|
|
|
|
449
|
my $token = $c->session->{csrf_token}; |
342
|
104
|
|
|
|
|
323
|
my $header = $req->headers->header('X-CSRF-Token'); |
343
|
104
|
|
|
|
|
397
|
my $hash = $req->params->to_hash; |
344
|
104
|
100
|
33
|
|
|
379
|
$hash->{csrf_token} //= $header if $token && $header; |
|
|
|
100
|
|
|
|
|
345
|
104
|
|
|
|
|
215
|
$hash->{$_} = $req->every_upload($_) for map { $_->name } @{$req->uploads}; |
|
3
|
|
|
|
|
11
|
|
|
104
|
|
|
|
|
314
|
|
346
|
104
|
|
|
|
|
362
|
my $v = $c->app->validator->validation->input($hash); |
347
|
104
|
|
|
|
|
373
|
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"content">, 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"content">, 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"session">, 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"stash">. |
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"session">. |
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"stash">. |
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"url_for">. |
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"stash"> 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"stash"> 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"accepts">. |
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 L"timing-Ebegin"> 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">. |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
=head2 ua |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
%= ua->get('mojolicious.org')->result->dom->at('title')->text |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
Alias for L. |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
=head2 url_for |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
%= url_for 'named', foo => 'bar', baz => 'yada' |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
Alias for L. |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
%= url_for('/index.html')->query(foo => 'bar') |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
=head2 url_with |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
%= url_with 'named', foo => 'bar', baz => 'yada' |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
Does the same as L"url_for">, but inherits query parameters from the current request. |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
%= url_with->query({page => 2}) |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
=head2 validation |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
my $v = $c->validation; |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
Get L object for current request to validate file uploads as well as C and |
875
|
|
|
|
|
|
|
C parameters extracted from the query string and C or C |
876
|
|
|
|
|
|
|
message body. Parts of the request body need to be loaded into memory to parse C parameters, so you have to make |
877
|
|
|
|
|
|
|
sure it is not excessively large. There's a 16MiB limit for requests by default. |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
# Validate GET/POST parameter |
880
|
|
|
|
|
|
|
my $v = $c->validation; |
881
|
|
|
|
|
|
|
$v->required('title', 'trim')->size(3, 50); |
882
|
|
|
|
|
|
|
my $title = $v->param('title'); |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
# Validate file upload |
885
|
|
|
|
|
|
|
my $v = $c->validation; |
886
|
|
|
|
|
|
|
$v->required('tarball')->upload->size(1, 1048576); |
887
|
|
|
|
|
|
|
my $tarball = $v->param('tarball'); |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
=head1 METHODS |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
L inherits all methods from L and implements the following |
892
|
|
|
|
|
|
|
new ones. |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
=head2 register |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
$plugin->register(Mojolicious->new); |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
Register helpers in L application. |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=head1 SEE ALSO |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
L, L, L. |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
=cut |