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.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/README.md +932 -11
  4. data/doc/reactive_programming_with_reacto.md +238 -0
  5. data/lib/reacto.rb +70 -0
  6. data/lib/reacto/behaviours.rb +24 -1
  7. data/lib/reacto/constants.rb +4 -1
  8. data/lib/reacto/executors.rb +8 -10
  9. data/lib/reacto/labeled_trackable.rb +14 -2
  10. data/lib/reacto/operations.rb +23 -2
  11. data/lib/reacto/operations/act.rb +69 -0
  12. data/lib/reacto/operations/append.rb +45 -0
  13. data/lib/reacto/operations/blocking_enumerable.rb +40 -0
  14. data/lib/reacto/operations/buffer.rb +1 -4
  15. data/lib/reacto/operations/chunk.rb +81 -0
  16. data/lib/reacto/operations/chunk_while.rb +56 -0
  17. data/lib/reacto/operations/cycle.rb +27 -0
  18. data/lib/reacto/operations/delay_each.rb +75 -0
  19. data/lib/reacto/operations/depend_on.rb +4 -5
  20. data/lib/reacto/operations/diff.rb +8 -10
  21. data/lib/reacto/operations/drop.rb +6 -8
  22. data/lib/reacto/operations/drop_while.rb +23 -0
  23. data/lib/reacto/operations/each_collect.rb +57 -0
  24. data/lib/reacto/operations/each_with_object.rb +31 -0
  25. data/lib/reacto/operations/extremums.rb +54 -0
  26. data/lib/reacto/operations/find_index.rb +28 -0
  27. data/lib/reacto/operations/flat_map.rb +2 -2
  28. data/lib/reacto/operations/flatten.rb +2 -7
  29. data/lib/reacto/operations/flatten_labeled.rb +44 -0
  30. data/lib/reacto/operations/{label.rb → group_by_label.rb} +1 -1
  31. data/lib/reacto/operations/include.rb +40 -0
  32. data/lib/reacto/operations/inject.rb +15 -9
  33. data/lib/reacto/operations/map.rb +15 -13
  34. data/lib/reacto/operations/merge.rb +17 -16
  35. data/lib/reacto/operations/operation_on_labeled.rb +29 -0
  36. data/lib/reacto/operations/partition.rb +52 -0
  37. data/lib/reacto/operations/prepend.rb +0 -3
  38. data/lib/reacto/operations/rescue_and_replace_error.rb +21 -0
  39. data/lib/reacto/operations/retry.rb +26 -0
  40. data/lib/reacto/operations/retry_when.rb +30 -0
  41. data/lib/reacto/operations/select.rb +2 -6
  42. data/lib/reacto/operations/slice.rb +50 -0
  43. data/lib/reacto/operations/slice_when.rb +41 -0
  44. data/lib/reacto/operations/split_labeled.rb +32 -0
  45. data/lib/reacto/operations/take.rb +9 -14
  46. data/lib/reacto/operations/take_while.rb +28 -0
  47. data/lib/reacto/operations/throttle.rb +2 -3
  48. data/lib/reacto/operations/track_on.rb +1 -3
  49. data/lib/reacto/shared_trackable.rb +2 -5
  50. data/lib/reacto/subscriptions/buffered_subscription.rb +10 -9
  51. data/lib/reacto/subscriptions/executor_subscription.rb +12 -4
  52. data/lib/reacto/subscriptions/tracker_subscription.rb +0 -4
  53. data/lib/reacto/subscriptions/zipping_subscription.rb +0 -1
  54. data/lib/reacto/trackable.rb +429 -64
  55. data/lib/reacto/version.rb +1 -1
  56. data/reacto.gemspec +2 -3
  57. data/spec/reacto/labeled_trackable_spec.rb +17 -0
  58. data/spec/reacto/trackable/act_spec.rb +15 -0
  59. data/spec/reacto/trackable/all_spec.rb +38 -0
  60. data/spec/reacto/trackable/any_spec.rb +39 -0
  61. data/spec/reacto/trackable/append_spec.rb +38 -0
  62. data/spec/reacto/trackable/buffer_spec.rb +11 -15
  63. data/spec/reacto/trackable/chunk_spec.rb +86 -0
  64. data/spec/reacto/trackable/chunk_while_spec.rb +22 -0
  65. data/spec/reacto/trackable/class_level/combine_last_spec.rb +1 -3
  66. data/spec/reacto/trackable/class_level/interval_spec.rb +4 -6
  67. data/spec/reacto/trackable/class_level/make_spec.rb +0 -15
  68. data/spec/reacto/trackable/{zip_spec.rb → class_level/zip_spec.rb} +0 -2
  69. data/spec/reacto/trackable/concat_spec.rb +12 -12
  70. data/spec/reacto/trackable/count_spec.rb +38 -0
  71. data/spec/reacto/trackable/cycle_spec.rb +14 -0
  72. data/spec/reacto/trackable/delay_each_spec.rb +18 -0
  73. data/spec/reacto/trackable/depend_on_spec.rb +6 -9
  74. data/spec/reacto/trackable/diff_spec.rb +3 -5
  75. data/spec/reacto/trackable/drop_errors_spec.rb +1 -3
  76. data/spec/reacto/trackable/drop_while_spec.rb +15 -0
  77. data/spec/reacto/trackable/each_cons_spec.rb +53 -0
  78. data/spec/reacto/trackable/each_slice_spec.rb +37 -0
  79. data/spec/reacto/trackable/each_with_index_spec.rb +33 -0
  80. data/spec/reacto/trackable/each_with_object_spec.rb +26 -0
  81. data/spec/reacto/trackable/entries_spec.rb +25 -0
  82. data/spec/reacto/trackable/execute_on_spec.rb +33 -0
  83. data/spec/reacto/trackable/find_index_spec.rb +31 -0
  84. data/spec/reacto/trackable/find_spec.rb +34 -0
  85. data/spec/reacto/trackable/first_spec.rb +36 -0
  86. data/spec/reacto/trackable/flat_map_latest_spec.rb +5 -5
  87. data/spec/reacto/trackable/flat_map_spec.rb +25 -0
  88. data/spec/reacto/trackable/flatten_labeled_spec.rb +48 -0
  89. data/spec/reacto/trackable/grep_spec.rb +29 -0
  90. data/spec/reacto/trackable/grep_v_spec.rb +23 -0
  91. data/spec/reacto/trackable/{label_spec.rb → group_by_label_spec.rb} +4 -11
  92. data/spec/reacto/trackable/include_spec.rb +23 -0
  93. data/spec/reacto/trackable/inject_spec.rb +30 -4
  94. data/spec/reacto/trackable/lift_spec.rb +1 -3
  95. data/spec/reacto/trackable/map_spec.rb +17 -3
  96. data/spec/reacto/trackable/max_by_spec.rb +12 -0
  97. data/spec/reacto/trackable/max_spec.rb +19 -0
  98. data/spec/reacto/trackable/merge_spec.rb +6 -7
  99. data/spec/reacto/trackable/min_by_spec.rb +12 -0
  100. data/spec/reacto/trackable/min_spec.rb +19 -0
  101. data/spec/reacto/trackable/minmax_by_spec.rb +12 -0
  102. data/spec/reacto/trackable/minmax_spec.rb +19 -0
  103. data/spec/reacto/trackable/none_spec.rb +38 -0
  104. data/spec/reacto/trackable/on_spec.rb +11 -4
  105. data/spec/reacto/trackable/one_spec.rb +38 -0
  106. data/spec/reacto/trackable/partition_spec.rb +23 -0
  107. data/spec/reacto/trackable/prepend_spec.rb +1 -3
  108. data/spec/reacto/trackable/reject_spec.rb +21 -0
  109. data/spec/reacto/trackable/rescue_and_replace_error_spec.rb +48 -0
  110. data/spec/reacto/trackable/rescue_and_replace_error_with_spec.rb +26 -0
  111. data/spec/reacto/trackable/retry_spec.rb +50 -0
  112. data/spec/reacto/trackable/retry_when_spec.rb +33 -0
  113. data/spec/reacto/trackable/select_spec.rb +18 -7
  114. data/spec/reacto/trackable/slice_after_spec.rb +38 -0
  115. data/spec/reacto/trackable/slice_before_spec.rb +38 -0
  116. data/spec/reacto/trackable/slice_when_spec.rb +26 -0
  117. data/spec/reacto/trackable/sort_by_spec.rb +16 -0
  118. data/spec/reacto/trackable/sort_spec.rb +23 -0
  119. data/spec/reacto/trackable/split_labeled_spec.rb +37 -0
  120. data/spec/reacto/trackable/take_while_spec.rb +16 -0
  121. data/spec/reacto/trackable/throttle_spec.rb +2 -3
  122. data/spec/reacto/trackable/track_on_spec.rb +2 -3
  123. data/spec/reacto/trackable/uniq_spec.rb +2 -4
  124. data/spec/support/helpers.rb +9 -1
  125. metadata +135 -25
  126. data/Gemfile.lock +0 -32
  127. data/lib/reacto/operations/cache.rb +0 -53
  128. data/spec/reacto/trackable/cache_spec.rb +0 -64
