File Coverage

blib/lib/YATT/Lite/Partial/ErrorReporter.pm
Criterion Covered Total %
statement 42 68 61.7
branch 17 32 53.1
condition 13 27 48.1
subroutine 9 13 69.2
pod 0 7 0.0
total 81 147 55.1


line stmt bran cond sub pod time code
1             package YATT::Lite::Partial::ErrorReporter; sub MY () {__PACKAGE__}
2             # -*- coding: utf-8 -*-
3 14     14   762 use strict;
  14         29  
  14         424  
4 14     14   83 use warnings qw(FATAL all NONFATAL misc);
  14         26  
  14         635  
5             use YATT::Lite::Partial
6 14         82 (fields => [qw/cf_at_done
7             cf_error_handler
8             cf_die_in_error
9             cf_ext_pattern
10 14     14   7396 /]);
  14         33  
11             require Devel::StackTrace;
12              
13 14     14   8504 use YATT::Lite::Error;
  14         35  
  14         683  
14 14     14   78 use YATT::Lite::Util qw/incr_opt/;
  14         28  
  14         13780  
15              
16              
17             #========================================
18             # error reporting.
19             #========================================
20              
21             sub error {
22 31 50   31 0 585 (my MY $self) = map {ref $_ ? $_ : MY} shift;
  31         115  
23 31         123 $self->raise(error => incr_opt(depth => \@_), @_);
24             }
25              
26             sub error_with_status {
27 0 0   0 0 0 (my MY $self) = map {ref $_ ? $_ : MY} shift;
  0         0  
28 0         0 my ($code) = shift;
29 0         0 my $opts = incr_opt(depth => \@_);
30 0         0 $opts->{http_status_code} = $code;
31 0         0 $self->raise(error => $opts, @_);
32             }
33              
34             sub make_error {
35 31     31 0 68 my ($self, $depth, $opts) = splice @_, 0, 3;
36 31         83 my ($fmt, @args) = @_;
37 31         141 my ($pkg, $file, $line) = caller($depth);
38 31         62 my $bt = do {
39 31         85 my @bt_opts = (ignore_package => [__PACKAGE__]);
40 31 50       95 if (my $frm = delete $opts->{ignore_frame}) {
41             # $YATT::Lite::CON->logdump(ignore_frame => $frm);
42             push @bt_opts, frame_filter => sub {
43 0     0   0 my ($hash) = @_;
44 0         0 my $caller = $hash->{'caller'};
45 0   0     0 my $all_match = grep {($frm->[$_] // '') eq ($caller->[$_] // '')}
  0   0     0  
46             1, 2; # __FILE__, __LINE__
47             # print STDERR YATT::Lite::Util::terse_dump("filter: ", $all_match, $frm, $caller), "\n";
48 0         0 $all_match != 2;
49             }
50 0         0 }
51 31         194 Devel::StackTrace->new(@bt_opts);
52             };
53              
54 31   33     21075 my $pattern = $self->{cf_ext_pattern} // qr/\.(yatt|ytmpl|ydo)$/;
55              
56 31         48 my @tmplinfo;
57 31         123 foreach my $fr ($bt->frames) {
58 443 50       23665 my $fn = $fr->filename
59             or next;
60 443 50       3235 $fn =~ $pattern
61             or next;
62 0         0 push @tmplinfo, tmpl_file => $fn, tmpl_line => $fr->line;
63 0         0 last;
64             }
65              
66             $self->Error->new
67 31 100 100     514 (file => $opts->{file} // $file, line => $opts->{line} // $line
    50 100        
68             , @tmplinfo
69             , (@args
70             ? (format => $fmt, args => \@args)
71             : (reason => $fmt))
72             , backtrace => $bt
73             , $opts ? %$opts : ());
74             }
75              
76             # $yatt->raise($errType => ?{opts}?, $errFmt, @fmtArgs)
77              
78             sub raise {
79 31     31 0 76 (my MY $self, my $type) = splice @_, 0, 2;
80 31 50 33     225 my $opts = shift if @_ and ref $_[0] eq 'HASH';
81             # shift/splice しないのは、引数を stack trace に残したいから
82 31   50     112 my $depth = (delete($opts->{depth}) // 0);
83 31         118 my Error $err = $self->make_error(2 + $depth, $opts, @_); # 2==raise+make_error
84 31 100 66     197 if (ref $self and my $sub = deref($self->{cf_error_handler})) {
    50 33        
    50          
    50          
85             # $con を引数で引きずり回すのは大変なので、むしろ外から closure を渡そう、と。
86             # $SIG{__DIE__} を使わないのはなぜかって? それはユーザに開放しておきたいのよん。
87 2 50       7 unless (ref $sub eq 'CODE') {
88 0         0 die "error_handler is not a CODE ref: $sub";
89             }
90 2         6 $sub->($type, $err);
91             } elsif ($sub = $self->can('error_handler')) {
92 0         0 $sub->($self, $type, $err);
93             } elsif (not ref $self or $self->{cf_die_in_error}) {
94 0         0 die $err->message;
95             } elsif ($err->{cf_http_status_code}) {
96             # If http_status_code is specified explicitly (from error_with_status),
97             # raise it immediately, with simple reason. (not full backtrace message).
98             $self->raise_psgi_html($err->{cf_http_status_code}
99 0         0 , $err->reason);
100             } else {
101             # 即座に die しないモードは、デバッガから error 呼び出し箇所に step して戻れるようにするため。
102             # ... でも、受け側を do {my $err = $con->error; die $err} にでもしなきゃダメかも?
103 29         1062 return $err;
104             }
105             }
106              
107             # XXX: 将来、拡張されるかも。
108             sub DONE {
109 0     0 0 0 my MY $self = shift;
110 0 0       0 if (my $sub = $self->{cf_at_done}) {
111 0         0 $sub->(@_);
112             } else {
113 0         0 die \ 'DONE';
114             }
115             }
116              
117             sub raise_psgi_html {
118 0     0 0 0 (my MY $self, my ($status, $html, @rest)) = @_;
119 0         0 die [$status, ["Content-type" => "text/html; charset=utf-8", @rest]
120             , [$html]];
121             }
122              
123             sub deref {
124 31 100   31 0 400 return undef unless defined $_[0];
125 2 50 33     15 if (ref $_[0] eq 'REF' or ref $_[0] eq 'SCALAR') {
126 0         0 ${$_[0]};
  0         0  
127             } else {
128 2         9 $_[0];
129             }
130             }
131              
132             1;