File Coverage

blib/lib/MooseX/Observer/Role/Observable.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package MooseX::Observer::Role::Observable;
2             {
3             $MooseX::Observer::Role::Observable::VERSION = '0.010';
4             }
5             # ABSTRACT: Adds methods an logic to a class, enabling instances changes to be observed
6              
7 1     1   1283 use MooseX::Role::Parameterized;
  0            
  0            
8             use Moose::Util::TypeConstraints;
9             use List::MoreUtils ();
10            
11             {
12             my $observerrole_type = role_type('MooseX::Observer::Role::Observer');
13             subtype 'ArrayRefOfObservers'
14             => as 'ArrayRef'
15             => where { List::MoreUtils::all { $observerrole_type->check($_) } @$_ },
16             => message { "The Object given must do the 'MooseX::Role::Observer' role." };
17             }
18            
19             parameter notify_after => (isa => 'ArrayRef', default => sub { [] });
20              
21             role {
22             my $parameters = shift;
23             my $notifications_after = $parameters->notify_after;
24              
25             my %args = @_;
26             my $consumer = $args{consumer};
27            
28             has observers => (
29             traits => ['Array'],
30             is => 'bare',
31             isa => 'ArrayRefOfObservers',
32             default => sub { [] },
33             writer => '_observers',
34             handles => {
35             add_observer => 'push',
36             count_observers => 'count',
37             all_observers => 'elements',
38             remove_all_observers => 'clear',
39             _filter_observers => 'grep',
40             },
41             );
42              
43             for my $methodname (@{ $notifications_after }) {
44             if ( $consumer->isa('Class::MOP::Class') ) {
45             if ($consumer->find_attribute_by_name($methodname)) {
46            
47             after $methodname => sub {
48             my $self = shift;
49             $self->_notify(\@_, $methodname) if (@_);
50             };
51            
52             } else {
53              
54             after $methodname => sub {
55             my $self = shift;
56             $self->_notify(\@_, $methodname);
57             };
58            
59             }
60             }
61             elsif ( $consumer->isa('Moose::Meta::Role') ) {
62             $consumer->add_after_method_modifier(
63             $methodname,
64             sub {
65             my $self = shift;
66             $self->_notify( \@_, $methodname );
67             }
68             );
69             }
70             }
71            
72             sub _notify {
73             my ($self, $args, $eventname) = @_;
74             $_->update($self, $args, $eventname) for ( $self->all_observers );
75             }
76            
77             sub remove_observer {
78             my ($self, $observer) = @_;
79             my @filtered = $self->_filter_observers( sub { $_ ne $observer } );
80             $self->_observers(\@filtered);
81             }
82             };
83            
84             1;
85              
86              
87             __END__
88             =pod
89              
90             =head1 NAME
91              
92             MooseX::Observer::Role::Observable - Adds methods an logic to a class, enabling instances changes to be observed
93              
94             =head1 VERSION
95              
96             version 0.010
97              
98             =head1 SYNOPSIS
99              
100             ############################################################################
101             package Counter;
102              
103             use Moose;
104              
105             has count => (
106             traits => ['Counter'],
107             is => 'rw',
108             isa => 'Int',
109             default => 0,
110             handles => {
111             inc_counter => 'inc',
112             dec_counter => 'dec',
113             },
114             );
115              
116             # apply the observable-role and
117             # provide methodnames, after which the observers are notified of changes
118             with 'MooseX::Observer::Role::Observable' => { notify_after => [qw~
119             count
120             inc_counter
121             dec_counter
122             reset_counter
123             ~] };
124              
125             sub reset_counter { shift->count(0) }
126              
127             sub _utility_method { ... }
128              
129             ############################################################################
130             package Display;
131              
132             use Moose;
133              
134             # apply the oberserver-role, tagging the class as observer and ...
135             with 'MooseX::Observer::Role::Observer';
136              
137             # ... require an update-method to be implemented
138             # this is called after the observed subject calls an observed method
139             sub update {
140             my ( $self, $subject, $args, $eventname ) = @_;
141             print $subject->count;
142             }
143              
144             ############################################################################
145             package main;
146              
147             my $counter = Counter->new();
148             # add an observer of type "Display" to our observable counter
149             $counter->add_observer( Display->new() );
150              
151             # increments the counter to 1, afterwards its observers are notified of changes
152             # Display is notified of a change, its update-method is called
153             $counter->inc_counter; # Display prints 1
154             $counter->dec_counter; # Display prints 0
155              
156             =head1 DESCRIPTION
157              
158             This is a parameterized role, that is applied to your observed class. Usually
159             when applying this role, you provide a list of methodnames. After method
160             modifiers are installed for these methods. They call the _notify-method, which
161             in turn calls the update-method of all observers.
162              
163             =head1 METHODS
164              
165             =head2 add_observer($observer)
166              
167             Adds an observer to the object. This Observer must do the
168             MooseX::Observer::Role::Observer role.
169              
170             =head2 count_observers
171              
172             Returns how many observers are attached to the object.
173              
174             =head2 all_observers
175              
176             Returns a list of all observers attached to the object.
177              
178             =head2 remove_observer($observer)
179              
180             Remove the given observer from the object.
181              
182             =head2 remove_all_observers
183              
184             Removes all observers from the object.
185              
186             =head2 _notify($args, $eventname)
187              
188             This private method notifies all observers, passing $self, $args and an
189             $eventname to the observers' update method.
190              
191             =head1 INSTALLATION
192              
193             See perlmodinstall for information and options on installing Perl modules.
194              
195             =head1 SEE ALSO
196              
197             Please see those modules/websites for more information related to this module.
198              
199             =over 4
200              
201             =item *
202              
203             L<MooseX::Observer|MooseX::Observer>
204              
205             =back
206              
207             =head1 AUTHOR
208              
209             Thomas Müller <tmueller@cpan.org>
210              
211             =head1 COPYRIGHT AND LICENSE
212              
213             This software is copyright (c) 2011 by Thomas Müller.
214              
215             This is free software; you can redistribute it and/or modify it under
216             the same terms as the Perl 5 programming language system itself.
217              
218             =cut
219