File Coverage

blib/lib/Reflex/Role/Interval.pm
Criterion Covered Total %
statement 19 27 70.3
branch 6 10 60.0
condition 2 9 22.2
subroutine 6 7 85.7
pod 0 1 0.0
total 33 54 61.1


line stmt bran cond sub pod time code
1             package Reflex::Role::Interval;
2             # vim: ts=2 sw=2 noexpandtab
3             $Reflex::Role::Interval::VERSION = '0.100';
4 6     6   6449 use Reflex::Role;
  6         22  
  6         20  
5 6     6   7872 use Reflex::Event::Interval;
  6         18  
  6         241  
6              
7 6     6   42 use Scalar::Util qw(weaken);
  6         8  
  6         3728  
8              
9             attribute_parameter att_auto_repeat => "auto_repeat";
10             attribute_parameter att_auto_start => "auto_start";
11             attribute_parameter att_interval => "interval";
12             callback_parameter cb_tick => qw( on att_interval tick );
13             method_parameter method_repeat => qw( repeat att_interval _ );
14             method_parameter method_start => qw( start att_interval _ );
15             method_parameter method_stop => qw( stop att_interval _ );
16              
17             role {
18             my $p = shift;
19              
20             my $att_auto_repeat = $p->att_auto_repeat();
21             my $att_auto_start = $p->att_auto_start();
22             my $att_interval = $p->att_interval();
23             my $cb_tick = $p->cb_tick();
24              
25             requires $att_interval, $cb_tick;
26              
27             has $att_auto_repeat => ( is => 'ro', isa => 'Bool', default => 1 );
28             has $att_auto_start => ( is => 'ro', isa => 'Bool', default => 1 );
29              
30             my $method_start = $p->method_start();
31             my $method_repeat = $p->method_repeat();
32             my $method_stop = $p->method_stop();
33              
34             my $timer_id_name = "${att_interval}_timer_id";
35              
36             has $timer_id_name => (
37             isa => 'Maybe[Str]',
38             is => 'rw',
39             );
40              
41             # Work around a Moose edge case.
42       6 0   sub BUILD {}
43              
44             after BUILD => sub {
45             my ($self, $args) = @_;
46             $self->$method_repeat() if $self->$att_auto_start();
47             };
48              
49             method $method_repeat => sub {
50 32     32   64 my ($self, $args) = @_;
51              
52             # Switch to the proper session.
53             return unless (
54 32 100 33     838 defined $self->$att_interval() and $self->call_gate($method_repeat)
55             );
56              
57             # Stop a previous alarm.
58 26 100       1039 $self->$method_stop() if defined $self->$timer_id_name();
59              
60             # Put a weak $self in an envelope that can be passed around
61             # without strenghtening the object.
62              
63 26         70 my $envelope = [ $self, $cb_tick, 'Reflex::Event::Interval' ];
64 26         98 weaken $envelope->[0];
65              
66 26         681 $self->$timer_id_name(
67             $POE::Kernel::poe_kernel->delay_set(
68             'timer_due',
69             $self->$att_interval(),
70             $envelope,
71             )
72             );
73             };
74              
75             method $method_start => sub {
76 0     0   0 my ($self, $args) = @_;
77              
78             # Nothing to start if the timer is already going.
79 0 0       0 if ($self->$timer_id_name()) {
80 0         0 carp($method_start . "() called on a running timer");
81 0         0 return;
82             }
83              
84             # Switch to the proper session.
85             return unless (
86 0 0 0     0 defined $self->$att_interval() and $self->call_gate($method_repeat)
87             );
88              
89             # Put a weak $self in an envelope that can be passed around
90             # without strenghtening the object.
91              
92 0         0 my $envelope = [ $self, $cb_tick, 'Reflex::Event::Interval' ];
93 0         0 weaken $envelope->[0];
94              
95 0         0 $self->$timer_id_name(
96             $POE::Kernel::poe_kernel->delay_set(
97             'timer_due',
98             $self->$att_interval(),
99             $envelope,
100             )
101             );
102             };
103              
104             after DEMOLISH => sub {
105             my ($self, $args) = @_;
106             $self->$method_stop();
107             };
108              
109             method $method_stop => sub {
110 27     27   48 my ($self, $args) = @_;
111              
112             # Return if it was a false "alarm" (pun intended).
113 27 100 33     672 return unless defined $self->$timer_id_name() and $self->call_gate("stop");
114              
115 26         844 $POE::Kernel::poe_kernel->alarm_remove($self->$timer_id_name());
116 26         1872 $self->$timer_id_name(undef);
117             };
118              
119             after $cb_tick => sub {
120             my ($self, $event) = @_;
121             $self->$method_repeat() if $self->$att_auto_repeat();
122             };
123             };
124              
125             1;
126              
127             __END__
128              
129             =pod
130              
131             =encoding UTF-8
132              
133             =for :stopwords Rocco Caputo
134              
135             =head1 NAME
136              
137             Reflex::Role::Interval - set a periodic, recurring timer
138              
139             =head1 VERSION
140              
141             This document describes version 0.100, released on April 02, 2017.
142              
143             =head1 SYNOPSIS
144              
145             package Reflex::Interval;
146              
147             use Moose;
148             extends 'Reflex::Base';
149              
150             has interval => ( isa => 'Num', is => 'ro' );
151             has auto_repeat => ( isa => 'Bool', is => 'ro', default => 1 );
152             has auto_start => ( isa => 'Bool', is => 'ro', default => 1 );
153              
154             with 'Reflex::Role::Interval' => {
155             interval => "interval",
156             auto_start => "auto_start",
157             auto_repeat => "auto_repeat",
158             cb_tick => "on_tick",
159             method_start => "start",
160             method_stop => "stop",
161             method_repeat => "repeat",
162             };
163              
164             1;
165              
166             =head1 DESCRIPTION
167              
168             Reflex::Role::Interval adds a periodic timer and callback to a class.
169             It's parameterized, so it can be consumed multiple times to add more
170             than one interval timer to the same class.
171              
172             In the SYNOPSIS, the Reflex::Interval class consumes a single
173             Reflex::Role::Interval. Reflex::Interval provides some data in the
174             form of interval(), auto_repeat() and auto_start(). The role provides
175             a callback to the on_tick() method, which is also provided by the
176             role. The role also provides some control methods, start(), stop()
177             and repeat().
178              
179             The general rules and conventions for Reflex paramaeterized roles are
180             covered in L<Reflex::Role>.
181              
182             =head2 Attribute Parameters
183              
184             Attribute parameters specify the names of attributes in the consumer
185             that control the role's behavior.
186              
187             =head3 interval
188              
189             C<interval> names an attribute in the consumer that must hold the
190             role's interval, in seconds. The role will trigger a callback
191             every interval() seconds, if the C<auto_repeat> attribute is true.
192              
193             C<interval> is a Reflex::Role "key" attribute. The interval
194             attribute's name is used in the default names for the role's internal
195             and public attributes, methods and callbacks.
196              
197             =head3 auto_repeat
198              
199             Interval timers will repeat automatically if the value of the
200             attribute named in C<auto_repeat> is true. Otherwise, repeat() must
201             be called to trigger the next interval callback, C<interval> seconds
202             after repeat() is called.
203              
204             =head3 auto_start
205              
206             Interval timers will automatically start if the value of the attribute
207             named in C<auto_start> is true. Otherwise, the class consuming this
208             role must call the role's start method, named in C<method_start>.
209              
210             =head2 Callback Parameters
211              
212             Callback parameters specify the names of methods in the consumer that
213             will be called when the role notifies the class of events.
214              
215             =head3 cb_tick
216              
217             C<cb_tick> sets the name of the tick callback method, which must be
218             implemented by this role's consumer. C<cb_tick> is optional, and will
219             default to the catenation of "on_", the name of the interval
220             attribute, and "_tick".
221              
222             Reflex::Role::Interval provides a default callback that will emit the
223             "tick" event and repeat the timer if C<<$self->$auto_repeat()>>
224             evaluates to true.
225              
226             =head2 Method Parameters
227              
228             Method parameters generally specify the names of methods the role will
229             provide to modify the role's behavior.
230              
231             =head3 method_repeat
232              
233             Reflex::Role::Interval provides a method to manually trigger
234             repetition of the interval timer. This method exists in case
235             C<auto_repeat> evaluates to false. The repeat method name may be
236             overridden by C<method_repeat>'s value. By default, the repeat method
237             will be "repeat_" prepended to the name of the interval attribute.
238              
239             =head3 method_start
240              
241             Reflex::Role::Interval provides a method to start the interval timer,
242             which is vital for cases when C<auto_start> evaluates to false. The
243             start method name may be overridden by C<method_start>'s value. By
244             default, the start method will be "start_" prepended to the name of
245             the interval attribute.
246              
247             =head3 method_stop
248              
249             Reflex::Role::Interval provides a method to stop the interval timer.
250             This method will be flattened into the consuming class, per Moose.
251             C<method_stop> allows the role's consumer to define the name of that
252             method. By default, the stop method's name will be "stop_" prepended
253             to the name of the interval attribute.
254              
255             =for Pod::Coverage BUILD
256              
257             =head1 EXAMPLES
258              
259             L<Reflex::Timeout> is one example of using Reflex::Role::Timeout.
260              
261             =head1 SEE ALSO
262              
263             Please see those modules/websites for more information related to this module.
264              
265             =over 4
266              
267             =item *
268              
269             L<Reflex|Reflex>
270              
271             =item *
272              
273             L<Reflex>
274              
275             =item *
276              
277             L<Reflex::Interval>
278              
279             =item *
280              
281             L<Reflex::Role>
282              
283             =item *
284              
285             L<Reflex/ACKNOWLEDGEMENTS>
286              
287             =item *
288              
289             L<Reflex/ASSISTANCE>
290              
291             =item *
292              
293             L<Reflex/AUTHORS>
294              
295             =item *
296              
297             L<Reflex/BUGS>
298              
299             =item *
300              
301             L<Reflex/BUGS>
302              
303             =item *
304              
305             L<Reflex/CONTRIBUTORS>
306              
307             =item *
308              
309             L<Reflex/COPYRIGHT>
310              
311             =item *
312              
313             L<Reflex/LICENSE>
314              
315             =item *
316              
317             L<Reflex/TODO>
318              
319             =back
320              
321             =head1 BUGS AND LIMITATIONS
322              
323             You can make new bug reports, and view existing ones, through the
324             web interface at L<http://rt.cpan.org/Public/Dist/Display.html?Name=Reflex>.
325              
326             =head1 AUTHOR
327              
328             Rocco Caputo <rcaputo@cpan.org>
329              
330             =head1 COPYRIGHT AND LICENSE
331              
332             This software is copyright (c) 2017 by Rocco Caputo.
333              
334             This is free software; you can redistribute it and/or modify it under
335             the same terms as the Perl 5 programming language system itself.
336              
337             =head1 AVAILABILITY
338              
339             The latest version of this module is available from the Comprehensive Perl
340             Archive Network (CPAN). Visit L<http://www.perl.com/CPAN/> to find a CPAN
341             site near you, or see L<https://metacpan.org/module/Reflex/>.
342              
343             =head1 DISCLAIMER OF WARRANTY
344              
345             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
346             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT
347             WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
348             PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND,
349             EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
350             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
351             PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
352             SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME
353             THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
354              
355             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
356             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
357             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE
358             TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR
359             CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
360             SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
361             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
362             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
363             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
364             DAMAGES.
365              
366             =cut