File Coverage

blib/lib/Reflex/Role/Reactive.pm
Criterion Covered Total %
statement 151 199 75.8
branch 28 64 43.7
condition 6 9 66.6
subroutine 34 42 80.9
pod 8 13 61.5
total 227 327 69.4


line stmt bran cond sub pod time code
1             package Reflex::Role::Reactive;
2             # vim: ts=2 sw=2 noexpandtab
3             $Reflex::Role::Reactive::VERSION = '0.100';
4 8     8   7951 use Moose::Role;
  8         24987  
  8         40  
5              
6 8     8   28691 use Scalar::Util qw(weaken blessed);
  8         11  
  8         464  
7 8     8   31 use Carp qw(carp croak);
  8         13  
  8         319  
8 8     8   3033 use Reflex;
  8         14  
  8         55  
9 8     8   401 use Reflex::Callback::Promise;
  8         11  
  8         85  
10 8     8   7489 use Reflex::Callback::CodeRef;
  8         13  
  8         460  
11              
12       8     END {
13             #warn join "; ", keys %watchers;
14             #warn join "; ", keys %watchings;
15             }
16              
17             our @CARP_NOT = (__PACKAGE__);
18              
19             # Singleton POE::Session.
20             # TODO - Extract the POE bits into another role if we want to support
21             # other event loops at the top level rather than beneath POE.
22              
23             # TODO - How to prevent these from being redefined?
24             # TODO - Such as if POE is loaded elsewhere first?
25             #
26             #sub POE::Kernel::ASSERT_DEFAULT () { 1 }
27             #sub POE::Kernel::CATCH_EXCEPTIONS () { 0 }
28             #sub POE::Kernel::USE_SIGCHLD () { 1 }
29              
30 8     8   3374 use POE;
  8         220106  
  8         50  
31 8     8   340559 use Reflex::POE::Session;
  8         25  
  8         17767  
