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
@@ -15,11 +15,11 @@ module Reacto
|
|
15
15
|
|
16
16
|
def call(tracker)
|
17
17
|
close = -> () { @close = true }
|
18
|
-
error =
|
18
|
+
error = -> (e) do
|
19
19
|
delay_task(tracker) unless @ready
|
20
20
|
@error = e
|
21
21
|
end
|
22
|
-
value =
|
22
|
+
value = -> (v) do
|
23
23
|
delay_task(tracker) unless @ready
|
24
24
|
@last = v
|
25
25
|
end
|
@@ -57,4 +57,3 @@ module Reacto
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
60
|
-
|
@@ -2,11 +2,8 @@ require 'reacto/trackable'
|
|
2
2
|
|
3
3
|
module Reacto
|
4
4
|
class SharedTrackable < Trackable
|
5
|
-
def initialize(
|
6
|
-
|
7
|
-
&block
|
8
|
-
)
|
9
|
-
super(behaviour, executor, &block)
|
5
|
+
def initialize(executor = nil, activate_on_subscribe = false, &block)
|
6
|
+
super(executor, &block)
|
10
7
|
|
11
8
|
@activate_on_subscribe = activate_on_subscribe
|
12
9
|
@active = false
|
@@ -1,9 +1,11 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
1
3
|
module Reacto
|
2
4
|
module Subscriptions
|
3
5
|
class BufferedSubscription < SimpleSubscription
|
4
6
|
include Subscription
|
5
7
|
|
6
|
-
attr_accessor :
|
8
|
+
attr_accessor :buffer, :last_error
|
7
9
|
|
8
10
|
def initialize(parent)
|
9
11
|
@parent = parent
|
@@ -11,27 +13,27 @@ module Reacto
|
|
11
13
|
@active = false
|
12
14
|
|
13
15
|
@buffer = Hash.new(NO_VALUE)
|
14
|
-
@current_index = 0
|
16
|
+
@current_index = Concurrent::AtomicFixnum.new(0)
|
15
17
|
@last_error = nil
|
16
18
|
|
17
|
-
open =
|
19
|
+
open = -> () do
|
18
20
|
@active = true
|
19
21
|
@parent.on_open
|
20
22
|
end
|
21
23
|
|
22
|
-
value =
|
23
|
-
@buffer[@current_index] = v
|
24
|
-
@current_index
|
24
|
+
value = -> (v) do
|
25
|
+
@buffer[@current_index.value] = v
|
26
|
+
@current_index.increment
|
25
27
|
|
26
28
|
@parent.on_value(v)
|
27
29
|
end
|
28
30
|
|
29
|
-
error =
|
31
|
+
error = -> (e) do
|
30
32
|
@last_error = e
|
31
33
|
@parent.on_error(e)
|
32
34
|
end
|
33
35
|
|
34
|
-
close =
|
36
|
+
close = -> () do
|
35
37
|
@closed = true
|
36
38
|
@parent.on_close
|
37
39
|
end
|
@@ -49,4 +51,3 @@ module Reacto
|
|
49
51
|
end
|
50
52
|
end
|
51
53
|
end
|
52
|
-
|
@@ -45,17 +45,25 @@ module Reacto
|
|
45
45
|
def on_close
|
46
46
|
return if !subscribed? || @closed
|
47
47
|
|
48
|
+
unsubscribe_subscription = Subscriptions.on_close_and_error do
|
49
|
+
@executor.post(&method(:unsubscribe))
|
50
|
+
@closed = true
|
51
|
+
end
|
52
|
+
@wrapped.add(unsubscribe_subscription)
|
53
|
+
|
48
54
|
@executor.post(&@wrapped.method(:on_close))
|
49
|
-
@executor.post(&method(:unsubscribe))
|
50
|
-
@closed = true
|
51
55
|
end
|
52
56
|
|
53
57
|
def on_error(error)
|
54
58
|
return if !subscribed? || @closed
|
55
59
|
|
60
|
+
unsubscribe_subscription = Subscriptions.on_close_and_error do
|
61
|
+
@executor.post(&method(:unsubscribe))
|
62
|
+
@closed = true
|
63
|
+
end
|
64
|
+
@wrapped.add(unsubscribe_subscription)
|
65
|
+
|
56
66
|
@executor.post(error, &@wrapped.method(:on_error))
|
57
|
-
unsubscribe
|
58
|
-
@closed = true
|
59
67
|
end
|
60
68
|
end
|
61
69
|
end
|
data/lib/reacto/trackable.rb
CHANGED
@@ -9,10 +9,21 @@ require 'reacto/operations'
|
|
9
9
|
require 'reacto/executors'
|
10
10
|
require 'reacto/resources'
|
11
11
|
|
12
|
-
# TODO: Refactor the constructors and the factory methods
|
13
12
|
module Reacto
|
14
13
|
class Trackable
|
15
|
-
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
TOPICS = %i(open value error close)
|
17
|
+
|
18
|
+
EXECUTOR_ALIASES = {
|
19
|
+
new_thread: Executors.new_thread,
|
20
|
+
background: Executors.tasks,
|
21
|
+
tasks: Executors.tasks,
|
22
|
+
io: Executors.io,
|
23
|
+
current: Executors.current,
|
24
|
+
immediate: Executors.immediate,
|
25
|
+
now: Executors.immediate
|
26
|
+
}
|
16
27
|
|
17
28
|
class << self
|
18
29
|
def never
|
@@ -25,6 +36,10 @@ module Reacto
|
|
25
36
|
)
|
26
37
|
end
|
27
38
|
|
39
|
+
def concat(*trackables)
|
40
|
+
trackables.inject { |current, trackable| current.concat(trackable) }
|
41
|
+
end
|
42
|
+
|
28
43
|
def combine_last(*trackables, &block)
|
29
44
|
combine_create(
|
30
45
|
Subscriptions::CombiningLastSubscription, *trackables, &block
|
@@ -35,24 +50,27 @@ module Reacto
|
|
35
50
|
combine_create(Subscriptions::ZippingSubscription, *trackables, &block)
|
36
51
|
end
|
37
52
|
|
38
|
-
def close(executor
|
39
|
-
make(
|
40
|
-
subscriber.on_close
|
41
|
-
end
|
53
|
+
def close(executor: nil)
|
54
|
+
make(executor) { |subscriber| subscriber.on_close }
|
42
55
|
end
|
43
56
|
|
44
|
-
def error(err, executor
|
45
|
-
make(
|
57
|
+
def error(err, executor: nil)
|
58
|
+
make(executor) do |subscriber|
|
46
59
|
subscriber.on_error(err)
|
47
60
|
end
|
48
61
|
end
|
49
62
|
|
50
|
-
def make(
|
51
|
-
|
52
|
-
|
63
|
+
def make(executor_param = nil, executor: nil, &block)
|
64
|
+
real_executor = executor_param ? executor_param : executor
|
65
|
+
|
66
|
+
behaviour = block_given? ? block : NO_ACTION
|
67
|
+
self.new(real_executor, &behaviour)
|
53
68
|
end
|
54
69
|
|
55
70
|
def later(secs, value, executor: Reacto::Executors.tasks)
|
71
|
+
stored = EXECUTOR_ALIASES[executor]
|
72
|
+
executor = stored if stored
|
73
|
+
|
56
74
|
if executor.is_a?(Concurrent::ImmediateExecutor)
|
57
75
|
make do |tracker|
|
58
76
|
sleep secs
|
@@ -72,6 +90,9 @@ module Reacto
|
|
72
90
|
enumerator = Behaviours.integers_enumerator,
|
73
91
|
executor: nil
|
74
92
|
)
|
93
|
+
stored = EXECUTOR_ALIASES[executor]
|
94
|
+
executor = stored if stored
|
95
|
+
|
75
96
|
if executor.is_a?(Concurrent::ImmediateExecutor)
|
76
97
|
make do |tracker|
|
77
98
|
Behaviours.with_close_and_error(tracker) do |subscriber|
|
@@ -91,7 +112,7 @@ module Reacto
|
|
91
112
|
end
|
92
113
|
else
|
93
114
|
make do |tracker|
|
94
|
-
Thread
|
115
|
+
Thread.abort_on_exception = true
|
95
116
|
|
96
117
|
queue = Queue.new
|
97
118
|
task = Concurrent::TimerTask.new(execution_interval: interval) do
|
@@ -134,27 +155,15 @@ module Reacto
|
|
134
155
|
)
|
135
156
|
end
|
136
157
|
|
137
|
-
def value(value, executor
|
138
|
-
make(Behaviours.single_value(value)
|
158
|
+
def value(value, executor: nil)
|
159
|
+
make(executor, &Behaviours.single_value(value))
|
139
160
|
end
|
140
161
|
|
141
|
-
def enumerable(enumerable, executor
|
142
|
-
make(
|
143
|
-
begin
|
144
|
-
enumerable.each do |val|
|
145
|
-
break unless tracker.subscribed?
|
146
|
-
tracker.on_value(val)
|
147
|
-
end
|
148
|
-
|
149
|
-
tracker.on_close if tracker.subscribed?
|
150
|
-
rescue => error
|
151
|
-
tracker.on_error(error) if tracker.subscribed?
|
152
|
-
end
|
153
|
-
end
|
162
|
+
def enumerable(enumerable, executor: nil)
|
163
|
+
make(executor, &Behaviours.enumerable(enumerable))
|
154
164
|
end
|
155
165
|
|
156
|
-
|
157
|
-
end
|
166
|
+
alias_method :combine_latest, :combine
|
158
167
|
|
159
168
|
private
|
160
169
|
|
@@ -168,12 +177,164 @@ module Reacto
|
|
168
177
|
end
|
169
178
|
end
|
170
179
|
|
171
|
-
def initialize(
|
172
|
-
@behaviour = block_given? ? block :
|
180
|
+
def initialize(executor = nil, &block)
|
181
|
+
@behaviour = block_given? ? block : NO_ACTION
|
182
|
+
|
183
|
+
stored = EXECUTOR_ALIASES[executor]
|
184
|
+
executor = stored if stored
|
173
185
|
@executor = executor
|
174
186
|
end
|
175
187
|
|
176
|
-
def
|
188
|
+
def all?(&block)
|
189
|
+
lift(Operations::BlockingEnumerable.new(:'all?', block))
|
190
|
+
end
|
191
|
+
|
192
|
+
def any?(&block)
|
193
|
+
lift(Operations::BlockingEnumerable.new(:'any?', block))
|
194
|
+
end
|
195
|
+
|
196
|
+
def none?(&block)
|
197
|
+
lift(Operations::BlockingEnumerable.new(:'none?', block))
|
198
|
+
end
|
199
|
+
|
200
|
+
def one?(&block)
|
201
|
+
lift(Operations::BlockingEnumerable.new(:'one?', block))
|
202
|
+
end
|
203
|
+
|
204
|
+
def sort(&block)
|
205
|
+
lift(Operations::BlockingEnumerable.new(:sort, block))
|
206
|
+
end
|
207
|
+
|
208
|
+
def sort_by(&block)
|
209
|
+
return self unless block_given?
|
210
|
+
|
211
|
+
lift(Operations::BlockingEnumerable.new(:sort_by, block))
|
212
|
+
end
|
213
|
+
|
214
|
+
def partition(executor: nil, &block)
|
215
|
+
return self unless block_given?
|
216
|
+
|
217
|
+
executor = retrieve_executor(executor)
|
218
|
+
executor = @executor if executor.nil?
|
219
|
+
|
220
|
+
lift(Operations::Partition.new(block, executor: executor))
|
221
|
+
end
|
222
|
+
|
223
|
+
def chunk(executor: nil, &block)
|
224
|
+
return self unless block_given?
|
225
|
+
|
226
|
+
executor = retrieve_executor(executor)
|
227
|
+
executor = @executor if executor.nil?
|
228
|
+
|
229
|
+
lift(Operations::Chunk.new(block, executor: executor))
|
230
|
+
end
|
231
|
+
|
232
|
+
def chunk_while(executor: nil, &block)
|
233
|
+
executor = retrieve_executor(executor)
|
234
|
+
executor = @executor if executor.nil?
|
235
|
+
|
236
|
+
lift(Operations::ChunkWhile.new(block, executor: executor))
|
237
|
+
end
|
238
|
+
|
239
|
+
def cycle(n = nil)
|
240
|
+
lift(Operations::Cycle.new(@behaviour, n))
|
241
|
+
end
|
242
|
+
|
243
|
+
def find(if_none = NO_VALUE, &block)
|
244
|
+
trackable = select(&block).first
|
245
|
+
|
246
|
+
if if_none != NO_VALUE
|
247
|
+
trackable = trackable.append(if_none, condition: :source_empty)
|
248
|
+
end
|
249
|
+
|
250
|
+
trackable
|
251
|
+
end
|
252
|
+
|
253
|
+
def find_index(value = NO_VALUE, &block)
|
254
|
+
predicate =
|
255
|
+
if value != NO_VALUE
|
256
|
+
-> (v) { value == v }
|
257
|
+
else
|
258
|
+
block
|
259
|
+
end
|
260
|
+
lift(Operations::FindIndex.new(predicate))
|
261
|
+
end
|
262
|
+
|
263
|
+
def count(value = NO_VALUE, &block)
|
264
|
+
source =
|
265
|
+
if value != NO_VALUE
|
266
|
+
select(&Behaviours.same_predicate(value))
|
267
|
+
elsif block_given?
|
268
|
+
select(&block)
|
269
|
+
else
|
270
|
+
self
|
271
|
+
end
|
272
|
+
|
273
|
+
source.map(1).inject(&:+).last
|
274
|
+
end
|
275
|
+
|
276
|
+
def each_cons(n, &block)
|
277
|
+
raise ArgumentError.new('invalid size') if n <= 0
|
278
|
+
return each(&block) if n == 1
|
279
|
+
|
280
|
+
reset_action = -> (current) { current[1..-1] }
|
281
|
+
|
282
|
+
trackable = lift(Operations::EachCollect.new(
|
283
|
+
n, reset_action: reset_action, on_error: NO_ACTION, on_close: NO_ACTION
|
284
|
+
))
|
285
|
+
block_given? ? trackable.on(&block) : trackable
|
286
|
+
end
|
287
|
+
|
288
|
+
def each_slice(n, &block)
|
289
|
+
raise ArgumentError.new('invalid size') if n <= 0
|
290
|
+
|
291
|
+
trackable = lift(Operations::EachCollect.new(n))
|
292
|
+
|
293
|
+
block_given? ? trackable.on(&block) : trackable
|
294
|
+
end
|
295
|
+
|
296
|
+
def each_with_index(&block)
|
297
|
+
index = 0
|
298
|
+
|
299
|
+
collect_action = -> (val, collection) do
|
300
|
+
collection << val
|
301
|
+
collection << index
|
302
|
+
index += 1
|
303
|
+
end
|
304
|
+
|
305
|
+
trackable = lift(Operations::EachCollect.new(
|
306
|
+
2, collect_action: collect_action, init_action: -> () { index = 0 },
|
307
|
+
on_error: NO_ACTION, on_close: NO_ACTION
|
308
|
+
))
|
309
|
+
|
310
|
+
block_given? ? trackable.on(&block) : trackable
|
311
|
+
end
|
312
|
+
|
313
|
+
def entries(n = nil)
|
314
|
+
return [] if n && n.is_a?(Integer) && n <= 0
|
315
|
+
|
316
|
+
trackable = self
|
317
|
+
trackable = trackable.take(n) if n && n.is_a?(Integer) && n > 0
|
318
|
+
|
319
|
+
result = []
|
320
|
+
subscription = trackable.on(value: ->(v) { result << v })
|
321
|
+
|
322
|
+
trackable.await(subscription)
|
323
|
+
|
324
|
+
result
|
325
|
+
end
|
326
|
+
|
327
|
+
def to_a
|
328
|
+
entries
|
329
|
+
end
|
330
|
+
|
331
|
+
def to_h
|
332
|
+
to_a.to_h
|
333
|
+
end
|
334
|
+
|
335
|
+
def on(trackers = {}, &block)
|
336
|
+
trackers[:value] = block if block_given?
|
337
|
+
|
177
338
|
unless (trackers.keys - TOPICS).empty?
|
178
339
|
raise "This Trackable supports only #{TOPICS}, " \
|
179
340
|
"but #{trackers.keys} were passed."
|
@@ -186,7 +347,9 @@ module Reacto
|
|
186
347
|
# Clean-up logic
|
187
348
|
end
|
188
349
|
|
189
|
-
def track(notification_tracker)
|
350
|
+
def track(notification_tracker, &block)
|
351
|
+
return on(&block) if block_given?
|
352
|
+
|
190
353
|
subscription =
|
191
354
|
Subscriptions::TrackerSubscription.new(notification_tracker, self)
|
192
355
|
|
@@ -197,7 +360,7 @@ module Reacto
|
|
197
360
|
|
198
361
|
def lift(operation = nil, &block)
|
199
362
|
operation = block_given? ? block : operation
|
200
|
-
|
363
|
+
create_lifted do |tracker_subscription|
|
201
364
|
begin
|
202
365
|
modified = operation.call(tracker_subscription)
|
203
366
|
lift_behaviour(modified) unless modified == NOTHING
|
@@ -207,40 +370,126 @@ module Reacto
|
|
207
370
|
end
|
208
371
|
end
|
209
372
|
|
210
|
-
def flat_map(transform = nil, &block)
|
211
|
-
|
373
|
+
def flat_map(transform = nil, label: nil, &block)
|
374
|
+
if label
|
375
|
+
lift(Operations::OperationOnLabeled.new(
|
376
|
+
label, block_given? ? block : transform, op: :flat_map
|
377
|
+
))
|
378
|
+
else
|
379
|
+
lift(Operations::FlatMap.new(block_given? ? block : transform))
|
380
|
+
end
|
212
381
|
end
|
213
382
|
|
214
383
|
def flat_map_latest(transform = nil, &block)
|
215
384
|
lift(Operations::FlatMapLatest.new(block_given? ? block : transform))
|
216
385
|
end
|
217
386
|
|
218
|
-
def map(
|
219
|
-
|
220
|
-
block_given?
|
221
|
-
|
387
|
+
def map(val = NO_VALUE, error: nil, close: nil, label: nil, &block)
|
388
|
+
action =
|
389
|
+
if block_given?
|
390
|
+
block
|
391
|
+
else
|
392
|
+
val == NO_VALUE ? IDENTITY_ACTION : Behaviours.constant(val)
|
393
|
+
end
|
394
|
+
if label
|
395
|
+
lift(Operations::OperationOnLabeled.new(
|
396
|
+
label, action, error: error, close: close
|
397
|
+
))
|
398
|
+
else
|
399
|
+
lift(Operations::Map.new(action, error: error, close: close))
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def max(&block)
|
404
|
+
lift(Operations::Extremums.new(action: block))
|
405
|
+
end
|
406
|
+
|
407
|
+
def max_by(&block)
|
408
|
+
lift(Operations::Extremums.new(by: block))
|
409
|
+
end
|
410
|
+
|
411
|
+
def min(&block)
|
412
|
+
lift(Operations::Extremums.new(action: block, type: :min))
|
413
|
+
end
|
414
|
+
|
415
|
+
def min_by(&block)
|
416
|
+
lift(Operations::Extremums.new(by: block, type: :min))
|
417
|
+
end
|
418
|
+
|
419
|
+
def minmax(&block)
|
420
|
+
lift(Operations::Extremums.new(action: block, type: :minmax))
|
421
|
+
end
|
422
|
+
|
423
|
+
def minmax_by(&block)
|
424
|
+
lift(Operations::Extremums.new(by: block, type: :minmax))
|
425
|
+
end
|
426
|
+
|
427
|
+
def grep(pattern, &block)
|
428
|
+
select_map(-> (v) { pattern === v }, &block)
|
429
|
+
end
|
430
|
+
|
431
|
+
def grep_v(pattern, &block)
|
432
|
+
select_map(-> (v) { !(pattern === v) }, &block)
|
433
|
+
end
|
434
|
+
|
435
|
+
def include?(obj)
|
436
|
+
lift(Operations::Include.new(obj))
|
437
|
+
end
|
438
|
+
|
439
|
+
def lazy
|
440
|
+
self # Just to comply with Enumerable
|
222
441
|
end
|
223
442
|
|
224
443
|
def wrap(**args)
|
225
444
|
lift(Operations::Wrap.new(args))
|
226
445
|
end
|
227
446
|
|
228
|
-
def select(
|
229
|
-
|
447
|
+
def select(label: nil, &block)
|
448
|
+
return self unless block_given?
|
449
|
+
|
450
|
+
if label
|
451
|
+
lift(Operations::OperationOnLabeled.new(label, block, op: :select))
|
452
|
+
else
|
453
|
+
lift(Operations::Select.new(block))
|
454
|
+
end
|
230
455
|
end
|
231
456
|
|
232
|
-
def
|
233
|
-
|
457
|
+
def reject(&block)
|
458
|
+
select(&->(val) { !block.call(val)} )
|
234
459
|
end
|
235
460
|
|
236
|
-
def
|
237
|
-
|
461
|
+
def inject(initial = NO_VALUE, label: nil, initial_value: NO_VALUE, &block)
|
462
|
+
return self unless block_given?
|
463
|
+
|
464
|
+
init = initial != NO_VALUE ? initial : initial_value
|
465
|
+
if label
|
466
|
+
lift(Operations::OperationOnLabeled.new(
|
467
|
+
label, block, op: :inject, initial_value: init
|
468
|
+
))
|
469
|
+
else
|
470
|
+
lift(Operations::Inject.new(block, init))
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
def each_with_object(obj, &block)
|
475
|
+
lift(Operations::EachWithObject.new(block, obj))
|
476
|
+
end
|
477
|
+
|
478
|
+
def diff(initial = NO_VALUE, &block)
|
479
|
+
lift(Operations::Diff.new(
|
480
|
+
block_given? ? block : Operations::Diff::DEFAULT_FN, initial
|
481
|
+
))
|
238
482
|
end
|
239
483
|
|
240
484
|
def drop(how_many_to_drop)
|
241
485
|
lift(Operations::Drop.new(how_many_to_drop))
|
242
486
|
end
|
243
487
|
|
488
|
+
def drop_while(&block)
|
489
|
+
predicate = block_given? ? block : FALSE_PREDICATE
|
490
|
+
lift(Operations::DropWhile.new(predicate))
|
491
|
+
end
|
492
|
+
|
244
493
|
def drop_errors
|
245
494
|
lift(Operations::DropErrors.new)
|
246
495
|
end
|
@@ -249,6 +498,12 @@ module Reacto
|
|
249
498
|
lift(Operations::Take.new(how_many_to_take))
|
250
499
|
end
|
251
500
|
|
501
|
+
def take_while(&block)
|
502
|
+
return self unless block_given?
|
503
|
+
|
504
|
+
lift(Operations::TakeWhile.new(block))
|
505
|
+
end
|
506
|
+
|
252
507
|
def uniq
|
253
508
|
lift(Operations::Uniq.new)
|
254
509
|
end
|
@@ -257,8 +512,10 @@ module Reacto
|
|
257
512
|
lift(Operations::Flatten.new)
|
258
513
|
end
|
259
514
|
|
260
|
-
def first
|
261
|
-
|
515
|
+
def first(n = 1)
|
516
|
+
raise ArgumentError.new('negative array size') if n < 0
|
517
|
+
|
518
|
+
take(n)
|
262
519
|
end
|
263
520
|
|
264
521
|
def [](x)
|
@@ -273,12 +530,16 @@ module Reacto
|
|
273
530
|
lift(Operations::Prepend.new(enumerable))
|
274
531
|
end
|
275
532
|
|
533
|
+
def append(to_append, condition: nil)
|
534
|
+
lift(Operations::Append.new(to_append, condition: condition))
|
535
|
+
end
|
536
|
+
|
276
537
|
def concat(trackable)
|
277
538
|
lift(Operations::Concat.new(trackable))
|
278
539
|
end
|
279
540
|
|
280
|
-
def merge(
|
281
|
-
lift(Operations::Merge.new(
|
541
|
+
def merge(*trackables, delay_error: false)
|
542
|
+
lift(Operations::Merge.new(trackables, delay_error: delay_error))
|
282
543
|
end
|
283
544
|
|
284
545
|
def buffer(count: nil, delay: nil)
|
@@ -289,47 +550,131 @@ module Reacto
|
|
289
550
|
buffer(delay: delay)
|
290
551
|
end
|
291
552
|
|
292
|
-
def
|
293
|
-
lift(Operations::
|
553
|
+
def delay_each(delay)
|
554
|
+
lift(Operations::DelayEach.new(delay))
|
294
555
|
end
|
295
556
|
|
296
|
-
def
|
297
|
-
|
298
|
-
lift(Operations::Cache.new(type: type, **settings))
|
557
|
+
def throttle(delay)
|
558
|
+
lift(Operations::Throttle.new(delay))
|
299
559
|
end
|
300
560
|
|
301
|
-
def depend_on(trackable, key: :data,
|
561
|
+
def depend_on(trackable, key: :data, &block)
|
302
562
|
lift(Operations::DependOn.new(
|
303
|
-
trackable, key: key, accumulator:
|
563
|
+
trackable, key: key, accumulator: block
|
304
564
|
))
|
305
565
|
end
|
306
566
|
|
307
|
-
def
|
308
|
-
lift(Operations::
|
309
|
-
block_given? ? block : labeling_action, executor
|
310
|
-
))
|
567
|
+
def group_by_label(executor: nil, &block)
|
568
|
+
lift(Operations::GroupByLabel.new(block, executor))
|
311
569
|
end
|
312
570
|
|
313
|
-
def
|
314
|
-
lift(Operations::
|
571
|
+
def flatten_labeled(initial: NO_VALUE, &block)
|
572
|
+
lift(Operations::FlattenLabeled.new(block, initial))
|
573
|
+
end
|
574
|
+
|
575
|
+
def split_labeled(label, executor: nil, &block)
|
576
|
+
lift(Operations::SplitLabeled.new(label, block, executor))
|
577
|
+
end
|
578
|
+
|
579
|
+
def act(on: Operations::Act::ALL, &block)
|
580
|
+
lift(Operations::Act.new(block, on))
|
581
|
+
end
|
582
|
+
|
583
|
+
def retry(retries = 1)
|
584
|
+
lift(Operations::Retry.new(@behaviour, retries))
|
585
|
+
end
|
586
|
+
|
587
|
+
def retry_when(&block)
|
588
|
+
return self unless block_given?
|
589
|
+
|
590
|
+
lift(Operations::RetryWhen.new(@behaviour, block))
|
591
|
+
end
|
592
|
+
|
593
|
+
def rescue_and_replace_error(&block)
|
594
|
+
return self unless block_given?
|
595
|
+
|
596
|
+
lift(Operations::RescueAndReplaceError.new(block))
|
597
|
+
end
|
598
|
+
|
599
|
+
def rescue_and_replace_error_with(trackable)
|
600
|
+
rescue_and_replace_error { |_error| trackable }
|
601
|
+
end
|
602
|
+
|
603
|
+
def combine_last(*trackables, &block)
|
604
|
+
return self unless block_given?
|
605
|
+
|
606
|
+
self.class.combine_last(*([self] + trackables), &block)
|
607
|
+
end
|
608
|
+
|
609
|
+
def combine(*trackables, &block)
|
610
|
+
return self unless block_given?
|
611
|
+
|
612
|
+
self.class.combine(*([self] + trackables), &block)
|
613
|
+
end
|
614
|
+
|
615
|
+
def slice_after(pattern = NO_VALUE, &block)
|
616
|
+
slice(pattern, type: :after, &block)
|
617
|
+
end
|
618
|
+
|
619
|
+
def slice_before(pattern = NO_VALUE, &block)
|
620
|
+
slice(pattern, type: :before, &block)
|
621
|
+
end
|
622
|
+
|
623
|
+
def slice(pattern = NO_ACTION, type:, &block)
|
624
|
+
predicate =
|
625
|
+
if pattern != NO_VALUE
|
626
|
+
-> (val) { pattern === val }
|
627
|
+
else
|
628
|
+
block
|
629
|
+
end
|
630
|
+
lift(Operations::Slice.new(predicate, type: type))
|
631
|
+
end
|
632
|
+
|
633
|
+
def slice_when(&block)
|
634
|
+
lift(Operations::SliceWhen.new(block))
|
635
|
+
end
|
636
|
+
|
637
|
+
def zip(*trackables, &block)
|
638
|
+
self.class.zip(*([self] + trackables), &block)
|
315
639
|
end
|
316
640
|
|
317
641
|
def track_on(executor)
|
642
|
+
stored = EXECUTOR_ALIASES[executor]
|
643
|
+
executor = stored if stored
|
644
|
+
|
318
645
|
lift(Operations::TrackOn.new(executor))
|
319
646
|
end
|
320
647
|
|
321
648
|
def execute_on(executor)
|
322
|
-
|
649
|
+
stored = EXECUTOR_ALIASES[executor]
|
650
|
+
executor = stored if stored
|
651
|
+
|
652
|
+
self.class.new(executor, &@behaviour)
|
323
653
|
end
|
324
654
|
|
325
655
|
def await(subscription, timeout = nil)
|
656
|
+
return unless subscription.subscribed?
|
657
|
+
|
326
658
|
latch = Concurrent::CountDownLatch.new(1)
|
327
659
|
subscription.add(Subscriptions.on_close_and_error { latch.count_down })
|
660
|
+
|
328
661
|
latch.wait(timeout)
|
662
|
+
rescue Exception => e
|
663
|
+
raise e unless e.message.include?('No live threads left')
|
329
664
|
end
|
330
665
|
|
331
666
|
alias_method :skip, :drop
|
332
667
|
alias_method :skip_errors, :drop_errors
|
668
|
+
alias_method :collect, :map
|
669
|
+
alias_method :collect_concat, :flat_map
|
670
|
+
alias_method :detect, :find
|
671
|
+
alias_method :each, :on
|
672
|
+
alias_method :each_entry, :on
|
673
|
+
alias_method :combine_latest, :combine
|
674
|
+
alias_method :group_by, :group_by_label
|
675
|
+
alias_method :find_all, :select
|
676
|
+
alias_method :'member?', :'include?'
|
677
|
+
alias_method :reduce, :inject
|
333
678
|
|
334
679
|
def do_track(subscription)
|
335
680
|
if @executor
|
@@ -339,8 +684,28 @@ module Reacto
|
|
339
684
|
end
|
340
685
|
end
|
341
686
|
|
687
|
+
protected
|
688
|
+
|
689
|
+
def create_lifted(&block)
|
690
|
+
self.class.new(@executor, &block)
|
691
|
+
end
|
692
|
+
|
342
693
|
private
|
343
694
|
|
695
|
+
def select_map(predicate, &block)
|
696
|
+
result = select(&predicate)
|
697
|
+
result = result.map(&block) if block_given?
|
698
|
+
|
699
|
+
result
|
700
|
+
end
|
701
|
+
|
702
|
+
def retrieve_executor(executor)
|
703
|
+
return nil if executor.nil?
|
704
|
+
|
705
|
+
stored = EXECUTOR_ALIASES[executor]
|
706
|
+
stored ? stored : executor
|
707
|
+
end
|
708
|
+
|
344
709
|
def lift_behaviour(lifted_tracker_subscription)
|
345
710
|
begin
|
346
711
|
lifted_tracker_subscription.on_open
|