File Coverage

blib/lib/Mojolicious/Plugin/MoreHelpers.pm
Criterion Covered Total %
statement 9 108 8.3
branch 0 28 0.0
condition 0 18 0.0
subroutine 3 20 15.0
pod 1 1 100.0
total 13 175 7.4


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::MoreHelpers;
2 1     1   795 use Mojo::Base 'Mojolicious::Plugin';
  1         3  
  1         9  
3              
4 1     1   1634 use Data::Validate::IP;
  1         32368  
  1         155  
5 1     1   588 use Email::Address;
  1         6853  
  1         1827  
6              
7             our $VERSION = '0.04';
8             $VERSION = eval $VERSION;
9              
10             sub register {
11 0     0 1   my ($self, $app, $conf) = @_;
12              
13 0   0       $conf->{header_message} //= 'X-Message';
14              
15             # Route params
16             $app->helper(route_params => sub {
17 0     0     my ($c, @names) = @_;
18              
19 0           my $route = $c->match->endpoint;
20              
21 0           my %params;
22              
23 0           while ($route) {
24 0           for my $name (@names) {
25 0 0         next if exists $params{$name};
26 0 0         next unless exists $route->to->{$name};
27              
28 0           $params{$name} = $route->to->{$name};
29             }
30              
31 0           $route = $route->parent;
32             }
33              
34 0           return \%params;
35 0           });
36              
37             # Simple onle-level depth object validation
38             $app->helper(validation_json => sub {
39 0     0     my ($c) = @_;
40              
41 0           my $v = $c->validation;
42              
43 0   0       my $json = $c->req->json || { };
44 0 0         $json = { } unless ref $json eq 'HASH';
45              
46 0           for my $key (keys %$json) {
47 0           my $success = 0;
48              
49 0 0         if (not ref $json->{$key}) { $success = 1 }
  0 0          
50              
51             elsif (ref $json->{$key} eq 'ARRAY') {
52             # Success only if there are no any refs in array
53 0 0         $success = 1 unless grep { ref $_ } @{$json->{$key}};
  0            
  0            
54             }
55              
56 0 0         $v->input->{$key} = $json->{$key} if $success;
57             }
58              
59 0           return $v;
60 0           });
61              
62             my $reply_headers = sub {
63 0     0     my ($c, $headers, $message) = @_;
64              
65 0 0 0       $headers->{$conf->{header_message}} //= $message
66             if defined $message;
67              
68 0           my $h = $c->res->headers;
69 0           map { $h->header($_ => $headers->{$_}) }
70 0           grep { defined $headers->{$_} } keys %$headers;
  0            
71 0           };
72              
73             $app->helper('reply_json.success' => sub {
74 0     0     my ($c, $json, %headers) = @_;
75              
76 0           $reply_headers->($c, \%headers);
77              
78 0 0         my $status = $c->req->method eq 'POST' ? 201 : 200;
79 0   0       $c->render(json => $json // { }, status => $status);
80 0           });
81              
82             $app->helper('reply_json.bad_request' => sub {
83 0     0     my ($c, %headers) = @_;
84              
85 0           $reply_headers->($c, \%headers, "error.validation_failed");
86 0           $c->render(json => { }, status => 400);
87 0           });
88              
89             $app->helper('reply_json.unauthorized' => sub {
90 0     0     my ($c, %headers) = @_;
91              
92 0           $reply_headers->($c, \%headers, "error.authorization_failed");
93 0           $c->render(json => { }, status => 401);
94 0           });
95              
96             $app->helper('reply_json.forbidden' => sub {
97 0     0     my ($c, %headers) = @_;
98              
99 0           $reply_headers->($c, \%headers, "error.access_denied");
100 0           $c->render(json => { }, status => 403);
101 0           });
102              
103             $app->helper('reply_json.not_found' => sub {
104 0     0     my ($c, %headers) = @_;
105              
106 0           $reply_headers->($c, \%headers, "error.resource_not_found");
107 0           $c->render(json => { }, status => 404);
108 0           });
109              
110             $app->helper('reply_json.not_acceptable' => sub {
111 0     0     my ($c, %headers) = @_;
112              
113 0           $reply_headers->($c, \%headers, "error.not_acceptable");
114 0           $c->render(json => { }, status => 406);
115 0           });
116              
117             $app->helper('reply_json.unprocessable' => sub {
118 0     0     my ($c, %headers) = @_;
119              
120 0           $reply_headers->($c, \%headers, "error.unprocessable_entity");
121 0           $c->render(json => { }, status => 422);
122 0           });
123              
124             $app->helper('reply_json.locked' => sub {
125 0     0     my ($c, %headers) = @_;
126              
127 0           $reply_headers->($c, \%headers, "error.temporary_locked");
128 0           $c->render(json => { }, status => 423);
129 0           });
130              
131             $app->helper('reply_json.rate_limit' => sub {
132 0     0     my ($c, %headers) = @_;
133              
134 0           $reply_headers->($c, \%headers, "error.too_many_requests");
135 0           $c->render(json => { }, status => 429);
136 0           });
137              
138             $app->helper('reply_json.unavailable' => sub {
139 0     0     my ($c, %headers) = @_;
140              
141 0           $reply_headers->($c, \%headers, "error.service_unavailable");
142 0           $c->render(json => { }, status => 503);
143 0           });
144              
145             $app->helper('reply_json.dispatch' => sub {
146 0     0     my ($c, $status, $message, %headers) = @_;
147              
148 0 0 0       die "Wrong reply_json dispatch status\n"
149             unless defined $status and not ref $status;
150              
151 0 0 0       die "Wrong reply_json dispatch message\n"
152             unless defined $message and not ref $message;
153              
154             my %hash = (
155 0           success => sub { $c->reply_json->success(@_) },
156 0           bad_request => sub { $c->reply_json->bad_request(@_) },
157 0           unauthorized => sub { $c->reply_json->unauthorized(@_) },
158 0           forbidden => sub { $c->reply_json->forbidden(@_) },
159 0           not_found => sub { $c->reply_json->not_found(@_) },
160 0           unprocessable => sub { $c->reply_json->unprocessable(@_) },
161 0           locked => sub { $c->reply_json->locked(@_) },
162 0           rate_limit => sub { $c->reply_json->rate_limit(@_) },
163 0           unavailable => sub { $c->reply_json->unavailable(@_) },
164 0           );
165              
166 0           my $sub = $hash{$status};
167              
168 0 0         die "Wrong reply_json dispatch status '$status'\n"
169             unless defined $sub;
170              
171 0           $sub->(%headers, $conf->{header_message} => $message);
172 0           });
173              
174             $app->validator->add_check(inet_address => sub {
175 0     0     my ($v, $name, $value) = @_;
176              
177 0 0         return is_ip $value ? undef : 1;
178 0           });
179              
180             $app->validator->add_check(email_address => sub {
181 0     0     my ($validate, $name, $value) = @_;
182              
183 0           my ($email) = Email::Address->parse($value);
184 0 0 0       return defined $email && $email->address ? undef : 1;
185 0           });
186             }
187              
188             1;
189              
190             =encoding utf8
191              
192             =head1 NAME
193              
194             Mojolicious::Plugin::MoreHelpers - More helpers lacking in Mojolicious
195              
196             =head1 SYNOPSIS
197              
198             # Mojolicious
199             $app->plugin('MoreHelpers');
200              
201             # Mojolicious::Lite
202             plugin 'MoreHelpers';
203              
204             =head1 DESCRIPTION
205              
206             L is a mingle of helpers lacking in
207             L Web framework for REST-like APIs.
208              
209             =head1 HELPERS
210              
211             L implements the following helpers.
212              
213             =head2 route_params
214              
215             my $params = $c->route_params(@names);
216              
217             Recursive collect current route params and his parents.
218              
219             =head2 validation_json
220              
221             my $v = $c->validation_json;
222              
223             Merge flat request JSON object with validation.
224              
225             =head2 headers_response
226              
227             my $h = $c->headers_response(%headers);
228              
229             Set multiple reponse headers in one time.
230              
231             =head2 reply_json->success
232              
233             $c->reply_json->success($data, %headers);
234              
235             Render the success JSON object with status code, depend on POST or GET request.
236              
237             =head2 reply_json->bad_request
238              
239             $c->reply_json->bad_request(%headers);
240              
241             Render empty JSON object with 400 Bad Request HTTP status.
242              
243             =head2 reply_json->unquthorized
244              
245             $c->reply_json->unauthorized(%headers);
246              
247             Render empty JSON object with 401 HTTP status.
248              
249             =head2 reply_json->forbidden
250              
251             $c->reply_json->forbidden(%headers);
252              
253             Render empty JSON object with 403 Forbidden HTTP status.
254              
255             =head2 reply_json->not_found
256              
257             $c->reply_json->not_found(%headers);
258              
259             Render empty JSON object with 404 Not Found HTTP status.
260              
261             =head2 reply_json->not_acceptable
262              
263             $c->reply-_json>not_acceptable(%headers);
264              
265             Render empty JSON object with 406 HTTP status.
266              
267             =head2 reply_json->unprocessable
268              
269             $c->reply_json->unprocessable(%headers);
270              
271             Render empty JSON object with 422 HTTP status.
272              
273             =head2 reply_json->locked
274              
275             $c->reply_json->locked(%headers);
276              
277             Render empty JSON object with 423 HTTP status.
278              
279             =head2 reply_json->rate_limit
280              
281             $c->reply_json->rate_limit(%headers);
282              
283             Render empty JSON object with 429 HTTP status.
284              
285             =head2 reply_json->unavailable
286              
287             $c->reply_json->unavailable(%headers);
288              
289             Render empty JSON object with 503 HTTP status.
290              
291             =head2 reply_json->dispatch
292              
293             $c->reply_json->dispatch($status, %headers);
294              
295             Dispatch with status and render properly error code.
296              
297             =head1 CHECKS
298              
299             Validation checks.
300              
301             =head2 inet_address
302              
303             String value is a internet IPv4 or IPv6 address.
304              
305             =head2 email_address
306              
307             String value is a valie Email address.
308              
309             =head1 METHODS
310              
311             L inherits all methods from L
312             and implements the following new ones.
313              
314             =head2 register
315              
316             $plugin->register(Mojolicious->new);
317              
318             Register helpers in L application.
319              
320             =head1 SEE ALSO
321              
322             L.
323              
324             =head1 SUPPORT
325              
326             =head2 Bugs / Feature Requests
327              
328             Bugs should always be submitted via the GitHub bug tracker.
329              
330             L
331              
332             =head2 Source Code
333              
334             Feel free to fork the repository and submit pull requests.
335              
336             L
337              
338             =head1 AUTHOR
339              
340             Dmitry Krutikov Emonstar@cpan.orgE
341              
342             =head1 COPYRIGHT AND LICENSE
343              
344             Copyright 2020 Dmitry Krutikov.
345              
346             This library is free software; you can redistribute it and/or modify it
347             under the same terms as Perl itself.
348              
349             =cut
350