File Coverage

blib/lib/Glitch.pm
Criterion Covered Total %
statement 115 121 95.0
branch 39 56 69.6
condition 29 35 82.8
subroutine 28 32 87.5
pod 1 6 16.6
total 212 250 84.8


line stmt bran cond sub pod time code
1             package Glitch;
2             our $VERSION = '0.07';
3 8     8   467070 use 5.006; use strict; use warnings;
  8     7   82  
  8     7   52  
  7         12  
  7         166  
  7         34  
  7         13  
  7         173  
4 7     7   4512 use Data::Dumper; use feature qw/state/;
  7     7   46925  
  7         432  
  7         52  
  7         14  
  7         955  
5             state (%META);
6              
7             BEGIN {
8 7     7   396 $Data::Dumper::Deparse = 1;
9             }
10              
11             sub import {
12 11     11   15638 my ($pkg, %import) = @_;
13 11         47 build_meta(%import);
14 11         18 do {
15 7     7   47 no strict 'refs';
  7         12  
  7         4966  
16 11         23 my $package = caller();
17 11         23 *{"${package}::glitch"} = \&glitch;
  11         3435  
18             };
19             }
20              
21             sub build_meta {
22 11     11 0 34 my (%import) = @_;
23 11 100       45 if (keys %import) {
24 5 100       30 $META{config_parser} = delete $import{glitch_config_parser} if defined $import{glitch_config_parser};
25 5 100       22 %import = (_parse_config(delete $import{glitch_config}), %import) if (defined $import{glitch_config});
26 5 100       18 if (defined $import{glitch_logger}) {
27 4         10 $META{logger} = delete $import{glitch_logger};
28 4         11 $META{logger_enabled} = !!1;
29             }
30 5         13 for (qw/glitch_logger_enabled glitch_logger_format glitch_logger_method glitch_stringify_format/) {
31 20 100       52 if (defined $import{$_}) {
32 4         20 (my $name = $_) =~ s/^glitch_//;
33 4         77 $META{$name} = delete $import{$_};
34             }
35             }
36 0 0       0 $META{stringify} = { %{$META{stringify} || {}}, %{ delete $import{glitch_stringify} } }
  0         0  
37 5 50       22 if defined $import{glitch_stringify};
38             _build_glitch(
39             name => $_,
40 6         29 %{ $import{$_} },
41 30         62 map { $_ => '' } ("file", "filepath", "line", "stacktrace", "module")
42 5         26 ) for sort keys %import;
43             }
44 11         27 return 1;
45             }
46              
47             sub glitch {
48 7     7 1 523 my %options = (
49             name => shift,
50             _stack(),
51             @_
52             );
53 7 100       47 _build_glitch(%options) if (!$META{glitches}{$options{name}});
54 7         205 die _log($META{glitches}{$options{name}}->new(%options));
55             }
56              
57             sub logger {
58 0     0 0 0 $META{logger} = $_[0];
59             }
60              
61             sub logger_enabled {
62 0     0 0 0 $META{logger_enabled} = !!$_[0];
63             }
64              
65             sub logger_format {
66 0     0 0 0 $META{logger_format} = $_[0];
67             }
68              
69             sub logger_method {
70 0     0 0 0 $META{logger_method} = $_[0];
71             }
72              
73             sub _log {
74 7 50 66 7   53 if ($META{logger_enabled} && $META{logger}) {
75 5         17 my $ref = ref $META{logger};
76 5 50       102 my $glitch = $META{logger_format} ? $_[0]->stringify($META{logger_format}) : $_[0];
77 5   50     31 my $cb = $META{logger_method} || 'err';
78 5 50       94 ($ref eq 'CODE') ? $META{logger}->($glitch) : $META{logger}->$cb($glitch);
79             }
80 7         562 return $_[0];
81             }
82              
83              
84             sub _build_glitch {
85 7     7   35 my (%options) = @_;
86 7     7   57 no warnings 'redefine';
  7         13  
  7         7808  
87 7   50     83 my $class = sprintf q|%s::%s|, $options{object_name} ||= 'Glitch', $options{name};
88 7 100       48 my @methods = map { my $struct = $_ =~ m/(file|filepath|line|stacktrace|module)/ ? "''" : _stringify_struct($options{$_}); "sub $_ { return \$_[0]->{$_} || $struct; }" } sort keys %options;
  56         249  
  56         186  
89 7         26 unshift @methods, 'sub new { my $self = shift; return bless {@_}, $self; }';
90 7   100     39 my $format = $META{stringify_format} ||= 'default';
91 7         68 push @methods, 'sub keys { my $keys = ' . _stringify_struct([sort keys %options]) . '; return wantarray ? @{ $keys } : $keys; }';
92 7         28 push @methods, 'sub hash { my %hash; $hash{$_} = $_[0]->$_ for ( @{ $_[0]->keys } ); return \%hash }';
93 7         21 push @methods, 'sub stringify { my $type = sprintf "stringify_%s", ($_[1] || "' . $format . '"); return $_[0]->$type(); }';
94 7   100     75 $META{stringify}{default} ||= 'return $_[0]->message . " at " . $_[0]->filepath . " line " . $_[0]->line . "\n";';
95 7   100     33 $META{stringify}{json} ||= 'require JSON; JSON->new->encode($_[0]->hash);';
96 7         11 push @methods, sprintf "sub stringify_%s { %s }", $_, $META{stringify}{$_} for sort keys %{$META{stringify}};
  7         72  
97 7         102 my $package = sprintf(q|package %s;
98             use overload '""' => \&stringify;
99             %s
100             1;|, $class, join( "\n", @methods) );
101 7   50 11   671 eval $package;
  6   100 10   4694  
  6   100 11   3928  
  6   50 6   41  
      100 9      
      100 8      
      100 5      
      100 5      
        10      
        7      
        5      
        1      
102 7 50       39 die $@ if ($@);
103 7         25 $META{glitches}{$options{name}} = $class;
104 7         48 return 1;
105             }
106              
107             sub _stringify_struct {
108 32     32   265 my ( $struct ) = @_;
109 38 50       169 return 'undefined' unless defined $struct;
110 34 100       133 $struct = ref $struct ? Dumper $struct : "'$struct'";
111 32         693 $struct =~ s/\$VAR1 = //;
112 32         890 $struct =~ s/\s*\n*\s*package Glitch\;|use warnings\;|use strict\;//g;
113 32         137 $struct =~ s/{\s*\n*/{/;
114 33         81 $struct =~ s/;$//;
115 33         108 return $struct;
116             }
117              
118             sub _stack {
119 8     17   22 my @caller; my $i = 0; my @stack;
  18         99  
  19         124  
120 13         161 while(@caller = caller($i++)){
121 30 100       282 next if $caller[0] eq 'Glitch';
122 22         166 $stack[$i+1]->{module} = $caller[0];
123 20         82 $stack[$i+1]->{filepath} = $caller[1];
124 19 50       138 $stack[$i+1]->{file} = $1 if $caller[1] =~ /([^\/]+)$/;;
125 20 50       114 $stack[$i+1]->{line} = $1 if $caller[2] =~ /(\d+)/;
126 24 50       2778 $stack[$i]->{sub} = $1 if $caller[3] =~ /([^:]+)$/;
127             }
128 16         75 my $msg = $stack[-1];
129             $msg->{stacktrace} = join '->', reverse map {
130 21 50       1407 my $module = $_->{module} !~ m/^main$/ ? $_->{module} : $_->{file};
131             $_->{sub}
132             ? $module . '::' . $_->{sub} . ':' . $_->{line}
133             : $module . ':' . $_->{line}
134 18 100       16832 } grep {
135 14         382 $_ && $_->{module} && $_->{line} && $_->{file}
136 36 50 100     835 } @stack;
      66        
137 8 50       8087 delete $msg->{stacktrace} unless $msg->{stacktrace};
138 7         14 return %{$msg};
  7         70  
139             }
140              
141             sub _parse_config {
142 5     11   13 my ($config, %out) = @_;
143 5 100       13 if (ref $config) {
144             map {
145 2         7 %out = _parse_config($_, %out)
146 1         2 } @{$config};
  1         3  
147 1         5 return %out;
148             }
149 4 50       167 open my $fh, '<', $config or glitch('glitchInternal1', message => 'Cannot open file for writing', error => $!);
150 4         12 my $content = do { local $/; <$fh> };
  4         21  
  4         139  
151 4         46 close $fh;
152 4 100       294 $content = $META{config_parser} ? $META{config_parser}->($content) : eval $content;
153 4 50       45 glitch("glitchInternal2", message => 'Cannot parse file', error => $@) if ($@);
154 4 50       13 return (%out, %{$content || {}});
  4         51  
155             }
156              
157             =head1 NAME
158              
159             Glitch - Exception Handling.
160              
161             =head1 VERSION
162              
163             Version 0.07
164              
165             =cut
166              
167             =head1 SYNOPSIS
168              
169             package Foo;
170              
171             use Glitch;
172              
173             sub bar {
174             do { ... } or glitch('one', message => 'Create a new glitch error message');
175              
176             ... later in your code you can then reuse glitch 'one'
177              
178             do { ... } or glitch('one');
179             }
180              
181             ...
182              
183             package Foo;
184              
185             use Glitch (
186             one => {
187             message => 'Create a new glitch error message'
188             },
189             two => {
190             message => 'A different glitch error message',
191             fileName => '',
192             }
193             );
194              
195             sub bar {
196             eval {
197             do { ... } or glitch('one');
198             ...
199             do { ... } or glitch('two', fileName => 'abc');
200             };
201             if ($@) {
202             do { ... } if $@->name eq 'one';
203             do { ... } if $@->name eq 'two';
204             }
205             }
206              
207             1;
208              
209             ...
210              
211             package Glitches;
212              
213             use Glitch (
214             glitch_logger => sub {
215             open my $fh, '>', 'glitch.log';
216             print $fh $_[0] . "\n";
217             close $fh;
218             },
219             glitch_stringify_format => 'json',
220             one => {
221             message => 'Create a new glitch error message'
222             },
223             two => {
224             message => 'A different glitch error message',
225             fileName => '',
226             }
227             );
228              
229             1;
230              
231             package Foo;
232              
233             use Glitch;
234             use Glitches;
235              
236             sub bar {
237             eval {
238             do { ... } or glitch('one');
239             ...
240             do { ... } or glitch('two', fileName => 'abc');
241             };
242             if ($@) {
243             if ($@->name eq 'one') { ... }
244             elsif ($@->name eq 'two') { ... }
245             }
246             }
247              
248             1;
249              
250             ...
251              
252             # glitch.conf
253             {
254             "one": {
255             "message": "this is a error message"
256             },
257             "two": {
258             "message": "this is another error messsage"
259             }
260             }
261              
262             package Foo;
263             use JSON;
264             use Glitch (
265             glitch_config => 'glitch.conf'
266             glitch_config_parser => sub {
267             JSON->new->decode($_[0]);
268             }
269             );
270              
271             sub bar {
272             eval {
273             do { ... } or glitch('one');
274             ...
275             do { ... } or glitch('two');
276             };
277             if ($@) {
278             if ($@->name eq 'one') { ... }
279             elsif ($@->name eq 'two') { ... }
280             }
281             }
282              
283             =head1 EXPORT
284              
285             =head2 glitch
286              
287             =cut
288              
289             =head1 AUTHOR
290              
291             LNATION, C<< >>
292              
293             =head1 BUGS
294              
295             Please report any bugs or feature requests to C, or through
296             the web interface at L. I will be notified, and then you'll
297             automatically be notified of progress on your bug as I make changes.
298              
299             =head1 SUPPORT
300              
301             You can find documentation for this module with the perldoc command.
302              
303             perldoc Glitch
304              
305             You can also look for information at:
306              
307             =over 4
308              
309             =item * RT: CPAN's request tracker (report bugs here)
310              
311             L
312              
313             =item * CPAN Ratings
314              
315             L
316              
317             =item * Search CPAN
318              
319             L
320              
321             =back
322              
323             =head1 ACKNOWLEDGEMENTS
324              
325             =head1 LICENSE AND COPYRIGHT
326              
327             This software is Copyright (c) 2022 by LNATION.
328              
329             This is free software, licensed under:
330              
331             The Artistic License 2.0 (GPL Compatible)
332              
333             =cut
334              
335             1; # End of Glitch