@@ -1,31 +1,35 @@
1
+ require 'concurrent'
1
2
  require 'reacto/subscriptions/operation_subscription'
2
3
 
3
4
  module Reacto
4
5
  module Operations
5
6
  class Merge
6
- def initialize(trackable, delay_error: false)
7
- @trackable = trackable
8
- @close_notifications = 2
7
+ def initialize(trackables, delay_error: false)
8
+ @trackables = trackables
9
+
10
+ @close_notifications =
11
+ Concurrent::AtomicFixnum.new(@trackables.size + 1)
9
12
  @lock = Mutex.new
10
13
  @delay_error = delay_error
11
14
  end
12
15
 
13
16
  def call(tracker)
14
- close = lambda do
17
+ error = nil
18
+
19
+ close = -> () do
15
20
  @lock.synchronize do
16
- @close_notifications -= 1
17
- if @close_notifications == 0
18
- @error.nil? ? tracker.on_close : tracker.on_error(@error)
21
+ if @close_notifications.decrement == 0
22
+ error.nil? ? tracker.on_close : tracker.on_error(error)
19
23
  end
20
24
  end
21
25
  end
26
+
22
27
  err =
23
28
  if @delay_error
24
- lambda do |er|
29
+ -> (er) do
25
30
  @lock.synchronize do
