-
Notifications
You must be signed in to change notification settings - Fork 22
/
eg-60-collection-promise.pl
176 lines (150 loc) · 4.67 KB
/
eg-60-collection-promise.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/env perl
# Agorman's concerns:
#
# 1. This way of doing things seems like I couldn't do $tc->next to get
# the result event.
#
# a. We can add a new pass_on() that re-emits events without
# clobbering _sender. However, this inverts the problem---in some
# cases we may want _sender to refer to the collection! The caller
# should be able to decide.
#
# b. Add the member to $args when re-emitting an event. This idea
# is implemeted in PromiseCollection, below. It feels like the best
# option from a design standpoint, but it's also the most tedious
# and least efficient. Every layer of an application's stack must
# re-emit events that are part of its interface. That's good
# encapsulation and explicit code.
#
# Since it's a common thing to do, maybe there can be some
# shortcuts. Like "handles" in Moose allows an object's methods to
# become part of its owner. Something similar may be done to say an
# object's events are part of its owner.
#
# c. We can hack Reflex::Role::Reactive to propagate unhandled
# emitted events to parents/grandparent/etc. promises. Too
# implicit. "Spooky action at a distance." I don't like invisible
# things like this.
#
# 2. Not sure I love $self->result( $args ); inside my TestCollectible
# method. Be nice if it could just get the current state of the
# object. Using sender doesn't work because the state has (possible)
# changed by the time of the callback.
#
# I don't know if that would be possible without cloning the object
# at the time of result(). And then any calls on the clone wouldn't
# affect the original object that might still be in the collection.
#
# It might help to think of results as being different types than
# the objects that create them. DNS resolvers return IP addresses,
# not more DNS resolvers. HTTP user agents return HTTP responses.
#
# Maybe result() could be renamed response() or output()?
#
# 3. It would be nice if my callback inside the owner class was
# on_foo_event rather than on_event but I don't know of a good way to
# deal with the plural singular thing.
#
# a. I haven't got to this. 1 & 2 have given me a lot to work on
# already.
my $collectible_id = 1;
{
package TestCollectible;
use Moose;
with 'Reflex::Role::Collectible';
extends 'Reflex::Base'; # TODO - Implicit in Reflex::Role::Collectible?
use Reflex::Trait::Observed;
use Reflex::Interval;
has id => (
is => 'rw',
isa => 'Int',
);
has count => (
is => 'rw',
isa => 'Int',
default => 0,
);
observes timer => (
is => 'rw',
isa => 'Maybe[Reflex::Interval]',
setup => sub {
Reflex::Interval->new(
interval => rand() / 10, # Mixes up the output.
auto_repeat => 1,
);
}
);
sub on_timer_tick {
my $self = shift;
my $count = $self->count() + 1;
$self->result({ value => $count });
if ($count < 9) {
$self->count($count);
return;
}
$self->timer(undef);
$self->stopped();
return;
}
}
###
{
package TestCollection;
use Moose;
extends 'Reflex::Base';
use Reflex::Collection;
has_many foos => (
handles => { remember_foo => "remember" },
);
sub BUILD {
my ($self, $args) = @_;
for (1..9) {
$self->remember_foo(TestCollectible->new(id => $collectible_id++));
}
}
sub on_result {
my ($self, $args) = @_;
my $foo = $args->{_sender}->get_first_emitter();
my $value = $args->{value};
my $foo_type = ref $foo;
printf(
"test collection got a result from %s! id => %s, value => %s\n",
$foo_type, $foo->id, $value
);
}
}
{
package PromiseCollection;
use Moose;
extends 'Reflex::Base';
use Reflex::Collection;
has_many foos => (
handles => { remember_foo => "remember" },
);
sub BUILD {
my ($self, $args) = @_;
for (1..9) {
$self->remember_foo(TestCollectible->new(id => $collectible_id++));
}
}
# 2. TestCollection can include a new "member" parameter when it
# re-emits events. "member" points to the collectible object that
# really sent the event. Reflex::Role::Reactive is free to clobber
# _sender.
sub on_result {
my ($self, $args) = @_;
$self->emit( event => "result", args => $args );
}
}
# By waiting on a promise for TestCollection, we really want to
# receive events from any TestCollectible in the collection.
my $tc = TestCollection->new();
my $tcp = PromiseCollection->new();
while (my $e = $tcp->next) {
my $sender = $e->{arg}{_sender}->get_first_emitter();
printf(
"promise collection got a result of %s! id => %s, value => %s\n",
ref($sender), $sender->id, $e->{arg}{value}
);
}
exit;