File Coverage

blib/lib/EntityModel/Collection.pm
Criterion Covered Total %
statement 62 71 87.3
branch 15 30 50.0
condition 2 2 100.0
subroutine 13 13 100.0
pod 7 7 100.0
total 99 123 80.4


line stmt bran cond sub pod time code
1             package EntityModel::Collection;
2             {
3             $EntityModel::Collection::VERSION = '0.102';
4             }
5             use EntityModel::Class {
6 2         23 pending => 'int',
7             event_handler => 'hash',
8 2     2   127919 };
  2         4  
9 2     2   1088 no if $] >= 5.017011, warnings => "experimental::smartmatch";
  2         3  
  2         16  
10              
11             =head1 NAME
12              
13             EntityModel::Collection - manage entity model definitions
14              
15             =head1 VERSION
16              
17             version 0.102
18              
19             =head1 SYNOPSIS
20              
21             =cut
22              
23             =head2 OPERATORS
24              
25             The coderef operator is overloaded by default, allowing syntax such as C< $self->(event => @data) >.
26              
27             =cut
28              
29             use overload
30             # Allow ->(event => @parameter) for laziness
31             '&{}' => sub {
32 8     8   6132 my $self = shift;
33             sub {
34 6     6   12 my $event = shift;
35 6         15 my @param = @_;
36 6         343 logDebug("[%s] %s", $event, join ', ', @param);
37 6 50       1789 if(my $handler = $self->{event_handler}->{$event}) {
38             try {
39             # Use a temporary so we don't end up in void context
40 6         8 my $rslt;
41 6         49 $rslt = $_->($self, @param) for @$handler;
42 6         77 } catch {
43 6         4006 my $failure = $_;
44 6         38 logError("Handler on %s for [%s] threw error %s", $self, $event, $failure);
45 6 50       2762 die "No failure handler available" unless $self->has_event_handlers_for('fail');
46             # Raise a failure event, but avoid loops
47 3         13 try { $self->(fail => $failure); }
48 6 100       336 catch { logError("Also failed in failure handler, this time [%s] original [%s]", $_, $failure) }
  0         0  
49             unless $event eq 'fail';
50             };
51             } else {
52 0         0 logWarning("No handler for [%s]", $event);
53             }
54             return defined wantarray
55 0 0       0 ? $self
56             : $self->commit;
57             }
58 8         69 },
59             # Could dump out more info on "" perhaps
60 2     2   774 fallback => 1;
  2         4  
  2         21  
61              
62             =head2 each
63              
64             Execute the given code for each item that matches the current chain.
65              
66             =cut
67              
68             sub each {
69 2     2 1 6 my $self = shift;
70 2         4 my $code = shift;
71 2         13 logDebug("Each");
72 2         1307 $self->add_handler(item => $code);
73             return defined wantarray
74 2 50       16 ? $self
75             : $self->commit
76             }
77              
78             =head2 done
79              
80             Supply a coderef which will be called on successful completion of the chain so far, guaranteed
81             to be after any items have been processed.
82              
83             =cut
84              
85             sub done {
86 2     2 1 6 my $self = shift;
87 2         4 my $code = shift;
88 2         9 logDebug("Done");
89 2         595 $self->add_handler(done => $code);
90             return defined wantarray
91 2 50       14 ? $self
92             : $self->commit
93             }
94              
95             =head2 fail
96              
97             Supply a coderef which will be called on error. Default behaviour is to die().
98              
99             =cut
100              
101             sub fail {
102 2     2 1 6 my $self = shift;
103 2         6 my $code = shift;
104 2         8 logDebug("Set handler for failed");
105 2         591 $self->add_handler(fail => $code);
106             return defined wantarray
107 2 50       15 ? $self
108             : $self->commit
109             }
110              
111             =head2 commit
112              
113             =cut
114              
115             sub commit {
116 1     1 1 11 my $self = shift;
117 1         2 my $code = shift;
118             # Protect against unnecessary calls
119 1 50       4 unless($self->{pending}) {
120 0         0 logDebug("Commit with nothing pending");
121 0 0       0 $code->() if $code;
122 0         0 return $self;
123             }
124              
125 1         3 logDebug("Commit");
126             # Apply anything we can - and make sure we're not in void context to avoid commit loops
127 1 50       231 if(my $apply = $self->can('apply')) {
128 0         0 my $x; $x = $apply->($self);
  0         0  
129             }
130 1         4 $self->pending(0);
131 1 50       11 logDebug("Has pending? %s", ($self->has_pending ? 'yes' : 'no'));
132              
133 1 50       226 $code->($self) if $code;
134 1         5 $self->('done' => $self);
135 0         0 return $self;
136             }
137              
138             =head2 add_handler
139              
140             =cut
141              
142             sub add_handler {
143 6     6 1 13 my $self = shift;
144 6         22 while(@_) {
145 6         19 my ($event, $code) = splice @_, 0, 2;
146 6         28 logDebug("Defining handler %s for event %s", "$code", $event);
147 6         1850 push @{$self->{event_handler}->{$event}}, $code;
  6         34  
148 6 100       80 ++$self->{pending} if $event ~~ [qw(item done)];
149             }
150 6         9 return $self;
151             }
152              
153             =head2 has_event_handlers_for
154              
155             Returns how many event handlers are defined for this event.
156              
157             =cut
158              
159             sub has_event_handlers_for {
160 6     6 1 14 my $self = shift;
161 6 50       40 my $event = shift or die "Invalid event passed";
162 6         10 return scalar @{$self->{event_handler}->{$event}};
  6         40  
163             }
164              
165             =head2 has_pending
166              
167             Returns true if there's anything pending, false otherwise.
168              
169             =cut
170              
171 2   100 2 1 8 sub has_pending { (shift->pending || 0) > 0 }
172              
173             =head2 DESTROY
174              
175             When we go out of scope, we want any pending actions to be applied immediately.
176              
177             =cut
178              
179             sub DESTROY {
180 1     1   21 my $self = shift;
181 1 50       4 $self->commit if $self->has_pending;
182             }
183              
184             1;