26
- @error = er
27
- @close_notifications -= 1
28
- tracker.on_error(@error) if @close_notifications == 0
31
+ error = er
32
+ tracker.on_error(error) if @close_notifications.decrement == 0
29
33
  end
30
34
  end
31
35
  else
@@ -33,15 +37,12 @@ module Reacto
33
37
  end
34
38
 
35
39
  sub = Subscriptions::OperationSubscription.new(
36
- tracker,
37
- close: close,
38
- error: err
40
+ tracker, close: close, error: err
39
41
  )
40
42
 
41
- @trackable.send(:do_track, sub)
43
+ @trackables.each { |trackable| trackable.send(:do_track, sub) }
42
44
  sub
43
45
  end
44
46
  end
45
47
  end
46
48
  end
47
-
@@ -0,0 +1,29 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class OperationOnLabeled
6
+ def initialize(label, action, op: :map, **args)
7
+ @op = op
8
+ @action = action
9
+ @label = label
10
+ @args = args
11
+ end
12
+
13
+ def call(tracker)
14
+ value = -> (v) do
15
+ to_emit =
16
+ if v.is_a?(LabeledTrackable) && v.label == @label
17
+ v.send(@op, **@args, &@action)
18
+ else
19
+ v
20
+ end
21
+
22
+ tracker.on_value(to_emit)
23
+ end
24
+
25
+ Subscriptions::OperationSubscription.new(tracker, value: value)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,52 @@
1
+ require 'reacto/behaviours'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class Partition
7
+ def initialize(predicate, executor: nil)
8
+ @predicate = predicate
9
+ @executor = executor
10
+ end
11
+
12
+ def call(tracker)
13
+ true_array = []
14
+ false_array = []
15
+
16
+ behavior = -> (val) do
17
+ if @predicate.call(val)
18
+ true_array << val
19
+ else
20
+ false_array << val
21
+ end
22
+ end
23
+
24
+ error = -> (e) do
25
+ emit_trackables(tracker, true_array, false_array)
26
+ tracker.on_error(e)
27
+ end
28
+
29
+ close = -> () do
30
+ emit_trackables(tracker, true_array, false_array)
31
+ tracker.on_close
32
+ end
33
+
34
+ Subscriptions::OperationSubscription.new(
35
+ tracker, value: behavior, error: error, close: close
36
+ )
37
+ end
38
+
39
+ def emit_trackables(tracker, true_array, false_array)
40
+ true_trackable = LabeledTrackable.new(
41
+ true, @executor, &Behaviours.enumerable(true_array)
42
+ )
43
+ false_trackable = LabeledTrackable.new(
44
+ false, @executor, &Behaviours.enumerable(false_array)
45
+ )
46
+
47
+ tracker.on_value(true_trackable)
48
+ tracker.on_value(false_trackable)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,5 +1,3 @@
1
- require 'reacto/subscriptions/operation_subscription'
2
-
3
1
  module Reacto
