File Coverage

blib/lib/Math/ModInt/Event/Trap.pm
Criterion Covered Total %
statement 59 59 100.0
branch 16 16 100.0
condition 3 3 100.0
subroutine 16 16 100.0
pod 2 2 100.0
total 96 96 100.0


line stmt bran cond sub pod time code
1             # Copyright (c) 2010-2019 Martin Becker, Blaubeuren.
2             # This package is free software; you can distribute it and/or modify it
3             # under the terms of the Artistic License 2.0 (see LICENSE file).
4              
5             package Math::ModInt::Event::Trap;
6              
7 11     11   198 use 5.006;
  11         31  
8 11     11   49 use strict;
  11         18  
  11         228  
9 11     11   43 use warnings;
  11         18  
  11         301  
10 11     11   66 use Carp qw(carp croak);
  11         28  
  11         848  
11              
12             # ----- class data -----
13              
14             BEGIN {
15 11     11   45 our @CARP_NOT = qw(Math::ModInt::Event);
16 11         305 our $VERSION = '0.012';
17             }
18              
19             # Math::ModInt::Event::Trap=ARRAY(...)
20              
21             # ........... index ........... # ............ value ............
22 11     11   79 use constant F_TRAP_ID => 0; # instance ID, used as a weak ref
  11         26  
  11         1014  
23 11     11   61 use constant NFIELDS => 1;
  11         19  
  11         569  
24              
25             # ARRAY, element of @handlers:
26              
27             # ........... index ........... # ............ value ............
28 11     11   57 use constant H_TRAP_ID => 0; # instance ID of trap (0 if static)
  11         18  
  11         559  
29 11     11   67 use constant H_EVENT => 1; # event to trap
  11         18  
  11         479  
30 11     11   56 use constant H_HANDLER => 2; # the handler coderef
  11         24  
  11         6155  
31              
32             my $unique = 0;
33             my @handlers = ();
34              
35             my %generic_handlers = (
36             'ignore' => sub { 0 },
37             'warn' =>
38             sub {
39             my ($event, @details) = @_;
40             carp join q[: ], 'warning', $event->description, @details;
41             return 0;
42             },
43             'die' =>
44             sub {
45             my ($event, @details) = @_;
46             croak join q[: ], 'error', $event->description, @details;
47             },
48             );
49              
50             # ----- private subroutines -----
51              
52             sub _discard_handler {
53 12     12   20 my ($trap_id) = @_;
54 12         19 my $hx = @handlers;
55 12         27 while ($hx) {
56 17 100       63 if ($trap_id == $handlers[--$hx]->[H_TRAP_ID]) {
57 12         22 splice @handlers, $hx, 1;
58 12         165 return;
59             }
60             }
61             }
62              
63             sub _add_handler {
64 13     13   25 my ($trap_id, $event, $handler) = @_;
65 13         29 push @handlers, [$trap_id, $event, $handler];
66             }
67              
68             sub _final_trap {
69 36     36   61 my ($event, @details) = @_;
70 36         80 croak join q[: ], $event->description, @details;
71             }
72              
73             # ----- public methods -----
74              
75             sub new {
76 15     15 1 24 my ($class, $event, $handler) = @_;
77 15         26 my $is_static = !defined wantarray;
78 15 100       27 my $trap_id = $is_static? 0: ++$unique;
79 15 100       31 if (!ref $handler) {
80 5 100 100     22 if (!$handler || !exists $generic_handlers{$handler}) {
81 2         13 $event->UsageError->raise(
82             'bad argument: generic trap type or coderef expected'
83             );
84             }
85 3         7 $handler = $generic_handlers{$handler};
86             }
87 13         27 _add_handler($trap_id, $event, $handler);
88 13 100       26 return if $is_static;
89 12         34 return bless [$trap_id], $class;
90             }
91              
92             sub DESTROY {
93 12     12   744 my ($this) = @_;
94 12         29 _discard_handler($this->[F_TRAP_ID]);
95             }
96              
97             sub broadcast {
98 114     114 1 198 my ($class, $event, @details) = @_;
99 114         151 my $called = 0;
100 114         197 foreach my $handler (reverse @handlers) {
101 17 100       99 if ($event->isa(ref $handler->[H_EVENT])) {
102 13         19 ++$called;
103 13 100       37 last if !$handler->[H_HANDLER]->($event, @details);
104             }
105             }
106 110 100       240 if (!$event->is_recoverable) {
107 36         75 _final_trap($event, @details);
108             }
109 74         126 return $called;
110             }
111              
112             1;
113              
114             __END__