File Coverage

blib/lib/Yukki/Error.pm
Criterion Covered Total %
statement 20 42 47.6
branch n/a
condition 0 4 0.0
subroutine 7 15 46.6
pod 4 8 50.0
total 31 69 44.9


line stmt bran cond sub pod time code
1             package Yukki::Error;
2             $Yukki::Error::VERSION = '0.99_01'; # TRIAL
3              
4 4     4   580 $Yukki::Error::VERSION = '0.9901';use v5.24;
  4         13  
5 4     4   446 use utf8;
  4         18  
  4         25  
6 4     4   452 use Moo;
  4         8744  
  4         25  
7              
8             extends qw( HTTP::Throwable::Factory );
9              
10 4     4   3386 use Yukki::Web::View;
  4         14  
  4         120  
11              
12 4     4   27 use namespace::clean;
  4         9  
  4         31  
13              
14 4         42 use Sub::Exporter -setup => {
15             exports => [ qw< http_throw http_exception > ],
16 4     4   1493 };
  4         7729  
17              
18             # ABSTRACT: Yukki's exception class
19              
20              
21 0     0 1   sub base_class { 'Yukki::Error' }
22 0     0 1   sub extra_roles { 'Yukki::Error::Body' }
23              
24              
25             sub http_exception {
26 0     0 1   my ($name, $args) = @_;
27              
28 0   0       my %args = %{ $args // {} };
  0            
29 0   0       my $status = delete $args{status} // 'InternalServerError';
30              
31 0           Yukki::Error->new_exception($status => {
32             %args,
33             message => "$name",
34             });
35             }
36              
37              
38             sub http_throw {
39 0     0 1   my ($name, $args) = @_;
40              
41 0           http_exception($name, $args)->throw;
42             }
43              
44             sub BUILDARGS {
45 0     0 0   my ($class, $args) = @_;
46 0           $args;
47             }
48              
49              
50             {
51             package Yukki::Error::Body;
52             $Yukki::Error::Body::VERSION = '0.99_01'; # TRIAL
53              
54 4     4   2877 $Yukki::Error::Body::VERSION = '0.9901';use Moo::Role;
  4         8  
  4         22  
55              
56              
57             sub body {
58 0     0 0   my ($self, $env) = @_;
59              
60 0           my $app = $env->{'yukki.app'};
61 0           my $view = Yukki::Web::View->new(app => $app);
62 0           my $ctx = Yukki::Web::Context->new(env => $env);
63              
64 0           my $template = $view->prepare_template(
65             template => 'error.html',
66             directives => [
67             '#error-page' => 'error_message',
68             ],
69             );
70              
71 0           $ctx->response->page_title($self->reason);
72              
73 0           return $view->render_page(
74             template => $template,
75             context => $ctx,
76             vars => {
77             'error_message' => $self->message,
78             },
79             );
80             }
81              
82              
83             sub body_headers {
84 0     0 0   my ($self, $body) = @_;
85              
86             return [
87 0           'Content-type' => 'text/html',
88             'Content-length' => length $body,
89             ];
90             }
91              
92              
93             sub as_string {
94 0     0 0   my $self = shift;
95 0           return $self->message;
96             }
97              
98             around as_psgi => sub {
99             shift; # original method is ignored
100             my ($self, $env) = @_;
101             my $body = $self->body( $env );
102             my $headers = $self->build_headers( $body );
103             [ $self->status_code, $headers, [ defined $body ? $body : () ] ];
104             };
105              
106             }
107              
108             1;
109              
110             __END__
111              
112             =pod
113              
114             =encoding UTF-8
115              
116             =head1 NAME
117              
118             Yukki::Error - Yukki's exception class
119              
120             =head1 VERSION
121              
122             version 0.99_01
123              
124             =head1 SYNOPSIS
125              
126             Yukki::Error->throw("Something really bad.", { ... });
127              
128             =head1 DESCRIPTION
129              
130             If you are familiar with L<HTTP::Throwable::Factory>, this is similar to that (and is based on that).
131              
132             However, there are two differences. First, the error message is given primacy rather than exception type, so you can just use this to throw an exception:
133              
134             use Yukki::Error qw( http_throw );
135             http_throw('something went wrong');
136              
137             Since you almost always want your exception to be an internal server error of some kind, this makes more sense to me than having to write:
138              
139             use HTTP::Throwable::Factory qw( http_throw );
140             http_throw(InternalServerError => {
141             message => 'something went wrong',
142             });
143              
144             To specify the type of exception, us C<status>:
145              
146             use Yukki::Error qw( http_throw );
147             http_throw('something was not found', {
148             status => 'NotFound',
149             });
150              
151             The second difference is that all exceptions thrown by this factory inherit from L<Yukki::Error>, so this works:
152              
153             use Scalar::Util qw( blessed );
154             use Try::Tiny;
155             try { ... }
156             catch {
157             if (blassed $_ && $_->isa("Yukki::Error") {
158             # we now this is an application error from Yukki
159             }
160             };
161              
162             This makes it easy to know whether Yukki generated the exception or something else did.
163              
164             =head1 EXPORTS
165              
166             =head2 http_exception
167              
168             my $error = http_exception('message', {
169             status => 'InternalServerError',
170             show_stask_trace => 0,
171             });
172              
173             Creates a new exception object. Calls the constructor for L<Yukki:Error> and applied the L<HTTP::Throwable> status role needed (prior to construction actually).
174              
175             =head2 http_throw
176              
177             http_throw('message', {
178             status => 'InternalServerError',
179             show_stask_trace => 0,
180             });
181              
182             Constructs the exception (via L</http_exception>) and throws it.
183              
184             =for Pod::Coverage BUILDARGS
185             base_class
186             extra_roles
187              
188             =head1 METHODS
189              
190             =head2 body
191              
192             Renders the HTML body for the error.
193              
194             =head2 body_headers
195              
196             Setup the HTTP headers.
197              
198             =head2 as_string
199              
200             Returns the message.
201              
202             =head1 AUTHOR
203              
204             Andrew Sterling Hanenkamp <hanenkamp@cpan.org>
205              
206             =head1 COPYRIGHT AND LICENSE
207              
208             This software is copyright (c) 2017 by Qubling Software LLC.
209              
210             This is free software; you can redistribute it and/or modify it under
211             the same terms as the Perl 5 programming language system itself.
212              
213             =cut