File Coverage

blib/lib/Mojolicious/Plugin/ErrorsAndWarnings.pm
Criterion Covered Total %
statement 19 19 100.0
branch 2 2 100.0
condition 3 5 60.0
subroutine 5 5 100.0
pod 1 1 100.0
total 30 32 93.7


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::ErrorsAndWarnings;
2 3     3   18527 use Mojo::Base 'Mojolicious::Plugin';
  3         6  
  3         23  
3              
4             our $VERSION = '0.01';
5              
6             has config_key => 'codes';
7             has stash_key => 'plugin.errors';
8              
9             sub register {
10 3     3 1 118 my ($plugin, $app, $conf) = @_;
11              
12 3         6 for my $type (qw/errors warnings/) {
13 6         114 my $singular = ($type =~ m,^(.*)s$,,)[0];
14              
15             # Create helpers to add values to relevant stash
16 6     8   47 $app->helper("add_$singular" => sub { $plugin->_add_to_stash($type, @_) });
  8         61317  
17              
18             # Create accessors for errors and warnings
19 6     7   232 $app->helper($type => sub { shift->stash->{$plugin->stash_key}->{$type} });
  7         9618  
20             }
21             }
22              
23             sub _add_to_stash {
24 8     8   31 my ($plugin, $type, $app, $name, %attrs) = @_;
25              
26 8         27 my $data = {};
27 8 100       31 if (my $conf = $app->config($plugin->config_key)) {
28 6   66     427 $data = $conf->{$name} // $conf->{default} // {};
      50        
29             }
30              
31             # Add the code name and attrs. Added in this order because %attrs can
32             # override the code.
33 8         133 $data = {%{$data}, code => $name, %attrs};
  8         40  
34 8         16 push @{$app->stash->{$plugin->stash_key}->{$type}}, $data;
  8         27  
35              
36 8         131 return $data;
37             }
38              
39             1;
40              
41             =head1 NAME
42              
43             Mojolicious::Plugin::ErrorsAndWarnings - Store errors & warnings during a request
44              
45             =head1 SYNOPSIS
46              
47             # Mojolicious example
48             package MyApp;
49             use Mojo::Base 'Mojolicious';
50              
51             sub startup {
52             my $self = shift;
53              
54             $self->plugin('ErrorsAndWarnings');
55              
56             # Router
57             my $r = $self->routes;
58             $r->get('/')->to(cb => sub {
59             my $c = shift;
60             $c->add_error('first_error');
61             $c->add_error('second_error', more => 'detail');
62              
63             # {"errors":[{"code":"first_error"},{"code":"second_error","more":"detail"}]}
64             $c->render(json => { errors => $c->errors });
65             });
66             }
67              
68             1;
69              
70             =head1 DESCRIPTION
71              
72             L is a basic plugin for L
73             which provides helpers to store and retrieve user-defined errors and warnings.
74             This is particularly useful to help collect errors and warnings from within
75             multiple method calls during a request cycle. At the end of the request, the
76             error and warning objects provide additional information about any problems
77             encountered while performing an operation.
78              
79             Adding errors or warnings will store them under the L
80             stash|Mojolicious::Controller/stash> key C by default. Don't
81             access this stash value directly. Use the C<$c-Eerrors> and
82             C<$c-Ewarnings> accessors instead.
83              
84             # add errors and warnings using the imported helpers
85             $c->add_error('first_error');
86             $c->add_warning('first_warning');
87              
88             # {"errors":[{"code":"first_error"}], "warnings":[{"code":"first_warning"}]}
89             $c->render(json => {errors => $c->errors, warnings => $c->warnings});
90              
91             The first argument to L or L is referred to as the
92             C. This an application-specific error or warning code, expressed as a
93             string value.
94              
95             $c->add_error('sql', status => 400, title => 'Your SQL is malformed.');
96             $c->add_warning('search', title => 'Invalid search column.', path => 'pw');
97              
98             # {
99             # "errors": [
100             # {
101             # "code": "sql",
102             # "status": 400,
103             # "title": "Your SQL is malformed."
104             # }
105             # ],
106             # "warnings": [
107             # {
108             # "code": "search",
109             # "path": "password",
110             # "title": "Invalid search column."
111             # }
112             # ]
113             # }
114             $c->render(json => {errors => $c->errors, warnings => $c->warnings});
115              
116             Additional members can be added to provide more specific information about the
117             problem. See also L for examples of other
118             members you might want to use.
119              
120             =head1 ATTRIBUTES
121              
122             L implements the following attributes.
123              
124             =head2 config_key
125              
126             The name of the config key to inspect for user-defined error and warning codes.
127             Defaults to C.
128              
129             The plugin will merge default values from an app's config if a matching key is
130             found. See the example below.
131              
132             # Mojolicious::Lite example merging config values
133             use Mojolicious::Lite;
134              
135             plugin 'ErrorsAndWarnings';
136              
137             app->config({
138             # config_key attribute is `codes' by default
139             codes => {
140             # Default key/values merged for unmatched code names
141             'default' => {status => 400},
142              
143             # Global codes
144             'forbidden' => {status => 403, title => 'Permission denied to resource.'},
145             'not_found' => {status => 404, title => 'Not found.'},
146             'method_not_allowed' => {status => 405, title => 'Method not allowed.'},
147             },
148             });
149              
150             get '/' => sub {
151             my $c = shift;
152              
153             $c->add_error('not_found');
154             $c->add_error('user_defined_err', foo => 'bar bar' );
155              
156             # {
157             # "errors": [
158             # {
159             # "code": "not_found",
160             # "status": 404,
161             # "title": "Not found."
162             # },
163             # {
164             # "code": "user_defined_err",
165             # "status": 400,
166             # "foo": "bar bar"
167             # }
168             # ]
169             # }
170             $c->render(json => { errors => $c->errors });
171             };
172              
173             =head2 stash_key
174              
175             Name of the L key to store the
176             errors and warnings. Defaults to C.
177              
178             Don't access this stash value directly. Use the C<$c-Eerrors> and
179             C<$c-Ewarnings> accessors instead.
180              
181             =head1 HELPERS
182              
183             L implements the following helpers.
184              
185             =head2 add_error
186              
187             $self->add_error('user_not_found');
188             $self->add_error('user_not_found', additional => 'Error Attr');
189             $self->add_error('user_not_found', code => 'rename_error_code');
190              
191             Pushes to the errors stash.
192              
193             =head2 add_warning
194              
195             $self->add_warning('field_ignored');
196             $self->add_warning('field_ignored', path => 'username');
197             $self->add_warning('field_ignored', code => 'rename_warning_code');
198              
199             Pushes to the warnings stash.
200              
201             =head2 errors
202              
203             Returns an C of errors.
204              
205             =head2 warnings
206              
207             Returns an C of warnings.
208              
209             =head1 METHODS
210              
211             L inherits all methods from L
212             and implements the following new ones.
213              
214             =head2 register
215              
216             $plugin->register(Mojolicious->new);
217              
218             Register plugin in L application.
219              
220             =head1 COPYRIGHT AND LICENSE
221              
222             Copyright (C) 2015, Paul Williams.
223              
224             This program is free software, you can redistribute it and/or modify it under
225             the terms of the Artistic License version 2.0.
226              
227             =head1 AUTHOR
228              
229             Paul Williams
230              
231             =head1 SEE ALSO
232              
233             L, L, L.
234              
235             =cut