4
2
  module Operations
5
3
  class Prepend
@@ -16,4 +14,3 @@ module Reacto
16
14
  end
17
15
  end
18
16
  end
19
-
@@ -0,0 +1,21 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class RescueAndReplaceError
6
+ def initialize(action)
7
+ @action = action
8
+ end
9
+
10
+ def call(tracker)
11
+ error = -> (e) do
12
+ trackable = @action.call(e)
13
+
14
+ trackable.send(:do_track, tracker)
15
+ end
16
+
17
+ Subscriptions::OperationSubscription.new(tracker, error: error)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Retry
6
+ def initialize(behaviour, retries = 1)
7
+ @behaviour = behaviour
8
+ @retries = retries
9
+ end
10
+
11
+ def call(tracker)
12
+ error = ->(e) do
13
+ @retries = @retries - 1
14
+
15
+ if @retries < 0
16
+ tracker.on_error(e)
17
+ else
18
+ @behaviour.call(self.call(tracker))
19
+ end
20
+ end
21
+
22
+ Subscriptions::OperationSubscription.new(tracker, error: error)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class RetryWhen
6
+ def initialize(behaviour, predicate)
7
+ @behaviour = behaviour
8
+ @predicate = predicate
9
+ @retries = {}
10
+ end
11
+
12
+ def call(tracker)
13
+ @retries[tracker] ||= 0
14
+
15
+ error = -> (e) do
16
+ should_retry = @predicate.call(e, @retries[tracker])
17
+
18
+ if should_retry
19
+ @retries[tracker] += 1
20
+ @behaviour.call(self.call(tracker))
21
+ else
22
+ tracker.on_error(e)
23
+ end
24
+ end
25
+
26
+ Subscriptions::OperationSubscription.new(tracker, error: error)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -8,18 +8,14 @@ module Reacto
8
8
  end
9
9
 
10
10
  def call(tracker)
11
- on_value = lambda do |v|
11
+ behaviour = -> (v) do
12
12
  if @filter.call(v)
13
13
  tracker.on_value(v)
14
14
  end
15
15
  end
16
16
 
17
- Subscriptions::OperationSubscription.new(
18
- tracker,
19
- value: on_value
20
- )
17
+ Subscriptions::OperationSubscription.new(tracker, value: behaviour)
21
18
  end
22
19
  end
23
20
  end
24
21
  end
