reacto 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +932 -11
- data/doc/reactive_programming_with_reacto.md +238 -0
- data/lib/reacto.rb +70 -0
- data/lib/reacto/behaviours.rb +24 -1
- data/lib/reacto/constants.rb +4 -1
- data/lib/reacto/executors.rb +8 -10
- data/lib/reacto/labeled_trackable.rb +14 -2
- data/lib/reacto/operations.rb +23 -2
- data/lib/reacto/operations/act.rb +69 -0
- data/lib/reacto/operations/append.rb +45 -0
- data/lib/reacto/operations/blocking_enumerable.rb +40 -0
- data/lib/reacto/operations/buffer.rb +1 -4
- data/lib/reacto/operations/chunk.rb +81 -0
- data/lib/reacto/operations/chunk_while.rb +56 -0
- data/lib/reacto/operations/cycle.rb +27 -0
- data/lib/reacto/operations/delay_each.rb +75 -0
- data/lib/reacto/operations/depend_on.rb +4 -5
- data/lib/reacto/operations/diff.rb +8 -10
- data/lib/reacto/operations/drop.rb +6 -8
- data/lib/reacto/operations/drop_while.rb +23 -0
- data/lib/reacto/operations/each_collect.rb +57 -0
- data/lib/reacto/operations/each_with_object.rb +31 -0
- data/lib/reacto/operations/extremums.rb +54 -0
- data/lib/reacto/operations/find_index.rb +28 -0
- data/lib/reacto/operations/flat_map.rb +2 -2
- data/lib/reacto/operations/flatten.rb +2 -7
- data/lib/reacto/operations/flatten_labeled.rb +44 -0
- data/lib/reacto/operations/{label.rb → group_by_label.rb} +1 -1
- data/lib/reacto/operations/include.rb +40 -0
- data/lib/reacto/operations/inject.rb +15 -9
- data/lib/reacto/operations/map.rb +15 -13
- data/lib/reacto/operations/merge.rb +17 -16
- data/lib/reacto/operations/operation_on_labeled.rb +29 -0
- data/lib/reacto/operations/partition.rb +52 -0
- data/lib/reacto/operations/prepend.rb +0 -3
- data/lib/reacto/operations/rescue_and_replace_error.rb +21 -0
- data/lib/reacto/operations/retry.rb +26 -0
- data/lib/reacto/operations/retry_when.rb +30 -0
- data/lib/reacto/operations/select.rb +2 -6
- data/lib/reacto/operations/slice.rb +50 -0
- data/lib/reacto/operations/slice_when.rb +41 -0
- data/lib/reacto/operations/split_labeled.rb +32 -0
- data/lib/reacto/operations/take.rb +9 -14
- data/lib/reacto/operations/take_while.rb +28 -0
- data/lib/reacto/operations/throttle.rb +2 -3
- data/lib/reacto/operations/track_on.rb +1 -3
- data/lib/reacto/shared_trackable.rb +2 -5
- data/lib/reacto/subscriptions/buffered_subscription.rb +10 -9
- data/lib/reacto/subscriptions/executor_subscription.rb +12 -4
- data/lib/reacto/subscriptions/tracker_subscription.rb +0 -4
- data/lib/reacto/subscriptions/zipping_subscription.rb +0 -1
- data/lib/reacto/trackable.rb +429 -64
- data/lib/reacto/version.rb +1 -1
- data/reacto.gemspec +2 -3
- data/spec/reacto/labeled_trackable_spec.rb +17 -0
- data/spec/reacto/trackable/act_spec.rb +15 -0
- data/spec/reacto/trackable/all_spec.rb +38 -0
- data/spec/reacto/trackable/any_spec.rb +39 -0
- data/spec/reacto/trackable/append_spec.rb +38 -0
- data/spec/reacto/trackable/buffer_spec.rb +11 -15
- data/spec/reacto/trackable/chunk_spec.rb +86 -0
- data/spec/reacto/trackable/chunk_while_spec.rb +22 -0
- data/spec/reacto/trackable/class_level/combine_last_spec.rb +1 -3
- data/spec/reacto/trackable/class_level/interval_spec.rb +4 -6
- data/spec/reacto/trackable/class_level/make_spec.rb +0 -15
- data/spec/reacto/trackable/{zip_spec.rb → class_level/zip_spec.rb} +0 -2
- data/spec/reacto/trackable/concat_spec.rb +12 -12
- data/spec/reacto/trackable/count_spec.rb +38 -0
- data/spec/reacto/trackable/cycle_spec.rb +14 -0
- data/spec/reacto/trackable/delay_each_spec.rb +18 -0
- data/spec/reacto/trackable/depend_on_spec.rb +6 -9
- data/spec/reacto/trackable/diff_spec.rb +3 -5
- data/spec/reacto/trackable/drop_errors_spec.rb +1 -3
- data/spec/reacto/trackable/drop_while_spec.rb +15 -0
- data/spec/reacto/trackable/each_cons_spec.rb +53 -0
- data/spec/reacto/trackable/each_slice_spec.rb +37 -0
- data/spec/reacto/trackable/each_with_index_spec.rb +33 -0
- data/spec/reacto/trackable/each_with_object_spec.rb +26 -0
- data/spec/reacto/trackable/entries_spec.rb +25 -0
- data/spec/reacto/trackable/execute_on_spec.rb +33 -0
- data/spec/reacto/trackable/find_index_spec.rb +31 -0
- data/spec/reacto/trackable/find_spec.rb +34 -0
- data/spec/reacto/trackable/first_spec.rb +36 -0
- data/spec/reacto/trackable/flat_map_latest_spec.rb +5 -5
- data/spec/reacto/trackable/flat_map_spec.rb +25 -0
- data/spec/reacto/trackable/flatten_labeled_spec.rb +48 -0
- data/spec/reacto/trackable/grep_spec.rb +29 -0
- data/spec/reacto/trackable/grep_v_spec.rb +23 -0
- data/spec/reacto/trackable/{label_spec.rb → group_by_label_spec.rb} +4 -11
- data/spec/reacto/trackable/include_spec.rb +23 -0
- data/spec/reacto/trackable/inject_spec.rb +30 -4
- data/spec/reacto/trackable/lift_spec.rb +1 -3
- data/spec/reacto/trackable/map_spec.rb +17 -3
- data/spec/reacto/trackable/max_by_spec.rb +12 -0
- data/spec/reacto/trackable/max_spec.rb +19 -0
- data/spec/reacto/trackable/merge_spec.rb +6 -7
- data/spec/reacto/trackable/min_by_spec.rb +12 -0
- data/spec/reacto/trackable/min_spec.rb +19 -0
- data/spec/reacto/trackable/minmax_by_spec.rb +12 -0
- data/spec/reacto/trackable/minmax_spec.rb +19 -0
- data/spec/reacto/trackable/none_spec.rb +38 -0
- data/spec/reacto/trackable/on_spec.rb +11 -4
- data/spec/reacto/trackable/one_spec.rb +38 -0
- data/spec/reacto/trackable/partition_spec.rb +23 -0
- data/spec/reacto/trackable/prepend_spec.rb +1 -3
- data/spec/reacto/trackable/reject_spec.rb +21 -0
- data/spec/reacto/trackable/rescue_and_replace_error_spec.rb +48 -0
- data/spec/reacto/trackable/rescue_and_replace_error_with_spec.rb +26 -0
- data/spec/reacto/trackable/retry_spec.rb +50 -0
- data/spec/reacto/trackable/retry_when_spec.rb +33 -0
- data/spec/reacto/trackable/select_spec.rb +18 -7
- data/spec/reacto/trackable/slice_after_spec.rb +38 -0
- data/spec/reacto/trackable/slice_before_spec.rb +38 -0
- data/spec/reacto/trackable/slice_when_spec.rb +26 -0
- data/spec/reacto/trackable/sort_by_spec.rb +16 -0
- data/spec/reacto/trackable/sort_spec.rb +23 -0
- data/spec/reacto/trackable/split_labeled_spec.rb +37 -0
- data/spec/reacto/trackable/take_while_spec.rb +16 -0
- data/spec/reacto/trackable/throttle_spec.rb +2 -3
- data/spec/reacto/trackable/track_on_spec.rb +2 -3
- data/spec/reacto/trackable/uniq_spec.rb +2 -4
- data/spec/support/helpers.rb +9 -1
- metadata +135 -25
- data/Gemfile.lock +0 -32
- data/lib/reacto/operations/cache.rb +0 -53
- data/spec/reacto/trackable/cache_spec.rb +0 -64
@@ -0,0 +1,238 @@
|
|
1
|
+
# Reactive programming with Reacto
|
2
|
+
|
3
|
+
## Reactive programming
|
4
|
+
|
5
|
+
Reactive programming is a buzzword these days. Many think it is something
|
6
|
+
really complex and new, but in reality it is very simple thing.
|
7
|
+
It creates applications which know when and how to react to changes.
|
8
|
+
These changes could be outside ones - from some data source or another application,
|
9
|
+
or inside ones - parts of the application depend on other parts and react to
|
10
|
+
changes in them.
|
11
|
+
|
12
|
+
Reactive programming can be achieved by building the right dependencies between
|
13
|
+
applications, sources of data, or modules of applications and logic around
|
14
|
+
these dependencies -> the changes in all the modules/sources should be propagated
|
15
|
+
to the interested modules. There are frameworks and languages that do that
|
16
|
+
for us, there are different implementations of that too. In ruby we have
|
17
|
+
[RxRuby](https://github.com/ReactiveX/RxRuby), [Frappuccino](https://github.com/steveklabnik/frappuccino) and
|
18
|
+
now - Reacto.
|
19
|
+
|
20
|
+
The truth is that the way Reacto implements its _Reactive programming_ is by
|
21
|
+
implementing a not so complex design pattern. In this document we'll introduce it
|
22
|
+
and compare it to another pattern, which we already know.
|
23
|
+
|
24
|
+
## Patterns
|
25
|
+
|
26
|
+
### Enumerator
|
27
|
+
|
28
|
+
Using Reacto is basically using a specific programming pattern.
|
29
|
+
It even looks like a familiar one - the `Enumerator` (or Iterator).
|
30
|
+
We can call `each` on any `Enumerable` in Ruby and we'll get
|
31
|
+
an `Enumerator`. Then we can call `next` on it to _pull_ values.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
enumerator = [1, 2, 3].each
|
35
|
+
p enumerator.next # 1
|
36
|
+
p enumerator.next # 2
|
37
|
+
p enumerator.next # 3
|
38
|
+
p enumerator.next # a StopIteration error will be raised
|
39
|
+
```
|
40
|
+
|
41
|
+
It is more common to pass a *block* to `each`, which will be called for every
|
42
|
+
`next` value and the exception will be avoided.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
[1, 2, 3].each { |value| p value }
|
46
|
+
```
|
47
|
+
|
48
|
+
Lastly we can create our own implementations of `Enumerator`. For example
|
49
|
+
one that generates infinite sequential integer values, beginning
|
50
|
+
from zero.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
enumerator = Enumerator.new do |yielder|
|
54
|
+
n = 0
|
55
|
+
|
56
|
+
loop do
|
57
|
+
yielder << n
|
58
|
+
n = n + 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
p enumerator.next # 1
|
63
|
+
p enumerator.next # 2
|
64
|
+
p enumerator.next # 3
|
65
|
+
# .....
|
66
|
+
```
|
67
|
+
|
68
|
+
OK so we can look at the `Enumerator` as a behavior or a source which
|
69
|
+
produces values _on demand_. We _pull_ values from it.
|
70
|
+
|
71
|
+
### Reacto::Trackable
|
72
|
+
|
73
|
+
The central abstraction in Reacto is the `Trackable`. It, like the `Enumerator` implements
|
74
|
+
a pattern. An instance can be created depending on the underlying behaviour
|
75
|
+
or data source using different methods. As with the `Enumerator` we can
|
76
|
+
create a `Reacto::Trackable` using an `Enumerable`.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
trackable = Reacto::Trackable.enumerable([1, 2, 3])
|
80
|
+
```
|
81
|
+
|
82
|
+
Now we can track for values by calling `on(value: <Proc>)` on it.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
trackable.on(value: ->(v) { p v })
|
86
|
+
|
87
|
+
# same as
|
88
|
+
trackable.on do |val|
|
89
|
+
p val
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
This way the `Trackable` will _push_ the values to our _consumer_ lambda/block.
|
94
|
+
So in this case it is pretty much the same as with the `Enumerator`. The
|
95
|
+
main difference is that our code is not _pulling_ the values from the
|
96
|
+
`Trackable`, instead, the `Trackable` is _pushing_ the values, when they are
|
97
|
+
ready, to our client code.
|
98
|
+
|
99
|
+
As with the `Enumerator`, which includes the `Enumerable` module, `Reacto::Trackable` has
|
100
|
+
a lot of handy functions (called operations - `map`, `select`, etc. - more on them later) we can chain to it.
|
101
|
+
A difference between `Trackable` and `Enumerator` is that we can track for errors and `close` notifications
|
102
|
+
too.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
trackable.on(
|
106
|
+
error: ->(e) { p e.message }, value: ->(v) { p v }, close: ->() { p 'DONE' }
|
107
|
+
)
|
108
|
+
```
|
109
|
+
|
110
|
+
Now if there is an error while receiving the values, it will be passed to
|
111
|
+
the error-handling lambda.
|
112
|
+
So we can react to incoming notifications and even errors. The `close`
|
113
|
+
notification is flagging that all the incoming data has arrived.
|
114
|
+
|
115
|
+
Let's sum it up - a `Reacto::Trackable` is much like an `Enumerator`, but
|
116
|
+
our consumer code is not _pulling_ the data from it, it is instead _tracking_
|
117
|
+
it for notifications. These notifications could be values, errors, or
|
118
|
+
`close` ones. They are _pushed_ to our consumer code when available.
|
119
|
+
|
120
|
+
## Asynchronous programming
|
121
|
+
|
122
|
+
By default when we call `on` on a `Reacto::Trackable` instance, our program blocks
|
123
|
+
and waits for all the notifications to arrive until an error or `close`
|
124
|
+
notification is received. That's OK, but as said, values are received
|
125
|
+
when available, so in some cases it will be more efficient to do something else
|
126
|
+
while waiting for the values to come.
|
127
|
+
Enter `execute_on`. It, like all the other operations that can be called on
|
128
|
+
`Reacto::Trackable`, creates a new `Trackable` with source the caller.
|
129
|
+
The new one will execute all of its operations on the _Executor_ passed to the
|
130
|
+
`execute_on`. An _Executor_ is a service that manages threads.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
trackable = trackable.execute_on(:background)
|
134
|
+
trackable.on(
|
135
|
+
error: ->(e) { p e.message }, value: ->(v) { p v }, close: ->() { p 'DONE' }
|
136
|
+
)
|
137
|
+
|
138
|
+
# The call to `on` won't block so we can have some code here, which will
|
139
|
+
# execute immediately after it. When the values arrive, they'll be printed.
|
140
|
+
|
141
|
+
```
|
142
|
+
|
143
|
+
It's that easy to have code in the background. And what if we want to block
|
144
|
+
at some point?
|
145
|
+
First of all the `on` method returns a `Reacto::Subscription` instance
|
146
|
+
which we can use to `unsubscribe` from the `Reacto::Trackable` or do other
|
147
|
+
things. One of this things is to pass it to `Reacto::Trackable#await`:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
trackable = trackable.execute_on(:background)
|
151
|
+
subscription = trackable.on(
|
152
|
+
error: ->(e) { p e.message }, value: ->(v) { p v }, close: ->() { p 'DONE' }
|
153
|
+
)
|
154
|
+
|
155
|
+
# Some code we want to be executed while waiting for values
|
156
|
+
|
157
|
+
trackable.await(subscription) # Block until an error or close notification arives
|
158
|
+
```
|
159
|
+
|
160
|
+
That's how easy it is.
|
161
|
+
|
162
|
+
## Operations
|
163
|
+
|
164
|
+
So we were just talking about the _code_ in the background, but what kind of
|
165
|
+
code can we attach to the `Reacto::Trackable`s exactly?
|
166
|
+
We can answer that by defining what an *operation* is. An *operation* is
|
167
|
+
called on a `Reacto::Trackable` instance and it always returns a new
|
168
|
+
`Reacto::Trackable` instance with source the caller.
|
169
|
+
The *operation* will change the incoming notifications,
|
170
|
+
when the tracking takes places. Let's look at some examples:
|
171
|
+
|
172
|
+
#### Map
|
173
|
+
|
174
|
+
The `map` operation is a simple transformation. It's the same as
|
175
|
+
the `Enumerable`'s `map` - transforms each of the values into something else
|
176
|
+
using the passed `block`'s return values'.
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
source = Reacto::Trackable.enumerable((1..5))
|
180
|
+
|
181
|
+
trackable = source.map { |v| v * v * v }
|
182
|
+
trackable.on { |val| p val } # Will print 1 8 27 64 125
|
183
|
+
```
|
184
|
+
|
185
|
+
#### Select
|
186
|
+
|
187
|
+
The `select` operation filters incoming values, again similar to its
|
188
|
+
`Enumerable` counterpart.
|
189
|
+
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
source = Reacto::Trackable.enumerable((1..5))
|
193
|
+
|
194
|
+
trackable = source.select { |v| v % 2 == 0 }
|
195
|
+
trackable.on { |val| p val } # will print 2 4
|
196
|
+
```
|
197
|
+
|
198
|
+
#### Inject
|
199
|
+
|
200
|
+
Of course there is the `inject` operation which gives us the opportunity
|
201
|
+
to combine the current incoming value with the previous one and emit that
|
202
|
+
as value.
|
203
|
+
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
source = Reacto::Trackable.enumerable((1..5))
|
207
|
+
|
208
|
+
trackable = source.inject { |prev, val| prev + val }
|
209
|
+
trackable.on { |val| p val } # will print 1 3(1+2) 6(3 + 3) 10(6 + 4) 15(10 + 5)
|
210
|
+
```
|
211
|
+
|
212
|
+
Operation similar to `inject` are `diff`, which calls a given block for every
|
213
|
+
two consequetly emitted values and the `Reacto::Trackable` resulting from it,
|
214
|
+
emits this the block's return value. Another one is `each_with_object`, which
|
215
|
+
calls a given block for each value emitted by the source with an arbitrary
|
216
|
+
object given, and emits the initially given object.
|
217
|
+
|
218
|
+
#### FlatMap
|
219
|
+
|
220
|
+
Interesting operation is the `flat_map`. It accepts a block, which for
|
221
|
+
every call with a value must return an instance of `Reacto::Trackable`.
|
222
|
+
In the end the resulting `Reacto::Trackable` will emit all the values,
|
223
|
+
emitted by all of these returned by the block `Reacto::Trackable`s.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
source = Reacto::Trackable.enumerable((1..5))
|
227
|
+
|
228
|
+
trackable = source.flat_map { |val| Reacto::Trackable.enumerable(val..6) }
|
229
|
+
trackable.on { |val| p val }
|
230
|
+
# will print 1 2 3 4 5 6 2 3 4 5 6 3 4 5 6 4 5 6 5 6
|
231
|
+
```
|
232
|
+
|
233
|
+
This basically means that we can add operations to the inner `Reacto::Trackable`s too,
|
234
|
+
because all the available operations are producing `Reacto::Trackable` instances.
|
235
|
+
|
236
|
+
## Links
|
237
|
+
|
238
|
+
TODO
|
data/lib/reacto.rb
CHANGED
@@ -8,5 +8,75 @@ require 'reacto/shared_trackable'
|
|
8
8
|
require 'reacto/labeled_trackable'
|
9
9
|
|
10
10
|
module Reacto
|
11
|
+
module_function
|
11
12
|
|
13
|
+
def make(executor_param = nil, executor: executor, &block)
|
14
|
+
Trackable.make(executor_param, executor: executor, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def never
|
18
|
+
Trackable.never
|
19
|
+
end
|
20
|
+
|
21
|
+
def close(executor: nil)
|
22
|
+
Trackable.close(executor: executor)
|
23
|
+
end
|
24
|
+
|
25
|
+
def error(err, executor: nil)
|
26
|
+
Trackable.error(err, executor: executor)
|
27
|
+
end
|
28
|
+
|
29
|
+
def value(value, executor: nil)
|
30
|
+
Trackable.value(value, executor: executor)
|
31
|
+
end
|
32
|
+
|
33
|
+
def enumerable(enumerable, executor: nil)
|
34
|
+
Trackable.enumerable(enumerable, executor: executor)
|
35
|
+
end
|
36
|
+
|
37
|
+
def later(secs, value, executor: :tasks)
|
38
|
+
Trackable.later(secs, value, executor: executor)
|
39
|
+
end
|
40
|
+
|
41
|
+
def interval(
|
42
|
+
interval, enumerator = Behaviours.integers_enumerator, executor: nil
|
43
|
+
)
|
44
|
+
Trackable.interval(interval, enumerator, executor: executor)
|
45
|
+
end
|
46
|
+
|
47
|
+
def repeat(array, int: 0.1, executor: nil)
|
48
|
+
Trackable.repeat(array, int: int, executor: executor)
|
49
|
+
end
|
50
|
+
|
51
|
+
def combine(*trackables, &block)
|
52
|
+
Trackable.combine(*trackables, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def combine_last(*trackables, &block)
|
56
|
+
Trackable.combine_last(*trackables, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
def zip(*trackables, &block)
|
60
|
+
Trackable.zip(*trackables, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def concat(*trackables)
|
64
|
+
Trackable.concat(*trackables)
|
65
|
+
end
|
66
|
+
|
67
|
+
def [](object)
|
68
|
+
if object.is_a?(Enumerable)
|
69
|
+
enumerable(object)
|
70
|
+
elsif object.is_a?(StandardError)
|
71
|
+
error(object)
|
72
|
+
elsif object == :close
|
73
|
+
close
|
74
|
+
elsif object == :never || object.nil?
|
75
|
+
never
|
76
|
+
else
|
77
|
+
value(object)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
alias_method :combine_latest, :combine
|
12
82
|
end
|
data/lib/reacto/behaviours.rb
CHANGED
@@ -25,11 +25,26 @@ module Reacto
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
def enumerable(enumerable_value)
|
29
|
+
->(tracker) do
|
30
|
+
begin
|
31
|
+
enumerable_value.each do |val|
|
32
|
+
break unless tracker.subscribed?
|
33
|
+
tracker.on_value(val)
|
34
|
+
end
|
35
|
+
|
36
|
+
tracker.on_close if tracker.subscribed?
|
37
|
+
rescue => error
|
38
|
+
tracker.on_error(error) if tracker.subscribed?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
28
43
|
def integers_enumerator
|
29
44
|
Enumerator.new do |yielder|
|
30
45
|
n = 0
|
31
46
|
loop do
|
32
|
-
yielder
|
47
|
+
yielder << n
|
33
48
|
n = n + 1
|
34
49
|
end
|
35
50
|
end
|
@@ -46,5 +61,13 @@ module Reacto
|
|
46
61
|
end
|
47
62
|
end
|
48
63
|
end
|
64
|
+
|
65
|
+
def constant(const)
|
66
|
+
-> (_val) { const }
|
67
|
+
end
|
68
|
+
|
69
|
+
def same_predicate(val)
|
70
|
+
-> (value) { val == value }
|
71
|
+
end
|
49
72
|
end
|
50
73
|
end
|
data/lib/reacto/constants.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
module Reacto
|
2
2
|
NOTHING = {}
|
3
|
-
NO_ACTION = -> (*
|
3
|
+
NO_ACTION = -> (*_args) {}
|
4
|
+
IDENTITY_ACTION = -> (arg) { arg }
|
4
5
|
DEFAULT_ON_ERROR = -> (e) { raise e }
|
5
6
|
ID = -> (v) { v }
|
6
7
|
NO_VALUE = {}
|
8
|
+
TRUE_PREDICATE = -> (_val) { true }
|
9
|
+
FALSE_PREDICATE = -> (_val) { false }
|
7
10
|
end
|
data/lib/reacto/executors.rb
CHANGED
@@ -4,28 +4,26 @@ require 'concurrent/executor/fixed_thread_pool'
|
|
4
4
|
|
5
5
|
module Reacto
|
6
6
|
module Executors
|
7
|
-
class CurrentExecutor
|
8
|
-
def post
|
9
|
-
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
7
|
module_function
|
14
8
|
|
15
9
|
def immediate
|
16
|
-
Concurrent::ImmediateExecutor.new
|
10
|
+
@immediate ||= Concurrent::ImmediateExecutor.new
|
17
11
|
end
|
18
12
|
|
19
13
|
def current
|
20
|
-
|
14
|
+
immediate
|
21
15
|
end
|
22
16
|
|
23
17
|
def io
|
24
|
-
Concurrent::CachedThreadPool.new
|
18
|
+
@io ||= Concurrent::CachedThreadPool.new
|
25
19
|
end
|
26
20
|
|
27
21
|
def tasks
|
28
|
-
Concurrent::FixedThreadPool.new(4) # Number of cores here?
|
22
|
+
@tasks ||= Concurrent::FixedThreadPool.new(4) # Number of cores here?
|
23
|
+
end
|
24
|
+
|
25
|
+
def new_thread
|
26
|
+
@new_thread ||= Concurrent::SimpleExecutorService.new
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
@@ -4,10 +4,22 @@ module Reacto
|
|
4
4
|
class LabeledTrackable < Trackable
|
5
5
|
attr_reader :label
|
6
6
|
|
7
|
-
def initialize(label, executor = nil,
|
8
|
-
super(
|
7
|
+
def initialize(label, executor = nil, &block)
|
8
|
+
super(executor, &block)
|
9
9
|
|
10
10
|
@label = label
|
11
11
|
end
|
12
|
+
|
13
|
+
def relabel
|
14
|
+
new_label = yield label
|
15
|
+
|
16
|
+
self.class.new(new_label, @executor, &@behaviour)
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def create_lifted(&block)
|
22
|
+
self.class.new(label, @executor, &block)
|
23
|
+
end
|
12
24
|
end
|
13
25
|
end
|
data/lib/reacto/operations.rb
CHANGED
@@ -4,20 +4,41 @@ require 'reacto/operations/select'
|
|
4
4
|
require 'reacto/operations/inject'
|
5
5
|
require 'reacto/operations/diff'
|
6
6
|
require 'reacto/operations/drop'
|
7
|
+
require 'reacto/operations/drop_while'
|
7
8
|
require 'reacto/operations/drop_errors'
|
8
9
|
require 'reacto/operations/take'
|
10
|
+
require 'reacto/operations/take_while'
|
9
11
|
require 'reacto/operations/last'
|
10
12
|
require 'reacto/operations/track_on'
|
11
13
|
require 'reacto/operations/prepend'
|
14
|
+
require 'reacto/operations/append'
|
12
15
|
require 'reacto/operations/concat'
|
13
16
|
require 'reacto/operations/merge'
|
14
17
|
require 'reacto/operations/buffer'
|
18
|
+
require 'reacto/operations/delay_each'
|
15
19
|
require 'reacto/operations/uniq'
|
16
20
|
require 'reacto/operations/flatten'
|
17
21
|
require 'reacto/operations/throttle'
|
18
22
|
require 'reacto/operations/flat_map'
|
19
23
|
require 'reacto/operations/flat_map_latest'
|
20
|
-
require 'reacto/operations/cache'
|
21
24
|
require 'reacto/operations/depend_on'
|
22
|
-
require 'reacto/operations/
|
25
|
+
require 'reacto/operations/group_by_label'
|
26
|
+
require 'reacto/operations/operation_on_labeled'
|
27
|
+
require 'reacto/operations/flatten_labeled'
|
28
|
+
require 'reacto/operations/split_labeled'
|
23
29
|
require 'reacto/operations/act'
|
30
|
+
require 'reacto/operations/retry'
|
31
|
+
require 'reacto/operations/retry_when'
|
32
|
+
require 'reacto/operations/rescue_and_replace_error'
|
33
|
+
require 'reacto/operations/blocking_enumerable'
|
34
|
+
require 'reacto/operations/chunk'
|
35
|
+
require 'reacto/operations/chunk_while'
|
36
|
+
require 'reacto/operations/cycle'
|
37
|
+
require 'reacto/operations/each_collect'
|
38
|
+
require 'reacto/operations/each_with_object'
|
39
|
+
require 'reacto/operations/find_index'
|
40
|
+
require 'reacto/operations/include'
|
41
|
+
require 'reacto/operations/extremums'
|
42
|
+
require 'reacto/operations/partition'
|
43
|
+
require 'reacto/operations/slice'
|
44
|
+
require 'reacto/operations/slice_when'
|