File Coverage

blib/lib/Glitch.pm
Criterion Covered Total %
statement 109 115 94.7
branch 39 56 69.6
condition 29 35 82.8
subroutine 26 30 86.6
pod 1 5 20.0
total 204 241 84.6


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