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
@@ -0,0 +1,69 @@
1
+ require 'ostruct'
2
+
3
+ require 'reacto/constants'
4
+ require 'reacto/subscriptions/operation_subscription'
5
+
6
+ module Reacto
7
+ module Operations
8
+ class Act
9
+ ALL = %i(value error close)
10
+
11
+
12
+ def initialize(action = NO_ACTION, on = ALL)
13
+ @action = action
14
+ @on = on
15
+
16
+ @on = ALL if @on == :all
17
+ end
18
+
19
+ def call(tracker)
20
+ value =
21
+ if @on.include?(:value)
22
+ value_action(tracker)
23
+ else
24
+ tracker.method(:on_value)
25
+ end
26
+
27
+ error =
28
+ if @on.include?(:error)
29
+ error_action(tracker)
30
+ else
31
+ tracker.method(:on_error)
32
+ end
33
+
34
+ close =
35
+ if @on.include?(:close)
36
+ close_action(tracker)
37
+ else
38
+ tracker.method(:on_close)
39
+ end
40
+
41
+ Subscriptions::OperationSubscription.new(
42
+ tracker, value: value, error: error, close: close
43
+ )
44
+ end
45
+
46
+ def value_action(tracker)
47
+ lambda do |value|
48
+ @action.call(OpenStruct.new(value: value, type: :value))
49
+ tracker.on_value(value)
50
+ end
51
+ end
52
+
53
+ def error_action(tracker)
54
+ lambda do |error|
55
+ @action.call(OpenStruct.new(error: error, type: :error))
56
+ tracker.on_error(error)
57
+ end
58
+ end
59
+
60
+ def close_action(tracker)
61
+ lambda do
62
+ @action.call(OpenStruct.new(type: :close))
63
+ tracker.on_close
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+
@@ -0,0 +1,45 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Append
6
+ attr_reader :to_append
7
+
8
+ def initialize(to_append, condition: nil)
9
+ @to_append = to_append
10
+ @condition = condition
11
+ end
12
+
13
+ def call(tracker)
14
+ empty = true
15
+
16
+ on_value =
17
+ if @condition == :source_empty
18
+ -> (v) do
19
+ empty = false if empty
20
+ tracker.on_value(v)
21
+ end
22
+ else
23
+ tracker.method(:on_value)
24
+ end
25
+
26
+ on_close = -> () do
27
+ if (@condition == :source_empty && empty) || @condition.nil?
28
+ if to_append.respond_to? :each
29
+ to_append.each { |v| tracker.on_value(v) }
30
+ else
31
+ tracker.on_value(to_append)
32
+ end
33
+ end
34
+
35
+ tracker.on_close
36
+ end
37
+
38
+ Subscriptions::OperationSubscription.new(
39
+ tracker, close: on_close, value: on_value
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,40 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class BlockingEnumerable
6
+ def initialize(method_name, block)
7
+ @method_name = method_name
8
+ @block = block
9
+ end
10
+
11
+ def call(tracker)
12
+ data = []
13
+
14
+ value = -> (val) { data << val }
15
+ close = -> do
16
+ emit(tracker, data)
17
+ tracker.on_close
18
+ end
19
+ error = ->(e) do
20
+ emit(tracker, data)
21
+ tracker.on_error(e)
22
+ end
23
+
24
+ Subscriptions::OperationSubscription.new(
25
+ tracker, value: value, error: error, close: close
26
+ )
27
+ end
28
+
29
+ def emit(tracker, data)
30
+ result = data.send(@method_name, &@block)
31
+
32
+ if result.is_a?(Enumerable)
33
+ result.each { |value| tracker.on_value(value) }
34
+ else
35
+ tracker.on_value(result)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -30,10 +30,7 @@ module Reacto
30
30
  end
31
31
 
32
32
  Subscriptions::OperationSubscription.new(
33
- tracker,
34
- value: value,
35
- close: close,
36
- error: error
33
+ tracker, value: value, close: close, error: error
37
34
  )
38
35
  end
39
36
 
@@ -0,0 +1,81 @@
1
+ require 'reacto/constants'
2
+ require 'reacto/behaviours'
3
+ require 'reacto/subscriptions/operation_subscription'
4
+
5
+ module Reacto
6
+ module Operations
7
+ class Chunk
8
+ def initialize(func, executor: nil)
9
+ @func = func
10
+ @executor = executor
11
+ end
12
+
13
+ def call(tracker)
14
+ @current_key = NO_VALUE
15
+ @current_data = []
16
+
17
+ value = ->(v) do
18
+ key = @func.call(v)
19
+
20
+ if key == nil || key == :_separator
21
+ flush_current!(tracker)
22
+ return
23
+ end
24
+
25
+ if key == :_alone
26
+ flush_current!(tracker)
27
+ tracker.on_value(LabeledTrackable.new(
28
+ key, @executor, &Behaviours.single_value(v)
29
+ ))
30
+
31
+ return
32
+ end
33
+
34
+ if key.to_s.start_with?('_')
35
+ flush_current!(tracker)
36
+ tracker.on_error(RuntimeError.new(
37
+ 'symbols beginning with an underscore are reserved'
38
+ ))
39
+ return
40
+ end
41
+
42
+ if @current_key == NO_VALUE || @current_key == key
43
+ @current_key = key
44
+ @current_data << v
45
+ return
46
+ end
47
+
48
+ flush_current!(tracker)
49
+
50
+ @current_key = key
51
+ @current_data = [v]
52
+ end
53
+
54
+ error = ->(e) do
55
+ flush_current!(tracker)
56
+ tracker.on_error(e)
57
+ end
58
+
59
+ close = ->() do
60
+ flush_current!(tracker)
61
+ tracker.on_close
62
+ end
63
+
64
+ Subscriptions::OperationSubscription.new(
65
+ tracker, value: value, close: close, error: error
66
+ )
67
+ end
68
+
69
+ def flush_current!(tracker)
70
+ if @current_key != NO_VALUE
71
+ tracker.on_value(LabeledTrackable.new(
72
+ @current_key, @executor, &Behaviours.enumerable(@current_data)
73
+ ))
74
+ end
75
+
76
+ @current_key = NO_VALUE
77
+ @current_data = []
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,56 @@
1
+ require 'reacto/constants'
2
+ require 'reacto/subscriptions/operation_subscription'
3
+
4
+ module Reacto
5
+ module Operations
6
+ class ChunkWhile
7
+ def initialize(func, executor: nil)
8
+ @func = func
9
+ @executor = executor
10
+ end
11
+
12
+ def call(tracker)
13
+ current_data = []
14
+ prev_value = NO_VALUE
15
+
16
+ value = ->(v) do
17
+ if prev_value == NO_VALUE
18
+ prev_value = v
19
+ current_data << v
20
+
21
+ return
22
+ end
23
+
24
+ should_continue = @func.call(prev_value, v)
25
+ prev_value = v
26
+
27
+ unless should_continue
28
+ flush_current(tracker, current_data)
29
+ current_data = []
30
+ end
31
+ current_data << v
32
+ end
33
+
34
+ error = ->(e) do
35
+ flush_current(tracker, current_data)
36
+ tracker.on_error(e)
37
+ end
38
+
39
+ close = ->() do
40
+ flush_current(tracker, current_data)
41
+ tracker.on_close
42
+ end
43
+
44
+ Subscriptions::OperationSubscription.new(
45
+ tracker, value: value, close: close, error: error
46
+ )
47
+ end
48
+
49
+ def flush_current(tracker, current_data)
50
+ tracker.on_value(
51
+ Trackable.enumerable(current_data, executor: @executor)
52
+ )
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,27 @@
1
+ require 'reacto/subscriptions/operation_subscription'
2
+
3
+ module Reacto
4
+ module Operations
5
+ class Cycle
6
+ def initialize(behaviour, n = nil)
7
+ @behaviour = behaviour
8
+ @n = n
9
+ end
10
+
11
+ def call(tracker)
12
+
13
+ close = -> do
14
+ if @n.nil? || @n > 1
15
+ next_n = @n.nil? ? @n : @n - 1
16
+ @behaviour.call(self.class.new(@behaviour, next_n).call(tracker))
17
+ else
18
+ tracker.on_close
19
+ end
20
+ end
21
+
22
+ Subscriptions::OperationSubscription.new(tracker, close: close)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,75 @@
1
+ require 'ostruct'
2
+ require 'concurrent'
3
+
4
+ require 'reacto/subscriptions/operation_subscription'
5
+
6
+ module Reacto
7
+ module Operations
8
+ class DelayEach
9
+ class TaskObserver
10
+ def initialize(tracker)
11
+ @tracker = tracker
12
+ end
13
+ def update(time, result, e)
14
+ if e
15
+ @tracker.on_error(e) unless e.is_a?(Concurrent::TimeoutError)
16
+ end
17
+ end
18
+ end
19
+
20
+ def initialize(delay)
21
+ @delay = delay
22
+ @queue = []
23
+ end
24
+
25
+ def call(tracker)
26
+ close = lambda do
27
+ @queue << OpenStruct.new(type: :close)
28
+ delay_task(tracker)
29
+ end
30
+
31
+ error = lambda do |e|
32
+ @queue << OpenStruct.new(error: e, type: :error)
33
+ delay_task(tracker)
34
+ end
35
+
36
+ value = lambda do |v|
37
+ @queue << OpenStruct.new(value: v, type: :value)
38
+ delay_task(tracker)
39
+ end
40
+
41
+ Subscriptions::OperationSubscription.new(
42
+ tracker,
43
+ value: value,
44
+ close: close,
45
+ error: error
46
+ )
47
+ end
48
+
49
+ private
50
+
51
+ def delay_task(tracker)
52
+ return if @task
53
+
54
+ @task = Concurrent::TimerTask.new(execution_interval: @delay) do
55
+ notification = @queue.shift
56
+
57
+ return unless notification
58
+
59
+ if notification.type == :value
60
+ tracker.on_value(notification.value)
61
+ elsif notification.type == :error
62
+ tracker.on_error(notification.error)
63
+ @task.shutdown
64
+ else
65
+ tracker.on_close
66
+ @task.shutdown
67
+ end
68
+ end
69
+ @task.add_observer(TaskObserver.new(tracker))
70
+
71
+ @task.execute
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,3 +1,5 @@
1
+ require 'ostruct'
2
+
1
3
  require 'reacto/constants'
2
4
  require 'reacto/subscriptions/operation_subscription'
3
5
 
@@ -10,7 +12,7 @@ module Reacto
10
12
  if accumulator.nil?
11
13
  trackable.first
12
14
  else
13
- trackable.inject(NO_VALUE, accumulator)
15
+ trackable.inject(NO_VALUE, &accumulator)
14
16
  end
15
17
 
16
18
  @lock = Mutex.new
@@ -92,10 +94,7 @@ module Reacto
92
94
  end
93
95
 
94
96
  Subscriptions::OperationSubscription.new(
95
- tracker,
96
- value: value,
97
- error: error,
98
- close: close
97
+ tracker, value: value, error: error, close: close
99
98
  )
100
99
  end
101
100
  end
@@ -8,27 +8,25 @@ module Reacto
8
8
 
9
9
  def initialize(fn = DEFAULT_FN, initial = NO_VALUE)
10
10
  @fn = fn
11
- @prev = initial
11
+ @initial = initial
12
12
  end
13
13
 
14
14
  def call(tracker)
15
- value = lambda do |v|
16
- if @prev == NO_VALUE
17
- @prev = v
15
+ prev = @initial
16
+ value = -> (v) do
17
+ if prev == NO_VALUE
18
+ prev = v
18
19
  return
19
20
  end
20
21
 
21
- current = @fn.call(@prev, v)
22
- @prev = v
22
+ current = @fn.call(prev, v)
23
+ prev = v
23
24
 
24
25
  tracker.on_value(current)
25
26
  end
26
27
 
27
28
 
28
- Subscriptions::OperationSubscription.new(
29
- tracker,
30
- value: value
31
- )
29
+ Subscriptions::OperationSubscription.new(tracker, value: value)
32
30
  end
33
31
  end
34
32
  end