File Coverage

blib/lib/JIP/Mock/Control.pm
Criterion Covered Total %
statement 152 160 95.0
branch 34 36 94.4
condition 5 7 71.4
subroutine 34 36 94.4
pod 6 6 100.0
total 231 245 94.2


line stmt bran cond sub pod time code
1             package JIP::Mock::Control;
2              
3 2     2   70129 use strict;
  2         20  
  2         54  
4 2     2   9 use warnings;
  2         4  
  2         52  
5              
6 2     2   10 use Carp qw(croak);
  2         4  
  2         83  
7 2     2   10 use English qw(-no_match_vars);
  2         2  
  2         10  
8 2     2   652 use Scalar::Util qw(reftype blessed);
  2         9  
  2         112  
9              
10 2     2   824 use JIP::Mock::Event;
  2         5  
  2         2336  
11              
12             our $VERSION = 'v0.0.2';
13              
14             sub new {
15 26     26 1 44928 my ( $class, %param ) = @ARG;
16              
17 26 100       71 if ( my $error = $class->_validate(%param) ) {
18 4         471 croak 'Cannot instantiate: ' . $error;
19             }
20              
21 22         66 return $class->_instantiate(%param);
22             }
23              
24             sub package { ## no critic (Subroutines::ProhibitBuiltinHomonyms)
25 61     61 1 823 my ($self) = @ARG;
26              
27 61         199 return $self->{package};
28             }
29              
30             sub want_array {
31 33     33 1 52 my ($self) = @ARG;
32              
33 33         87 return $self->{want_array};
34             }
35              
36             sub times { ## no critic (Subroutines::ProhibitBuiltinHomonyms)
37 25     25 1 636 my ($self) = @ARG;
38              
39 25         61 return $self->{times};
40             }
41              
42             sub events {
43 10     10 1 1215 my ($self) = @ARG;
44              
45 10         15 my @events = map { $self->_extract_event_state($_) } @{ $self->_events() };
  10         21  
  10         18  
46              
47 10         101 return \@events;
48             }
49              
50             sub override {
51 19     19 1 125 my ( $self, %pair ) = @ARG;
52              
53 19         29 my @pairs;
54 19         52 foreach my $name ( sort keys %pair ) {
55 18         33 my $new_sub = $pair{$name};
56              
57 18 100       36 if ( my $error = $self->_validate_overriding( $name, $new_sub ) ) {
58 5         357 croak 'Cannot override: ' . $error;
59             }
60              
61 13         37 push @pairs, [ $name, $new_sub ];
62             }
63              
64 14         24 foreach my $pair (@pairs) {
65 13         20 my ( $name, $new_sub ) = @{$pair};
  13         23  
66              
67 13         25 $self->_override( $name, $new_sub );
68             }
69              
70 14         47 return;
71             } ## end sub override
72              
73             sub DESTROY {
74 9     9   5809 my ($self) = @ARG;
75              
76 9         27 $self->_restore_all();
77              
78 9         32 return;
79             }
80              
81             sub _validate {
82 26     26   60 my ( undef, %param ) = @ARG;
83              
84 26         45 my $package = $param{package};
85              
86 26 100       71 return 'package name is not present!' if !length $package;
87              
88 23 100       51 return if _is_package_loaded($package);
89              
90 1         6 return sprintf 'package "%s" is not loaded!', $package;
91             }
92              
93             sub _validate_overriding {
94 18     18   35 my ( $self, $name, $new_sub ) = @ARG;
95              
96 18 100       38 return 'name is not present!' if !length $name;
97              
98 17 100       31 if ( !$self->package->can($name) ) {
99 1         10 return sprintf 'cannot override non-existent sub "%s"!', $name;
100             }
101              
102 16 100       41 if ( !$new_sub ) {
103 2         12 return sprintf 'new sub of "%s" is not present!', $name;
104             }
105              
106 14 100       33 return if _is_coderef($new_sub);
107              
108 1         8 return sprintf 'new sub of "%s" is not CODE reference!', $name;
109             }
110              
111             sub _instantiate {
112 22     22   53 my ( $class, %param ) = @ARG;
113              
114             return bless(
115             {
116             package => $param{package},
117             want_array => $param{want_array},
118 22         175 originals => {},
119             times => {},
120             events => [],
121             },
122             $class,
123             );
124             }
125              
126             sub _events {
127 31     31   49 my ($self) = @ARG;
128              
129 31         66 return $self->{events};
130             }
131              
132             sub _originals {
133 35     35   52 my ($self) = @ARG;
134              
135 35         56 return $self->{originals};
136             }
137              
138             sub _get_original {
139 13     13   23 my ( $self, $name ) = @ARG;
140              
141 13         23 my $originals = $self->_originals();
142              
143 13         23 my $original_sub = $originals->{$name};
144              
145 13         29 return $original_sub;
146             }
147              
148             sub _add_original {
149 13     13   27 my ( $self, $name, $original_sub ) = @ARG;
150              
151 13         18 my $originals = $self->_originals();
152              
153 13 50       29 return if exists $originals->{$name};
154              
155 13         25 $originals->{$name} = $original_sub;
156              
157 13         19 return;
158             }
159              
160             sub _delete_original {
161 0     0   0 my ( $self, $name ) = @ARG;
162              
163 0         0 my $originals = $self->_originals();
164              
165 0         0 return delete $originals->{$name};
166             }
167              
168             sub _override {
169 13     13   27 my ( $self, $name, $new_sub ) = @_;
170              
171 13         29 $self->_collect_original($name);
172              
173 13         26 my $new_sub_wrapper = $self->_init_wrapper( $name, $new_sub );
174              
175 13         39 $self->_monkey_patch( $name, $new_sub_wrapper );
176              
177 13         27 return;
178             }
179              
180             sub _collect_original {
181 13     13   25 my ( $self, $name ) = @ARG;
182              
183 13 50       23 return if $self->_get_original($name);
184              
185 13         22 my $original_sub = $self->package->can($name);
186              
187 13         35 $self->_add_original( $name, $original_sub );
188              
189 13         19 return;
190             }
191              
192             sub _init_wrapper {
193 13     13   24 my ( $self, $name, $new_sub ) = @ARG;
194              
195             return sub {
196 21     21   2484 my @arguments = @ARG;
197 21         36 my $want_array = wantarray;
198              
199 21 100       77 if ( my $first_argument = $arguments[0] ) {
200 16         30 my $package = $self->package();
201              
202             #<<< no perltidy
203 16   66     136 my $is_class_or_object = (
204             ( $first_argument eq $package )
205             || ( ( blessed($first_argument) // q{} ) eq $package )
206             );
207             #>>>
208              
209 16 100       38 if ($is_class_or_object) {
210 14         26 shift @arguments;
211             }
212             }
213              
214 21         51 my %event = (
215             method => $name,
216             arguments => \@arguments,
217             times => $self->_increment_times($name),
218             );
219              
220 21 100       43 if ( $self->want_array() ) {
221 4         8 $event{want_array} = $want_array;
222             }
223              
224 21         86 my $event = JIP::Mock::Event->new(%event);
225              
226 21         57 $self->_collect_event($event);
227              
228             # void context
229 21 100       55 if ( !defined $want_array ) {
    100          
230 9         28 $new_sub->($event);
231              
232 9         3081 return;
233             }
234              
235             # is looking for a list value
236             elsif ($want_array) {
237 7         14 my @results = $new_sub->($event);
238              
239 7         727 return @results;
240             }
241              
242             # is looking for a scalar
243             else {
244 5         11 my $result = $new_sub->($event);
245              
246 5         1720 return $result;
247             }
248 13         74 };
249             } ## end sub _init_wrapper
250              
251             sub _monkey_patch {
252 13     13   23 my ( $self, $name, $sub ) = @ARG;
253              
254 13         21 my $target = $self->package() . q{::} . $name;
255              
256 2     2   16 no strict 'refs'; ## no critic (ProhibitNoStrict)
  2         4  
  2         76  
257 2     2   11 no warnings 'redefine'; ## no critic (TestingAndDebugging::ProhibitNoWarnings)
  2         4  
  2         850  
258              
259 13         24 *{$target} = $sub;
  13         43  
260              
261 13         24 return;
262             }
263              
264             sub _restore_all {
265 9     9   14 my ($self) = @ARG;
266              
267 9         19 my $originals = $self->_originals();
268              
269 9         16 foreach my $name ( sort keys %{$originals} ) {
  9         31  
270 0         0 $self->_restore($name);
271             }
272              
273 9         14 return;
274             }
275              
276             sub _restore {
277 0     0   0 my ( $self, $name ) = @ARG;
278              
279 0         0 my $original_sub = $self->_delete_original($name);
280              
281 0         0 $self->_monkey_patch( $name, $original_sub );
282              
283 0         0 return;
284             }
285              
286             sub _increment_times {
287 21     21   38 my ( $self, $name ) = @ARG;
288              
289 21         38 my $times = $self->times();
290              
291 21         35 my $count = $times->{$name};
292              
293 21   100     67 $count //= 0;
294              
295 21         31 $count += 1;
296              
297 21         31 $times->{$name} = $count;
298              
299 21         65 return $count;
300             }
301              
302             sub _collect_event {
303 21     21   36 my ( $self, $event ) = @ARG;
304              
305 21         26 push @{ $self->_events() }, $event;
  21         35  
306              
307 21         30 return;
308             }
309              
310             sub _extract_event_state {
311 10     10   20 my ( $self, $event ) = @ARG;
312              
313 10         23 my %state = (
314             method => $event->method(),
315             arguments => $event->arguments(),
316             );
317              
318 10 100       21 if ( $self->want_array() ) {
319 4         12 $state{want_array} = $event->want_array();
320             }
321              
322 10         34 return \%state;
323             }
324              
325             sub _is_coderef {
326 14     14   28 my ($sub) = @ARG;
327              
328 14         33 my $reftype = reftype($sub);
329              
330 14   50     30 $reftype //= q{};
331              
332 14 100       57 return 1 if $reftype eq 'CODE';
333 1         4 return 0;
334             }
335              
336             sub _is_package_loaded {
337 23     23   39 my ($package) = @ARG;
338              
339 23         39 $package .= q{::};
340              
341 2     2   14 no strict 'refs'; ## no critic (ProhibitNoStrict)
  2         4  
  2         128  
342              
343 23 100       32 return 1 if %{$package};
  23         131  
344 1         3 return 0;
345             }
346              
347             1;
348              
349             __END__