32              
33             # Disable a warning.
34             POE::Kernel->run();
35              
36             my %session_object_count;
37              
38             my $singleton_session_id;
39              
40             sub _create_singleton_session {
41             $singleton_session_id = POE::Session->create(
42             inline_states => {
43             # Make the session conveniently accessible.
44             # Although we're using the $singleton_session_id, so why bother?
45              
46             _start => sub {
47             # No-op to satisfy assertions.
48 9     9   6185 $_[KERNEL]->alias_set("alias_" . $_[SESSION]->ID);
49 9         343 undef;
50             },
51             _stop => sub {
52             # Session has become defunct.
53 8     8   3466 $singleton_session_id = undef;
54 8         23 undef;
55             },
56              
57             ### Timer manipulators and callbacks.
58              
59             timer_due => sub {
60 20     20   2436366 my $envelope = $_[ARG0];
61 20         83 my ($cb_object, $cb_method, $event_class) = @$envelope;
62 20         1826 $cb_object->$cb_method(
63             $event_class->new(_emitters => [ $cb_object ])
64             );
65             },
66              
67             ### I/O manipulators and callbacks.
68              
69             select_ready => sub {
70 0     0   0 my ($handle, $envelope, $mode) = @_[ARG0, ARG2];
71 0         0 my ($cb_object, $cb_method, $event_class) = @$envelope;
72 0         0 $cb_object->$cb_method(
73             $event_class->new(
74             _emitters => [ $cb_object ],
75             handle => $handle,
76             )
77             );
78             },
79              
80             ### Signals.
81              
82             signal_happened => sub {
83 2     2   316 my $signal_class = pop @_;
84 2         26 $signal_class->deliver(@_[ARG0..$#_]);
85 2         12 $_[KERNEL]->sig_handled();
86             },
87              
88             ### Cross-session emit() is converted into these events.
89              
90             deliver_callback => sub {
91 10     10   736 my ($callback, $event, $args) = @_[ARG0, ARG1, ARG2];
92 10         30 $callback->deliver($event, $args);
93             },
94              
95             # call_gate() uses this to call methods in the right session.
96              
97             call_gate_method => sub {
98 11     11   630 my ($object, $method, @args) = @_[ARG0..$#_];
99 11         69 return $object->$method(@args);
100             },
101              
102             call_gate_coderef => sub {
103 0     0   0 my ($coderef, @args) = @_[ARG0..$#_];
104 0         0 return $coderef->(@args);
105             },
106              
107             # Catch dynamic events.
108              
109             _default => sub {
110 0     0   0 my ($event, $args) = @_[ARG0, ARG1];
111              
112 0 0       0 return $event->deliver($args) if (
113             "$event" =~ /^Reflex::POE::Event(?:::|=)/
114             );
115              
116 0 0       0 return if Reflex::POE::Session->deliver($_[SENDER]->ID, $event, $args);
117              
118             # Unhandled event.
119             # TODO - Should anything special be done in this case?
120             },
121              
122             ### Support POE::Wheel classes.
123              
124             # Deliver to wheels based on the wheel ID. Different wheels pass
125             # their IDs in different ARGn offsets, so we need a few of these.
126             wheel_event_0 => sub {
127 0     0   0 $_[CALLER_FILE] =~ m{/([^/.]+)\.pm};
128 0         0 "Reflex::POE::Wheel:\:$1"->deliver(0, @_[ARG0..$#_]);
129             },
130             wheel_event_1 => sub {
131 11     11   1130 $_[CALLER_FILE] =~ m{/([^/.]+)\.pm};
132 11         73 "Reflex::POE::Wheel:\:$1"->deliver(1, @_[ARG0..$#_]);
133             },
134             wheel_event_2 => sub {
135 0     0   0 $_[CALLER_FILE] =~ m{/([^/.]+)\.pm};
136 0         0 "Reflex::POE::Wheel:\:$1"->deliver(2, @_[ARG0..$#_]);
137             },
138             wheel_event_3 => sub {
139 4     4   1727 $_[CALLER_FILE] =~ m{/([^/.]+)\.pm};
140 4         30 "Reflex::POE::Wheel:\:$1"->deliver(3, @_[ARG0..$#_]);
141             },
142             wheel_event_4 => sub {
143 2     2   88 $_[CALLER_FILE] =~ m{/([^/.]+)\.pm};
144 2         18 "Reflex::POE::Wheel:\:$1"->deliver(4, @_[ARG0..$#_]);
145             },
146             },
147 9     9   484 )->ID();
148             }
149              
150             sub session_id {
151 205 100   205 1 425 _create_singleton_session() unless defined $singleton_session_id;
152 205         2167 $singleton_session_id;
153             }
154              
155             # What's watching me.
156             # watchers()->{$watcher->get_id} = \@callbacks
157             has watchers => (
158             isa => 'HashRef',
159             is => 'rw',
160             lazy => 1,
161             default => sub { {} },
162             );
163              
164             # What's watching me.
165             # watchers_by_event()->{$event}->{$watcher->get_id} = \@callbacks
166             has watchers_by_event => (
167             isa => 'HashRef',
168             is => 'rw',
169             lazy => 1,
170             default => sub { {} },
171             );
172              
173             # What I'm watching.
174             # watched_objects()->{$watched->get_id}->{$event} = \@interests
175             has watched_object_events => (
176             isa => 'HashRef',
177             is => 'rw',
178             lazy => 1,
179             default => sub { {} },
180             );
181              
182             has watched_objects => (
183             isa => 'HashRef',
184             is => 'rw',
185             lazy => 1,
186             default => sub { {} },
187             );
188              
189             # TODO - Needs to be class, not object based!
190             #has role => (
191             # is => 'ro',
192             # isa => 'Str',
193             # default => sub {
194             # my $self = shift;
195             # my $role = ref($self);
196             # $role =~ s/^Reflex:://;
197             # $role =~ tr[a-zA-Z0-9][_]cs;
198             # return lc $role;
199             # },
200             #);
201              
202             has promise => (
203             is => 'rw',
204             isa => 'Maybe[Reflex::Callback::Promise]',
205             default => undef,
206             );
207              
208             has emits_seen => (
209             is => 'rw',
210             isa => 'HashRef[Str]',
211             default => sub { {} },
212             );
213              
214             my $next_id = 1;
215              
216             has _id => (
217             isa => 'Int',
218             is => 'ro',
219             default => sub { $next_id++ },
220             );
221              
222 134     134 0 2956 sub get_id { return shift()->_id() }
223              
224             # Base class.
225              
226       31 0   sub BUILD {}
227             after BUILD => sub {
228             my ($self, $args) = @_;
229              
230             # Set up all emitters and watchers.
231              
232             foreach my $setup (
233             grep {
234             $_->does('Reflex::Trait::EmitsOnChange') || $_->does('Reflex::Trait::Watched')
235             }
236             $self->meta()->get_all_attributes()
237             ) {
238             my $callback = $setup->setup();
239             next unless defined $callback;
240              
241             # TODO - Better way to detect CodeRef?
242             if (ref($callback) eq "CODE") {
243             my $member = $setup->name();
244             $self->$member( $callback->($self) ); # TODO - Proper parameters!
245             next;
246             }
247              
248             # TODO - Better way to detect HashRef?
249             if (ref($callback) eq "HASH") {
250             my $member = $setup->name();
251              
252             my @types = (
253             grep { $_ ne "Undef" }
254             split /\s*\|\s*/,
255             $setup->type_constraint()
256             );
257              
258             croak "Hashref 'setup' can't determine the class from 'isa'" if (
259             @types < 1
260             );
261              
262             croak "Hashref 'setup' can't set up more than one class from 'isa'" if (
263             @types > 1
264             );
265              
266             my $type = $types[0];
267             $self->$member( $type->new($callback) );
268             next;
269             }
270              
271             croak "Unknown 'setup' value: $callback";
272             }
273              
274             # Discrete callbacks.
275              
276             CALLBACK: while (my ($param, $value) = each %$args) {
277             next unless $param =~ /^on_(\S+)/;
278              
279             my $event = $1;
280              
281             if (ref($value) eq "CODE") {
282             $value = Reflex::Callback::CodeRef->new(
283             object => $self,
284             code_ref => $value,
285             );
286             }
287             elsif (ref($value) eq "ARRAY") {
288             $value = Reflex::Callback::Method->new(
289             object => $value->[0],
290             method_name => $value->[1],
291             );
292             }
293              
294             # There is an object, so we have a watcher.
295             if ($value->object()) {
296             $value->object()->watch($self, $event => $value);
297             next CALLBACK;
298             }
299              
300             # TODO - Who is the watcher?
301             # TODO - Optimization! watch() takes multiple event/callback
302             # pairs. We can combine them into a hash and call watch() once.
303             $self->watch($self, $event => $value);
304             next CALLBACK;
305             }
306              
307             # The session has an object.
308             $session_object_count{$self->session_id()}++;
309             };
310              
311             # TODO - Does Moose have sugar for passing named parameters?
312              
313             # Self is watching something. Register the interest with self.
314             sub watch {
315 35     35 1 79 my ($self, $watched, %callbacks) = @_;
316              
317 35         71 my $watched_id = $watched->get_id();
318              
319 35         141 while (my ($event, $callback) = each %callbacks) {
320 35         66 $event =~ s/^on_//;
321              
322 35 50       98 if (ref $callback) {
323 35 50       94 if (blessed($callback)) {
    0          
324 35 50       164 unless ($callback->isa('Reflex::Callback')) {
325 0         0 croak "Can't use $callback as a callback";
326             }
327             }
328             elsif (ref($callback) eq "CODE") {
329             # Coerce sub{} into Reflex::Callback.
330 0         0 $callback = Reflex::Callback::CodeRef->new(
331             object => $self,
332             code_ref => $callback,
333             );
334             }
335             else {
336 0         0 croak "Can't use $callback as a callback."
337             }
338             }
339             else {
340             # Coerce method name into a callback.
341 0         0 $callback = Reflex::Callback::Method->new(
342             object => $self,
343             method_name => $callback,
344             );
345             }
346              
347 35         108 my $interest = {
348             callback => $callback,
349             event => $event,
350             watched => $watched,
351             };
352              
353 35         84 weaken $interest->{watched};
354 35 100       814 unless (exists $self->watched_objects()->{$watched_id}) {
355 24         591 $self->watched_objects()->{$watched_id} = $watched;
356 24         517 weaken $self->watched_objects()->{$watched_id};
357              
358             # Keep this object's session alive.
359             #$POE::Kernel::poe_kernel->refcount_increment($self->session_id, "in_use");
360             }
361              
362 35         37 push @{$self->watched_object_events()->{$watched_id}->{$event}}, $interest;
  35         821  
363              
364             # Tell what I'm watching that it's being watched.
365              
366 35         134 $watched->_is_watched($self, $event, $callback);
367             }
368              
369 35         79 undef;
370             }
371              
372             # Self is no longer being watched. Remove interest from self.
373             sub _stop_watchers {
374 32     32   42 my ($self, $watcher, $events) = @_;
375              
376 32         67 my $watcher_id = $watcher->get_id();
377 32 50       41 my @events = @{$events || []};
  32         164  
378              
379 32 50       67 unless (@events) {
380             my %events = (
381 36         101 map { $_->{event} => $_->{event} }
382 25         57 map { @$_ }
383 32         40 values %{$self->watchers()}
  32         724  
384             );
385 32         85 @events = keys %events;
386             }
387              
388 32         57 foreach my $event (@events) {
389 35         864 delete $self->watchers_by_event()->{$event}->{$watcher_id};
390             delete $self->watchers_by_event()->{$event} unless (
391 35 50       35 scalar keys %{$self->watchers_by_event()->{$event}}
  35         828  
392             );
393 35         51 pop @{$self->watchers()->{$watcher_id}};
  35         820  
394             }
395              
396             delete $self->watchers()->{$watcher_id} unless (
397             exists $self->watchers()->{$watcher_id} and
398 32 100 100     729 @{$self->watchers()->{$watcher_id}}
  25         570  
399             );
400             }
401              
402             sub _is_watched {
403 35     35   52 my ($self, $watcher, $event, $callback) = @_;
404              
405 35         84 my $interest = {
406             callback => $callback,
407             event => $event,
408             watcher => $watcher ,
409             };
410 35         76 weaken $interest->{watcher};
411              
412 35         64 my $watcher_id = $watcher->get_id();
413              
414 35         39 push @{$self->watchers_by_event()->{$event}->{$watcher_id}}, $interest;
  35         801  
415 35         27 push @{$self->watchers()->{$watcher_id}}, $interest;
  35         724  
416             }
417              
418             my %loaded_event_types;
419              
420             sub re_emit {
421 34     34 0 159 my ($self, $event, %override_args) = @_;
422              
423 34         882 my $new_event = $event->_clone(%override_args);
424 34         177 $new_event->push_emitter($self);
425              
426 34         142 $self->_emit_event($new_event);
427             }
428              
429             sub emit {
430 37     37 1 180 my ($self, %args) = @_;
431              
432 37         80 my $event_type = delete $args{-type};
433 37 100 66     158 $event_type = 'Reflex::Event' unless (
434             defined $event_type and length $event_type
435             );
436              
437 37         60 my $event_name = delete $args{-name};
438 37 50 33     144 $event_name = "generic" unless (
439             defined $event_name and length $event_name
440             );
441              
442             # TODO - Needs consideration:
443             # TODO - Underscores for Reflex parameters?
444             # TODO - Must be a hash reference. Would be nice if non-hashref
445             # errors were pushed to the caller.
446              
447 37         1178 my $event = $event_type->new(
448             _name => $event_name,
449             _emitters => [ $self ],
450             %args
451             );
452              
453 37         120 $self->_emit_event($event);
454             }
455              
456             sub _emit_event {
457 71     71   82 my ($self, $event) = @_;
458              
459 71         1802 my $event_name = $event->_name();
460              
461             # Look for self-handling of the event.
462             # TODO - can() calls are also candidates for caching.
463             # (AKA: Cache as cache can()?)
464              
465             # TODO - Using the class name here is weak.
466             # It would be sweetest if we could find a better role name.
467              
468 71         171 my $caller_role = caller(1); # ref($self); # TODO - Need something better!
469 71         244 $caller_role =~ s/^Reflex::(?:Role::)?//;
470 71         155 $caller_role =~ tr[a-zA-Z0-9][_]cs;
471              
472 71         211 my $self_method = "on_" . lc($caller_role) . "_" . $event_name;
473             #warn $self_method;
474 71 50       403 if ($self->can($self_method)) {
475              
476             # Already seen this; we're recursing! Break it up!
477 0 0       0 if ($self->emits_seen()->{"$self -> $self_method"}) {
478 0         0 $self->emits_seen({});
479 0         0 $poe_kernel->post(
480             $self->session_id(), 'call_gate_method',
481             $self, $self_method, $event,
482             );
483 0         0 return;
484             }
485              
486             # Not recursing yet. Give it a try!
487 0         0 $self->emits_seen()->{"$self -> $self_method"} = 1;
488 0         0 $self->$self_method($event);
489 0         0 return;
490             }
491              
492             # This event isn't watched.
493              
494 71         81 my $deliver_event = $event_name;
495              
496             #warn $deliver_event;
497 71 100       1844 unless (exists $self->watchers_by_event()->{$deliver_event}) {
498 4 50       144 if ($self->promise()) {
499 0         0 $self->promise()->deliver($event);
500 0         0 return;
501             }
502              
503             # TODO - At this point, do we walk up the ownership tree looking
504             # for a promise? That would allow events to bubble out of objects.
505              
506 4         10 $deliver_event = "promise";
507             #warn $event unless exists $self->watchers_by_event()->{$deliver_event};
508 4 50       99 return unless exists $self->watchers_by_event()->{$deliver_event};
509              
510             # Fall through if the promise exists.
511             }
512              
513             # This event is watched. Broadcast it to watchers.
514             # TODO - Accessor calls are expensive. Optimize them away.
515              
516 67         91 while (
517             my ($watcher, $callbacks) = each %{
518 134         4157 $self->watchers_by_event()->{$deliver_event}
519             }
520             ) {
521 67         129 CALLBACK: foreach my $callback_rec (@$callbacks) {
522 70         109 my $callback = $callback_rec->{callback};
523              
524             # Same session. Just deliver it.
525             # TODO - Break recursive callbacks?
526 70 100       221 if (
527             $callback_rec->{watcher}->session_id() eq
528             $POE::Kernel::poe_kernel->get_active_session()->ID
529             ) {
530 60         525 $callback->deliver($event);
531 60         8023 next CALLBACK;
532             }
533              
534             # Different session. Post it through.
535             $poe_kernel->post(
536             $callback_rec->{watcher}->session_id(), 'deliver_callback',
537             $callback, $event,
538 10         41 $callback_rec->{watcher}, $self, # keep objects alive a bit
539             );
540             }
541             }
542             }
543              
544             sub deliver {
545 0     0 0 0 die "@_";
546             }
547              
548             # An object is demolished.
549             # The filehash should destroy everything it watches.
550             # All interests of this object must be manually demolished.
551              
552             sub _shutdown {
553 37     37   42 my $self = shift;
554              
555             # Anything that was watching us, no longer is.
556              
557             my %watchers = (
558 20         70 map { $_->{watcher} => $_->{watcher} }
559 9         36 map { @$_ }
560 37         41 values %{$self->watchers()}
  37         1009  
561             );
562              
563 37         86 foreach my $watcher (values %watchers) {
564 9         45 $watcher->ignore($self);
565             }
566              
567             # Anything we were watching, no longer is being.
568              
569 37         41 foreach my $watched (values %{$self->watched_objects()}) {
  37         925  
570 5         12 $self->ignore($watched);
571             }
572             }
573              
574             sub DEMOLISH {
575 37     37 0 2208 my $self = shift;
576 37         152 $self->_shutdown();
577             }
578              
579             sub ignore {
580 32     32 1 445 my ($self, $watched, @events) = @_;
581              
582 32 50       68 croak "ignore requires at least an object" unless defined $watched;
583              
584 32         81 my $watched_id = $watched->get_id();
585              
586 32 50       76 if (@events) {
587 0         0 delete @{$self->watched_object_events()->{$watched_id}}{@events};
  0         0  
588 0 0       0 unless (scalar keys %{$self->watched_object_events()->{$watched_id}}) {
  0         0  
589 0         0 delete $self->watched_object_events()->{$watched_id};
590 0         0 delete $self->watched_objects()->{$watched_id};
591              
592             # Decrement the session's use count.
593             #$POE::Kernel::poe_kernel->refcount_decrement($self->session_id, "in_use");
594             }
595 0         0 $watched->_stop_watchers($self, \@events);
596             }
597             else {
598 8 50   8   67 use Carp qw(cluck); cluck "whaaaa" unless defined $watched;
  8         9  
  8         3622  
  32         69  
599 32         863 delete $self->watched_object_events()->{$watched_id};
600 32         759 delete $self->watched_objects()->{$watched_id};
601 32         136 $watched->_stop_watchers($self);
602              
603             # Decrement the session's use count.
604             #$POE::Kernel::poe_kernel->refcount_decrement($self->session_id, "in_use");
605             }
606             }
607              
608             # http://en.wikipedia.org/wiki/Call_gate
609              
610             sub call_gate {
611 71     71 1 109 my ($self, $method) = @_;
612              
613             # POE::Kernel has already gone away.
614 71 50       144 return 0 unless $POE::Kernel::poe_kernel;
615              
616 71 100       136 return 1 if (
617             $self->session_id() eq $POE::Kernel::poe_kernel->get_active_session()->ID()
618             );
619              
620 11         94 $POE::Kernel::poe_kernel->call(
621             $self->session_id(), "call_gate_method", $self, $method, @_[2..$#_]
622             );
623              
624 11         737 return 0;
625             }
626              
627             sub run_within_session {
628 0     0 1 0 my ($self, $method) = @_;
629              
630 0 0       0 if (
631             $self->session_id() eq $POE::Kernel::poe_kernel->get_active_session()->ID()
632             ) {
633 0 0       0 if (ref($method) =~ /^CODE/) {
634 0         0 return $method->(@_[2..$#_]);
635             }
636 0         0 return $self->$method(@_[2..$#_]);
637             }
638              
639 0 0       0 if (ref($method) =~ /^CODE/) {
640 0         0 return $POE::Kernel::poe_kernel->call(
641             $self->session_id(), "call_gate_coderef", $method, @_[2..$#_]
642             );
643             }
644              
645 0         0 return $POE::Kernel::poe_kernel->call(
646             $self->session_id(), "call_gate_method", $self, $method, @_[2..$#_]
647             );
648             }
649              
650             sub run_all {
651 9     9 1 80 POE::Kernel->run();
652 9         7197 $singleton_session_id = undef;
653             }
654              
655             # TODO - Added semantics to wait for a specific event.
656             # Need to document that.
657              
658             sub next {
659 0     0 1   my $self = shift;
660 0 0         $self->promise() || $self->promise(Reflex::Callback::Promise->new());
661              
662 0 0         return $self->promise()->next() unless @_;
663              
664             # TODO - It's user friendly to accept a list and build a hash
665             # internally, but that adds runtime CPU overhead. On the other
666             # hand, passing in a hashref with dummy values kind of sucks for the
667             # user. I'd like to discuss the relative merits and costs of each
668             # option with someone.
669              
670 0           my %which = map { $_ => 1 } @_;
  0            
671 0           while (my $next = $self->promise()->next()) {
672 0 0         return $next if exists $which{$next->{name}};
673             }
674             }
675              
676             1;
677              
678             __END__
679              
680             =pod
681              
682             =encoding UTF-8
683              
684             =for :stopwords Rocco Caputo
685              
686             =head1 NAME
687              
688             Reflex::Role::Reactive - Make an object reactive (aka, event driven).
689              
690             =head1 VERSION
691              
692             This document describes version 0.100, released on April 02, 2017.
693              
694             =head1 SYNOPSIS
695              
696             With Moose:
697              
698             package Object;
699             use Moose;
700             with 'Reflex::Role::Reactive';
701              
702             ...;
703              
704             1;
705              
706             Without Moose:
707              
708             # Sorry, roles are defined and composed using Moose.
709             # However, Reflex::Base may be used the old fashioned way.
710              
711             =head1 DESCRIPTION
712              
713             Reflex::Role::Reactive provides Reflex's event-driven features to
714             other objects. It provides public methods that help use reactive
715             objects and to write them.
716              
717             =for Pod::Coverage BUILD DEMOLISH deliver get_id re_emit
718              
719             =head1 Public Attributes
720              
721             =head2 session_id
722              
723             Each Reflex object is associated with a POE session, although a single
724             session may (and usually does) drive several objects. Reflex objects
725             expose session_id() for times where it's important to know which
726             session owns them. Usually when interfacing between Reflex and POE.
727              
728             session_id() is rarely needed, especially since Reflex provides helper
729             classes for working with POE modules. Please see one or more of:
730             L<Reflex::POE::Event>, L<Reflex::POE::Postback>,
731             L<Reflex::POE::Session>, L<Reflex::POE::Wheel> and
732             L<Reflex::POE::Wheel::Run>.
733              
734             sub method {
735             my $self = shift;
736             print(
737             "I, $self, am driven by POE::Sesson ID ",
738             $self->session_id(), "\n"
739             );
740             }
741              
742             =head2 watch
743              
744             watch() allows one object (the watcher) to register interest in
745             events emitted by another. It takes three named parameters:
746             "watched" must contain a Reflex object (either a Reflex::Role::Reactive
747             consumer, or a Reflex::Base subclass). "event" contains the name of
748             an event that the watched object emits. Finally, "callback" contains
749             a Reflex::Callback that will be invoked when the event occurs.
750              
751             use Reflex::Callbacks(cb_method);
752              
753             $self->watch(
754             watched => $an_object_maybe_myself,
755             event => "occurrence",
756             callback => cb_method($self, "method_name"),
757             );
758              
759             =head2 emit
760              
761             Emit an event. This triggers callbacks for anything waiting for the
762             event from the object that emitted it. Callback invocation is often
763             synchronous, but this isn't guaranteed. Later versions of Reflex will
764             support remote objects, where the emitter and callback may not be in
765             the same room.
766              
767             Emit takes two named parameters so far: "event" names the event being
768             emitted and is required. "args" allows data to be passed along with
769             the event, and it should contain a hashref of named values.
770              
771             Reflex::Stream emits a "failure" event when things don't go as
772             planned:
773              
774             sub _emit_failure {
775             my ($self, $errfun) = @_;
776              
777             $self->emit(
778             -name => "failure",
779             data => undef,
780             errnum => ($!+0),
781             errstr => "$!",
782             errfun => $errfun,
783             );
784              
785             return;
786             }
787              
788             =head2 ignore
789              
790             The ignore() method tells Reflex that one object has lost interest in
791             events from another. It requires at least one parameter, the object
792             to be ignored. Additional parameters may name specific events to
793             ignore.
794              
795             Ignore an object entirely:
796              
797             $self->ignore($an_object_maybe_myself);
798              
799             Ignore just specific events:
800              
801             my @events = qw(success failure);
802             $self->ignore($an_object_maybe_myself, @events);
803              
804             An object may destruct while it's being watched and/or is watching
805             other objects. DEMOLISH will ensure that all watchers related to the
806             outgoing object are cleaned up. Therefore it's usually more
807             convenient to just destroy things when done with them.
808              
809             =head2 call_gate
810              
811             call_gate() is a helper that ensures a method is called from the same
812             POE::Session instance that owns its object. It's mainly of interest
813             to authors of POE modules and their Reflex interfaces. Other users
814             may never need it.
815              
816             POE consumers often return responses to the sessions that made
817             requests. For Reflex objects to receive these responses, they must
818             first send their requests from the right sessions. call_gate() helps
819             by ensuring the proper session is active.
820              
821             call_gate() takes one required positional parameter: the name of the
822             method calling call_gate(). Any other parameters are passed back to
823             the method, re-creating @_ as it was originally.
824              
825             call_gate() immediately returns 1 if it's called from the correct
826             session. Otherwise it re-invokes the method in the proper session and
827             returns 0.
828              
829             It's important to put call_gate() first in methods that need it, and
830             for them to return immediately fi call_gate() returns false.
831              
832             This method from Reflex::Signal makes sure the signal is watched by
833             the same session that owns the object doing the watching:
834              
835             sub start_watching {
836             my $self = shift;
837             return unless $self->call_gate("start_watching");
838             $POE::Kernel::poe_kernel->sig($self->name(), "signal_happened");
839             }
840              
841             =head2 run_within_session
842              
843             run_within_session() is another helper method to ensure some code is
844             running in the POE session that POE modules may expect. It takes one
845             required positional parameter, a code reference to invoke or the name
846             of a method to call on $self. Any other parameters are passed to the
847             code that will be executed.
848              
849             For example the IRC bot in eg/eg-13-irc-bot.pl wants to register
850             callbacks with POE::Component::IRC. It calls a couple $bot->yield()
851             methods within the object's session. This helps the component know
852             where to send its responses:
853              
854             sub BUILD {
855             my $self = shift;
856              
857             # Set up $self->component() to contain
858             # a POE::Component::IRC object.
859              
860             ...;
861              
862             # Register this object's interest in the component,
863             # via the session that owns this object.
864             $self->run_within_session(
865             sub {
866             $self->component()->yield(register => "all");
867             $self->component()->yield(connect => {});
868             }
869             )
870             }
871              
872             =head2 next
873              
874             Wait for the next event promised by an object. Requires the object to
875             emit an event that isn't already explicitly handled. All Reflex
876             objects will run in the background while next() blocks.
877              
878             next() returns the next event emitted by an object. Objects cease to
879             run while your code processes the event, so be quick about it.
880              
881             Here's most of eg/eg-32-promise-tiny.pl, which shows how to next() on
882             events from a Reflex::Interval.
883              
884             use Reflex::Interval;
885              
886             my $t = Reflex::Interval->new(
887             interval => 1,
888             auto_repeat => 1,
889             );
890              
891             while (my $event = $t->next()) {
892             print "next() returned event '$event->{name}'...\n";
893             }
894              
895             It's tempting to rename this method next().
896              
897             =head2 run_all
898              
899             Run all active Reflex objects until they destruct. This will not
900             return discrete events, like next() does. It will not return at all
901             before the program is done. It returns no meaningful value yet.
902              
903             run_all() is useful when you don't care to next() on objects
904             individually. You just want the program to run 'til it's done.
905              
906             =head1 EXAMPLES
907              
908             Many of the examples in the distribution's eg directory use Reflex
909             objects. Explore and enjoy!
910              
911             =head1 SEE ALSO
912              
913             Please see those modules/websites for more information related to this module.
914              
915             =over 4
916              
917             =item *
918              
919             L<Reflex|Reflex>
920              
921             =item *
922              
923             L<Moose::Manual::Concepts>
924              
925             =item *
926              
927             L<Reflex>
928              
929             =item *
930              
931             L<Reflex::Base>
932              
933             =item *
934              
935             L<Reflex/ACKNOWLEDGEMENTS>
936              
937             =item *
938              
939             L<Reflex/ASSISTANCE>
940              
941             =item *
942              
943             L<Reflex/AUTHORS>
944              
945             =item *
946              
947             L<Reflex/BUGS>
948              
949             =item *
950              
951             L<Reflex/BUGS>
952              
953             =item *
954              
955             L<Reflex/CONTRIBUTORS>
956              
957             =item *
958              
959             L<Reflex/COPYRIGHT>
960              
961             =item *
962              
963             L<Reflex/LICENSE>
964              
965             =item *
966              
967             L<Reflex/TODO>
968              
969             =back
970              
971             =head1 BUGS AND LIMITATIONS
972              
973             You can make new bug reports, and view existing ones, through the
974             web interface at L<http://rt.cpan.org/Public/Dist/Display.html?Name=Reflex>.
975              
976             =head1 AUTHOR
977              
978             Rocco Caputo <rcaputo@cpan.org>
979              
980             =head1 COPYRIGHT AND LICENSE
981              
982             This software is copyright (c) 2017 by Rocco Caputo.
983              
984             This is free software; you can redistribute it and/or modify it under
985             the same terms as the Perl 5 programming language system itself.
986              
987             =head1 AVAILABILITY
988              
989             The latest version of this module is available from the Comprehensive Perl
990             Archive Network (CPAN). Visit L<http://www.perl.com/CPAN/> to find a CPAN
991             site near you, or see L<https://metacpan.org/module/Reflex/>.
992              
993             =head1 DISCLAIMER OF WARRANTY
994              
995             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
996             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT
997             WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
998             PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND,
999             EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
1000             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1001             PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
1002             SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME
1003             THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
1004              
1005             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
1006             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
1007             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE
1008             TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR
1009             CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
1010             SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
1011             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
1012             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
1013             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
1014             DAMAGES.
1015              
1016             =cut