| blib/lib/MVC/Neaf/Exception.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 55 | 55 | 100.0 |
| branch | 17 | 18 | 94.4 |
| condition | 24 | 29 | 82.7 |
| subroutine | 14 | 14 | 100.0 |
| pod | 8 | 8 | 100.0 |
| total | 118 | 124 | 95.1 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package MVC::Neaf::Exception; | ||||||
| 2 | |||||||
| 3 | 95 | 95 | 68784 | use strict; | |||
| 95 | 214 | ||||||
| 95 | 2878 | ||||||
| 4 | 95 | 95 | 544 | use warnings; | |||
| 95 | 207 | ||||||
| 95 | 4387 | ||||||
| 5 | our $VERSION = '0.2901'; | ||||||
| 6 | |||||||
| 7 | =head1 NAME | ||||||
| 8 | |||||||
| 9 | MVC::Neaf::Exception - Exception class for Not Even A Framework. | ||||||
| 10 | |||||||
| 11 | =head1 DESCRIPTION | ||||||
| 12 | |||||||
| 13 | Currently internal signalling or L |
||||||
| 14 | mechanism. To avoid collisions with user's exceptions or Perl errors, | ||||||
| 15 | these internal exceptions are blessed into this class. | ||||||
| 16 | |||||||
| 17 | Please see the neaf_err() function in L |
||||||
| 18 | |||||||
| 19 | By convention, C |
||||||
| 20 | will be treated exactly the same by Neaf. | ||||||
| 21 | |||||||
| 22 | B |
||||||
| 23 | and may change with little to no warning. | ||||||
| 24 | Please file a bug/feature request demanding a more stable interface | ||||||
| 25 | if you plan to rely on it. | ||||||
| 26 | |||||||
| 27 | B |
||||||
| 28 | |||||||
| 29 | =cut | ||||||
| 30 | |||||||
| 31 | 95 | 95 | 618 | use Scalar::Util qw(blessed); | |||
| 95 | 229 | ||||||
| 95 | 4719 | ||||||
| 32 | 95 | 95 | 638 | use Carp; | |||
| 95 | 315 | ||||||
| 95 | 5761 | ||||||
| 33 | 95 | 95 | 2020 | use overload '""' => "as_string"; | |||
| 95 | 1198 | ||||||
| 95 | 742 | ||||||
| 34 | |||||||
| 35 | 95 | 95 | 8465 | use MVC::Neaf::Util qw(bare_html_escape); | |||
| 95 | 282 | ||||||
| 95 | 73929 | ||||||
| 36 | |||||||
| 37 | =head1 METHODS | ||||||
| 38 | |||||||
| 39 | =head2 new( $@ || 500, %options ) | ||||||
| 40 | |||||||
| 41 | =head2 new( %options ) | ||||||
| 42 | |||||||
| 43 | Returns a new exception object. | ||||||
| 44 | |||||||
| 45 | %options may include any keys as well as some Neaf-like control keys: | ||||||
| 46 | |||||||
| 47 | =over | ||||||
| 48 | |||||||
| 49 | =item * -status - alias for first argument. | ||||||
| 50 | If starts with 3 digits, will result in a "http error page" exception, | ||||||
| 51 | otherwise is reset to 500 and reason is updated. | ||||||
| 52 | |||||||
| 53 | =item * -reason - details about what happened | ||||||
| 54 | |||||||
| 55 | =item * -headers - array or hash of headers, just like that of a normal reply. | ||||||
| 56 | |||||||
| 57 | =item * -location - indicates a redirection | ||||||
| 58 | |||||||
| 59 | =item * -sudden - this was not an expected error (die 404 or redirect) | ||||||
| 60 | This will automatically turn on if -status cannot be parsed. | ||||||
| 61 | |||||||
| 62 | =item * -file - where error happened | ||||||
| 63 | |||||||
| 64 | =item * -line - where error happened | ||||||
| 65 | |||||||
| 66 | =item * -nocaller - don't try to determine error origin via caller | ||||||
| 67 | |||||||
| 68 | =back | ||||||
| 69 | |||||||
| 70 | =cut | ||||||
| 71 | |||||||
| 72 | sub new { | ||||||
| 73 | 56 | 56 | 1 | 2293 | my $class = shift; | ||
| 74 | 56 | 100 | 243 | if (@_ % 2) { | |||
| 75 | 6 | 11 | my $err = shift; | ||||
| 76 | 6 | 19 | push @_, -status => $err; | ||||
| 77 | }; | ||||||
| 78 | 56 | 239 | my %opt = @_; | ||||
| 79 | |||||||
| 80 | # TODO 0.30 bad rex will catch garbage if under 'C:\Program files' | ||||||
| 81 | 56 | 50 | 100 | 1113 | ($opt{-status} || '') | ||
| 82 | =~ qr{^(?:(\d\d\d)\s*)?(.*?)(?:\s+at (\S+) line (\d+)\.?)?$}s | ||||||
| 83 | or die "NEAF: Bug: Regex failed unexpectedly for q{$opt{-status}}"; | ||||||
| 84 | |||||||
| 85 | 56 | 100 | 371 | $opt{-status} = $1 || 500; | |||
| 86 | 56 | 100 | 605 | $opt{-reason} ||= $2 || $1 || 'unknown error'; | |||
| 66 | |||||||
| 87 | 56 | 100 | 407 | $opt{-sudden} ||= !$1; | |||
| 88 | 56 | 100 | 765 | my @caller = $opt{-nocaller} ? () : (caller(0)); | |||
| 89 | 56 | 100 | 681 | $opt{-file} ||= $3 || $caller[1]; | |||
| 66 | |||||||
| 90 | 56 | 100 | 522 | $opt{-line} ||= $4 || $caller[2]; | |||
| 66 | |||||||
| 91 | |||||||
| 92 | 56 | 329 | return bless \%opt, $class; | ||||
| 93 | }; | ||||||
| 94 | |||||||
| 95 | =head2 status() | ||||||
| 96 | |||||||
| 97 | Return error code. | ||||||
| 98 | |||||||
| 99 | =cut | ||||||
| 100 | |||||||
| 101 | sub status { | ||||||
| 102 | 66 | 66 | 1 | 212 | my $self = shift; | ||
| 103 | 66 | 454 | return $self->{-status}; | ||||
| 104 | }; | ||||||
| 105 | |||||||
| 106 | =head2 is_sudden() | ||||||
| 107 | |||||||
| 108 | Tells whether error was unexpected. | ||||||
| 109 | |||||||
| 110 | B |
||||||
| 111 | |||||||
| 112 | =cut | ||||||
| 113 | |||||||
| 114 | sub is_sudden { | ||||||
| 115 | 92 | 92 | 1 | 2008 | my $self = shift; | ||
| 116 | 92 | 100 | 1362 | return $self->{-sudden} ? 1 : 0; | |||
| 117 | }; | ||||||
| 118 | |||||||
| 119 | =head2 as_string() | ||||||
| 120 | |||||||
| 121 | Stringify. | ||||||
| 122 | |||||||
| 123 | Result will start with C |
||||||
| 124 | C |
||||||
| 125 | |||||||
| 126 | Otherwise it would look similar to the original -status. | ||||||
| 127 | |||||||
| 128 | =cut | ||||||
| 129 | |||||||
| 130 | sub as_string { | ||||||
| 131 | 9 | 9 | 1 | 3284 | my $self = shift; | ||
| 132 | |||||||
| 133 | return ($self->{-sudden} ? '' : "MVC::Neaf: ") | ||||||
| 134 | 9 | 100 | 57 | .($self->{-location} ? "See $self->{-location}: " : '') | |||
| 100 | |||||||
| 135 | . $self->reason; | ||||||
| 136 | }; | ||||||
| 137 | |||||||
| 138 | =head2 make_reply( $request ) | ||||||
| 139 | |||||||
| 140 | Returns a refault error HTML page. | ||||||
| 141 | |||||||
| 142 | The default page is guaranteen to contain | ||||||
| 143 | the status as its one and only C<< >> element, | ||||||
| 144 | the unique request-id as one and only C<< >> element, | ||||||
| 145 | and the location (if any) as its one and only C<< >> element. | ||||||
| 146 | |||||||
| 147 | This page used to be a JSON but it turned out hard to debug | ||||||
| 148 | when dealing with javascript. | ||||||
| 149 | |||||||
| 150 | =cut | ||||||
| 151 | |||||||
| 152 | sub make_reply { | ||||||
| 153 | 43 | 43 | 1 | 157 | my ($self, $req) = @_; | ||
| 154 | |||||||
| 155 | 43 | 221 | my $code = $self->{-status}; | ||||
| 156 | 43 | 93 | my $redirect = ''; | ||||
| 157 | 43 | 267 | my $request_id = $req->id; | ||||
| 158 | 43 | 100 | 581 | my @headers = @{ $self->{-headers} || [] }; | |||
| 43 | 307 | ||||||
| 159 | 43 | 100 | 251 | if (my $where = $self->{-location}) { | |||
| 160 | 3 | 10 | unshift @headers, Location => $where; | ||||
| 161 | 3 | 9 | $where = bare_html_escape( $where ); | ||||
| 162 | 3 | 12 | $redirect = qq{ See $where }; |
||||
| 163 | }; | ||||||
| 164 | |||||||
| 165 | # An in-place template to avoid rendering | ||||||
| 166 | # don't worry, be stupid! | ||||||
| 167 | 43 | 268 | my $content = qq{ | ||||
| 168 | |||||||
| 169 | |
||||||
| 170 | |||||||
| 171 | |||||||
| 172 | Error $code |
||||||
| 173 | Request-id:$request_id |
||||||
| 174 | $redirect | ||||||
| 175 | |
||||||
| 176 | Powered by Not even a framework. | ||||||
| 177 | |||||||
| 178 | |||||||
| 179 | }; | ||||||
| 180 | |||||||
| 181 | return { | ||||||
| 182 | -status => $self->{-status}, | ||||||
| 183 | 43 | 543 | -content => $content, | ||||
| 184 | -type => 'text/html; charset=utf8', | ||||||
| 185 | -headers => \@headers, | ||||||
| 186 | }; | ||||||
| 187 | }; | ||||||
| 188 | |||||||
| 189 | =head2 reason() | ||||||
| 190 | |||||||
| 191 | Returns error message that was expected to cause the error. | ||||||
| 192 | |||||||
| 193 | =cut | ||||||
| 194 | |||||||
| 195 | sub reason { | ||||||
| 196 | 21 | 21 | 1 | 143 | my $self = shift; | ||
| 197 | |||||||
| 198 | 21 | 50 | 109 | return ($self->{-reason} || "Unknown error") . $self->file_and_line; | |||
| 199 | }; | ||||||
| 200 | |||||||
| 201 | =head2 file_and_line | ||||||
| 202 | |||||||
| 203 | Return " at /foo/bar line 42" suffix, if both file and line are available. | ||||||
| 204 | Empty string otherwise. | ||||||
| 205 | |||||||
| 206 | =cut | ||||||
| 207 | |||||||
| 208 | sub file_and_line { | ||||||
| 209 | 23 | 23 | 1 | 121 | my $self = shift; | ||
| 210 | return ($self->{-file} && $self->{-line}) | ||||||
| 211 | 23 | 100 | 66 | 346 | ? " at $self->{-file} line $self->{-line}" | ||
| 212 | : '' | ||||||
| 213 | }; | ||||||
| 214 | |||||||
| 215 | =head2 TO_JSON() | ||||||
| 216 | |||||||
| 217 | Converts exception to JSON, so that it doesn't frighten View::JS. | ||||||
| 218 | |||||||
| 219 | =cut | ||||||
| 220 | |||||||
| 221 | sub TO_JSON { | ||||||
| 222 | 1 | 1 | 1 | 3 | my $self = shift; | ||
| 223 | 1 | 19 | return { %$self }; | ||||
| 224 | }; | ||||||
| 225 | |||||||
| 226 | =head1 LICENSE AND COPYRIGHT | ||||||
| 227 | |||||||
| 228 | This module is part of L |
||||||
| 229 | |||||||
| 230 | Copyright 2016-2023 Konstantin S. Uvarin C |
||||||
| 231 | |||||||
| 232 | This program is free software; you can redistribute it and/or modify it | ||||||
| 233 | under the terms of either: the GNU General Public License as published | ||||||
| 234 | by the Free Software Foundation; or the Artistic License. | ||||||
| 235 | |||||||
| 236 | See L |
||||||
| 237 | |||||||
| 238 | =cut | ||||||
| 239 | |||||||
| 240 | 1; |