25
-
@@ -0,0 +1,50 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Slice
6
+ TYPES = %i(before after)
7
+
8
+ def initialize(predicate, type: :after)
9
+ unless TYPES.include?(type)
10
+ raise ArgumentError.new(
11
+ "Type #{type} not supported. " \
12
+ "Supported types are #{TYPES.join(', ')}"
13
+ )
14
+ end
15
+
16
+ @type = type
17
+ @predicate = predicate
18
+ end
19
+
20
+ def call(tracker)
21
+ buffer = []
22
+
23
+ behaviour = -> (val) do
24
+ buffer << val if @type == :after
25
+
26
+ if @predicate.call(val)
27
+ tracker.on_value(Trackable.enumerable(buffer))
28
+ buffer = []
29
+ end
30
+
31
+ buffer << val if @type == :before
32
+ end
33
+
34
+ error = -> (e) do
35
+ tracker.on_value(Trackable.enumerable(buffer)) unless buffer.empty?
36
+ tracker.on_error(e)
37
+ end
38
+
39
+ close = -> () do
40
+ tracker.on_value(Trackable.enumerable(buffer)) unless buffer.empty?
41
+ tracker.on_close
42
+ end
43
+
44
+ Subscriptions::OperationSubscription.new(
45
+ tracker, value: behaviour, error: error, close: close
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,41 @@
1
+ require 'reacto/constants'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class SliceWhen
7
+ def initialize(predicate)
8
+ @predicate = predicate
9
+ end
10
+
11
+ def call(tracker)
12
+ previous = NO_VALUE
13
+ buffer = []
14
+
15
+ behaviour = -> (val) do
16
+ if previous != NO_VALUE && @predicate.call(previous, val)
17
+ tracker.on_value(Trackable.enumerable(buffer))
18
+ buffer = []
19
+ end
20
+
21
+ buffer << val
22
+ previous = val
23
+ end
24
+
25
+ error = -> (e) do
26
+ tracker.on_value(Trackable.enumerable(buffer)) unless buffer.empty?
27
+ tracker.on_error(e)
28
+ end
29
+
30
+ close = -> () do
31
+ tracker.on_value(Trackable.enumerable(buffer)) unless buffer.empty?
32
+ tracker.on_close
33
+ end
34
+
35
+ Subscriptions::OperationSubscription.new(
36
+ tracker, value: behaviour, error: error, close: close
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class SplitLabeled
6
+ def initialize(label, chose_label, executor = nil)
7
+ @label = label
8
+ @chose_label = chose_label
9
+ @executor = executor
10
+ end
11
+
12
+ def call(tracker)
13
+ value = -> (v) do
14
+ if v.is_a?(LabeledTrackable) && v.label == @label
15
+ action = -> (labeled_trackable) do
16
+ tracker.on_value(labeled_trackable)
17
+ end
18
+
19
+ v.group_by_label(executor: @executor, &@chose_label)
20
+ .on(value: action)
21
+ else
22
+ tracker.on_value(v)
23
+ end
24
+ end
25
+
26
+ Subscriptions::OperationSubscription.new(
27
+ tracker, value: value
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
@@ -9,30 +9,25 @@ module Reacto
9
9
  end
10
10
 
11
11
  @how_many_to_take = how_many_to_take
12
- @taken = 0
13
- @closed = false
14
12
  end
15
13
 
16
14
  def call(tracker)
17
- behaviour = lambda do |value|
18
- return if @closed
19
- if @taken < @how_many_to_take
15
+ taken = 0
16
+ closed = false
17
+
18
+ behaviour = -> (value) do
19
+ return if closed
20
+ if taken < @how_many_to_take
20
21
  tracker.on_value(value)
21
- @taken += 1
22
+ taken += 1
22
23
  else
23
- @closed = true
24
+ closed = true
24
25
  tracker.on_close
25
26
  end
26
27
  end
27
28
 
28
- Subscriptions::OperationSubscription.new(
29
- tracker,
30
- value: behaviour
31
- )
29
+ Subscriptions::OperationSubscription.new(tracker, value: behaviour)
32
30
  end
33
31
  end
34
32
  end
35
33
  end
36
-
37
-
38
-
@@ -0,0 +1,28 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class TakeWhile
6
+ def initialize(predicate)
7
+ @predicate = predicate
8
+ end
9
+
10
+ def call(tracker)
11
+ closed = false
12
+
13
+ behaviour = -> (value) do
14
+ return if closed
15
+
16
+ if @predicate.call(value)
17
+ tracker.on_value(value)
18
+ else
19
+ closed = true
20
+ tracker.on_close
21
+ end
22
+ end
23
+
24
+ Subscriptions::OperationSubscription.new(tracker, value: behaviour)
25
+ end
26
+ end
27
+ end
28
+ end