File Coverage

blib/lib/Exception/Chain.pm
Criterion Covered Total %
statement 108 109 99.0
branch 28 30 93.3
condition 3 3 100.0
subroutine 22 22 100.0
pod 5 9 55.5
total 166 173 95.9


line stmt bran cond sub pod time code
1             package Exception::Chain;
2 3     3   120497 use 5.008005;
  3         12  
  3         132  
3 3     3   18 use strict;
  3         6  
  3         94  
4 3     3   27 use warnings;
  3         5  
  3         182  
5             use overload
6 3 100   3   15 '""' => sub { $_[0]->to_string || $_[0] };
  3     10   5  
  3         50  
  10         344  
7              
8             use Class::Accessor::Lite (
9 3         22 ro => [qw/ delivery is_delivery_duplicated duplicated_trace /],
10 3     3   3195 );
  3         3661  
11 3     3   3994 use Time::Piece qw(localtime);
  3         41797  
  3         18  
12 3     3   3243 use Time::HiRes qw(gettimeofday tv_interval);
  3         5536  
  3         17  
13 3     3   4135 use Data::Dumper;
  3         22057  
  3         207  
14 3     3   2853 use Data::Util qw(is_instance);
  3         2880  
  3         3601  
15              
16             our $VERSION = "0.10";
17             our $SkipDepth = 0;
18              
19             # class method
20             sub new {
21 21     21 0 41 my ($class, %args) = @_;
22              
23 21         161 my $self = bless {
24             tags => {},
25             stack => [],
26             message => undef,
27             delivery => undef,
28             is_delivery_duplicated => 0,
29             duplicated_trace => [],
30             }, $class;
31             }
32              
33             sub _get_external_caller {
34 29     29   35 my $class = shift;
35 29         39 my $i = 0;
36 29         91 while (my @caller = caller(++$i)) {
37 59 100       3018 unless ($caller[0] =~ /^Exception::Chain/) {
38 29         44 my $level = $i + $SkipDepth;
39 29         71 @caller = caller($level);
40 29         1650 return @caller;
41             }
42             }
43             }
44              
45             sub _build_arg {
46 30     30   69 my ($class, @info) = @_;
47              
48 30 50       95 if (scalar @info == 0) {
    100          
49 0         0 return { };
50             }
51             elsif (scalar @info == 1) {
52 4 100       16 if ($class->_is_my_instance($info[0])) {
53 1         4 return { error => $info[0] };
54             }
55             else {
56 3         10 return { message => $class->dumper($info[0]) };
57             }
58             }
59             else {
60 26         72 my %data = @info;
61 26         40 my $ret = {};
62              
63 26         78 $ret->{message} = $class->dumper($data{message});
64 26         249 for my $name (qw/tag error delivery/) {
65 78         211 $ret->{$name} = $data{$name};
66             }
67              
68 26         91 return $ret;
69             }
70             }
71              
72             sub _is_my_instance {
73 10     10   21 my ($class, $instance) = @_;
74 10         78 is_instance($instance, 'Exception::Chain');
75             }
76              
77             sub dumper {
78 29     29 0 47 my ($self, $value) = @_;
79 29 100 100     142 if ( defined $value && ref($value) ) {
80 2         4 local $Data::Dumper::Terse = 1;
81 2         4 local $Data::Dumper::Indent = 0;
82 2         3 local $Data::Dumper::Sortkeys = 1;
83 2         10 return Data::Dumper::Dumper($value);
84             }
85 27         81 return $value;
86             }
87              
88             # instance method
89              
90             sub throw {
91 26     26 1 19315 my ($class, @args) = @_;
92 26         79 my $builded_args = $class->_build_arg(@args);
93 26         34 my $self;
94 26 100       82 if (not defined $builded_args->{error}) {
    100          
95 20         55 $self = $class->new;
96 20         133 $self->{message} = $builded_args->{message};
97             }
98             elsif ($class->_is_my_instance($builded_args->{error})) {
99 5         14 $self = delete $builded_args->{error};
100             }
101             else {
102 1         3 $self = $class->new;
103 1         2 push @{$self->{stack}}, $builded_args->{error};
  1         3  
104 1         2 $self->rethrow(%{$builded_args});
  1         5  
105             }
106 25         59 $self->logging($builded_args);
107 25         175 die $self;
108             }
109              
110             sub rethrow {
111 3     3 1 14 my ($self, @args) = @_;
112 3         9 $self->logging($self->_build_arg(@args));
113 3         21 die $self;
114             }
115              
116             sub to_string {
117 31     31 1 4139 my $self = shift;
118 31         39 my $string = join( ' ', @{$self->{stack}} );
  31         91  
119 31         72 $string =~ s/\n//g;
120 31         184 return $string;
121             }
122              
123             sub match {
124 11     11 1 1278 my ($self, @tags) = @_;
125 11         21 return scalar grep { defined $self->{tags}{$_} } @tags;
  13         107  
126             }
127              
128             sub first_message {
129 4     4 1 200 my $self = shift;
130 4         20 return $self->{message};
131             }
132              
133             sub add_message {
134 1     1 0 6 my ($self, $message) = @_;
135 1         3 $self->logging($self->_build_arg($message));
136             }
137              
138             sub logging {
139 29     29 0 46 my ($self, $args) = @_;
140              
141 29         59 my ($pkg, $file, $line) = $self->_get_external_caller;
142              
143 29 100       77 if (%{$args}) {
  29         76  
144 28 100       81 if (my $tags = $args->{tag}) {
145 10 100       33 $tags = [$tags] unless ref $tags;
146 10         19 for my $tag (@$tags) {
147 11         41 $self->{tags}{$tag} = 1;
148             }
149             }
150 28 100       82 if (my $message = $args->{message}) {
151 24         27 push @{$self->{stack}}, "$message at $file line $line.";
  24         98  
152             }
153 28 100       108 if (my $delivery = $args->{delivery}) {
154 5 100       26 if ($self->delivery) {
155 1 50       11 unless ($self->is_delivery_duplicated) {
156 1         9 $self->{is_delivery_duplicated} = 1;
157 1         2 push @{$self->{duplicated_trace}}, $self->{stack}->[0];
  1         4  
158             }
159 1         3 push @{$self->{duplicated_trace}}, "$file line $line." ;
  1         7  
160             }
161             else {
162 4         49 $self->{delivery} = $delivery;
163             }
164             }
165             }
166             else {
167 1         3 push @{$self->{stack}}, "at $file line $line.";
  1         6  
168             }
169             }
170              
171             1;
172             __END__