File Coverage

blib/lib/Reflex/Trait/EmitsOnChange.pm
Criterion Covered Total %
statement 19 19 100.0
branch 1 2 50.0
condition n/a
subroutine 6 6 100.0
pod 0 1 0.0
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Reflex::Trait::EmitsOnChange;
2             $Reflex::Trait::EmitsOnChange::VERSION = '0.100';
3             # vim: ts=2 sw=2 noexpandtab
4              
5 8     8   6656 use Moose::Role;
  8         13  
  8         69  
6 8     8   30533 use Scalar::Util qw(weaken);
  8         13  
  8         536  
7              
8 8     8   37 use Moose::Exporter;
  8         10  
  8         38  
9             Moose::Exporter->setup_import_methods( with_caller => [ qw( emits ) ]);
10              
11 8     8   3646 use Reflex::Event::ValueChange;
  8         22  
  8         3168  
12              
13             has setup => (
14             isa => 'CodeRef|HashRef',
15             is => 'ro',
16             );
17              
18             has trigger => (
19             is => 'ro',
20             default => sub {
21             my $meta_self = shift;
22              
23             # $meta_self->name() is not set yet.
24             # Weaken $meta_self so that the closure isn't permanent.
25              
26             my $event;
27             my $old_value;
28              
29             sub {
30             my ($self, $new_value) = @_;
31              
32             # Edge-detection. Only emit when a value has changed.
33             # TODO - Make this logic optional. Sometimes an application
34             # needs level logic rather than edge logic.
35              
36             #return if (
37             # (!defined($value) and !defined($last_value))
38             # or
39             # (defined($value) and defined($last_value) and $value eq $last_value)
40             #);
41              
42             $self->emit(
43             -type => 'Reflex::Event::ValueChange',
44             -name => (
45             $event ||=
46             $self->meta->find_attribute_by_name($meta_self->name())->event()
47             ),
48             old_value => $old_value,
49             new_value => $new_value,
50             );
51              
52             $old_value = $new_value;
53             weaken $old_value if defined($old_value) and ref($old_value);
54             }
55             }
56             );
57              
58             has initializer => (
59             is => 'ro',
60             default => sub {
61             my $role;
62             return sub {
63             my ($self, $value, $callback, $attr) = @_;
64             my $event;
65             $self->emit(
66             -name => (
67             $event ||=
68             $self->meta->find_attribute_by_name($attr->name())->event()
69             ),
70             value => $value,
71             );
72              
73             $callback->($value);
74             }
75             },
76             );
77              
78             has event => (
79             isa => 'Str',
80             is => 'ro',
81             lazy => 1,
82             default => sub {
83             my $self = shift;
84             return $self->name();
85             },
86             );
87              
88             ### EmitsOnChanged declarative syntax.
89              
90             sub emits {
91 2     2 0 2208 my ($caller, $name, %etc) = @_;
92 2         6 my $meta = Class::MOP::class_of($caller);
93 2         10 push @{$etc{traits}}, __PACKAGE__;
  2         7  
94 2 50       10 $etc{is} = 'rw' unless exists $etc{is};
95 2         10 $meta->add_attribute($name, %etc);
96             }
97              
98             package Moose::Meta::Attribute::Custom::Trait::Reflex::Trait::EmitsOnChange;
99             $Moose::Meta::Attribute::Custom::Trait::Reflex::Trait::EmitsOnChange::VERSION = '0.100';
100 2     2   612 sub register_implementation { 'Reflex::Trait::EmitsOnChange' }
101              
102             1;
103              
104             __END__
105              
106             =pod
107              
108             =encoding UTF-8
109              
110             =for :stopwords Rocco Caputo
111              
112             =head1 NAME
113              
114             Reflex::Trait::EmitsOnChange - Emit an event when an attribute's value changes.
115              
116             =head1 VERSION
117              
118             This document describes version 0.100, released on April 02, 2017.
119              
120             =head1 SYNOPSIS
121              
122             # Not a complete program. See examples eg-09-emitter-trait.pl
123             # and eg-10-setup.pl for working examples.
124              
125             package Counter;
126             use Moose;
127             extends 'Reflex::Base';
128             use Reflex::Trait::EmitsOnChange;
129              
130             emits count => (
131             isa => 'Int',
132             default => 0,
133             );
134              
135             An equivalent alternative:
136              
137             has count => (
138             traits => ['Reflex::Trait::EmitsOnChange'],
139             isa => 'Int',
140             is => 'rw',
141             default => 0,
142             );
143              
144             =head1 DESCRIPTION
145              
146             An attribute with the Reflex::Trait::EmitsOnChange trait emit an event
147             on behalf of its object whenever its value changes. The event will be
148             named after the attribute by default. It will be accompanied by a
149             "value" parameter, the value of which is the attribute's new value at
150             the time of the change.
151              
152             In the SYNOPSIS example, changes to count() cause its Counter object
153             to emit "count" events.
154              
155             =head2 event
156              
157             The "default" option can be used to override the default event emitted
158             by the Reflex::Trait::EmitsOnChange trait. That default, by the way,
159             is the name of the attribute.
160              
161             =head2 setup
162              
163             The "setup" option provides default constructor parameters for the
164             attribute. In the above example, clock() will by default contain
165              
166             Reflex::Interval->new(interval => 1, auto_repeat => 1);
167              
168             In other words, it will emit the Reflex::Interval event ("tick") once
169             per second until destroyed.
170              
171             =for Pod::Coverage emits
172              
173             =head1 Declarative Syntax
174              
175             Reflex::Trait::EmitsOnChange exports a declarative emits() function,
176             which acts almost identically to Moose's has() but with a couple
177             convenient defaults: The EmitsOnChange trait is added, and the
178             attribute is "rw" to allow changes.
179              
180             =head1 CAVEATS
181              
182             The "setup" option is a work-around for unfortunate default timing.
183             It will be deprecated if default can be made to work instead.
184              
185             =head1 SEE ALSO
186              
187             Please see those modules/websites for more information related to this module.
188              
189             =over 4
190              
191             =item *
192              
193             L<Reflex|Reflex>
194              
195             =item *
196              
197             L<Reflex>
198              
199             =item *
200              
201             L<Reflex::Trait::Watches>
202              
203             =item *
204              
205             L<Reflex/ACKNOWLEDGEMENTS>
206              
207             =item *
208              
209             L<Reflex/ASSISTANCE>
210              
211             =item *
212              
213             L<Reflex/AUTHORS>
214              
215             =item *
216              
217             L<Reflex/BUGS>
218              
219             =item *
220              
221             L<Reflex/BUGS>
222              
223             =item *
224              
225             L<Reflex/CONTRIBUTORS>
226              
227             =item *
228              
229             L<Reflex/COPYRIGHT>
230              
231             =item *
232              
233             L<Reflex/LICENSE>
234              
235             =item *
236              
237             L<Reflex/TODO>
238              
239             =back
240              
241             =head1 BUGS AND LIMITATIONS
242              
243             You can make new bug reports, and view existing ones, through the
244             web interface at L<http://rt.cpan.org/Public/Dist/Display.html?Name=Reflex>.
245              
246             =head1 AUTHOR
247              
248             Rocco Caputo <rcaputo@cpan.org>
249              
250             =head1 COPYRIGHT AND LICENSE
251              
252             This software is copyright (c) 2017 by Rocco Caputo.
253              
254             This is free software; you can redistribute it and/or modify it under
255             the same terms as the Perl 5 programming language system itself.
256              
257             =head1 AVAILABILITY
258              
259             The latest version of this module is available from the Comprehensive Perl
260             Archive Network (CPAN). Visit L<http://www.perl.com/CPAN/> to find a CPAN
261             site near you, or see L<https://metacpan.org/module/Reflex/>.
262              
263             =head1 DISCLAIMER OF WARRANTY
264              
265             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
266             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT
267             WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
268             PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND,
269             EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
270             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
271             PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
272             SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME
273             THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
274              
275             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
276             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
277             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE
278             TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR
279             CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
280             SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
281             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
282             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
283             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
284             DAMAGES.
285              
286             =cut