rx_ruby 0.0.2

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 (186) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +22 -0
  3. data/.gitignore +173 -0
  4. data/.travis.yml +10 -0
  5. data/Gemfile +4 -0
  6. data/Rakefile +11 -0
  7. data/examples/aggregate.rb +39 -0
  8. data/examples/amb.rb +25 -0
  9. data/examples/ambproto.rb +24 -0
  10. data/examples/and.rb +26 -0
  11. data/examples/as_observable.rb +25 -0
  12. data/examples/average.rb +43 -0
  13. data/examples/buffer_with_count.rb +44 -0
  14. data/examples/buffer_with_time.rb +51 -0
  15. data/examples/case.rb +29 -0
  16. data/examples/catch.rb +20 -0
  17. data/examples/catchproto.rb +39 -0
  18. data/examples/combine_latest.rb +35 -0
  19. data/examples/combine_latestproto.rb +33 -0
  20. data/examples/concat.rb +22 -0
  21. data/examples/concat_all.rb +27 -0
  22. data/examples/concat_map.rb +61 -0
  23. data/examples/concat_map_observer.rb +29 -0
  24. data/examples/concatproto.rb +25 -0
  25. data/examples/connect.rb +41 -0
  26. data/examples/contains.rb +37 -0
  27. data/examples/count.rb +36 -0
  28. data/examples/create.rb +55 -0
  29. data/examples/debounce.rb +35 -0
  30. data/examples/default_if_empty.rb +35 -0
  31. data/examples/defer.rb +20 -0
  32. data/examples/delay.rb +49 -0
  33. data/examples/delay_with_selector.rb +63 -0
  34. data/examples/dematerialize.rb +22 -0
  35. data/examples/disposable.rb +12 -0
  36. data/examples/distinct.rb +43 -0
  37. data/examples/distinct_until_changed.rb +43 -0
  38. data/examples/do.rb +59 -0
  39. data/examples/empty.rb +16 -0
  40. data/examples/for.rb +26 -0
  41. data/examples/fork_join.rb +23 -0
  42. data/examples/from.rb +106 -0
  43. data/examples/from_array.rb +21 -0
  44. data/examples/from_callback.rb +21 -0
  45. data/examples/generate.rb +24 -0
  46. data/examples/group_join.rb +39 -0
  47. data/examples/if.rb +46 -0
  48. data/examples/intervals.rb +26 -0
  49. data/examples/merge.rb +36 -0
  50. data/examples/merge_all.rb +27 -0
  51. data/examples/multicast.rb +32 -0
  52. data/examples/never.rb +15 -0
  53. data/examples/of.rb +19 -0
  54. data/examples/on_error_resume_next.rb +21 -0
  55. data/examples/pairs.rb +26 -0
  56. data/examples/publish.rb +79 -0
  57. data/examples/range.rb +19 -0
  58. data/examples/reduce.rb +18 -0
  59. data/examples/repeat.rb +19 -0
  60. data/examples/return.rb +17 -0
  61. data/examples/scan.rb +41 -0
  62. data/examples/start.rb +29 -0
  63. data/examples/throw.rb +17 -0
  64. data/examples/time_intervals.rb +28 -0
  65. data/examples/timer.rb +26 -0
  66. data/examples/timestamp.rb +28 -0
  67. data/examples/to_a.rb +23 -0
  68. data/examples/to_async.rb +26 -0
  69. data/examples/using.rb +52 -0
  70. data/examples/when.rb +26 -0
  71. data/examples/while.rb +25 -0
  72. data/examples/window_with_time.rb +78 -0
  73. data/examples/zip.rb +27 -0
  74. data/examples/zip_array.rb +25 -0
  75. data/lib/core_ext/enumerable.rb +22 -0
  76. data/lib/rx_ruby.rb +27 -0
  77. data/lib/rx_ruby/concurrency/async_lock.rb +57 -0
  78. data/lib/rx_ruby/concurrency/current_thread_scheduler.rb +75 -0
  79. data/lib/rx_ruby/concurrency/default_scheduler.rb +51 -0
  80. data/lib/rx_ruby/concurrency/historical_scheduler.rb +16 -0
  81. data/lib/rx_ruby/concurrency/immediate_scheduler.rb +68 -0
  82. data/lib/rx_ruby/concurrency/local_scheduler.rb +39 -0
  83. data/lib/rx_ruby/concurrency/periodic_scheduler.rb +74 -0
  84. data/lib/rx_ruby/concurrency/scheduled_item.rb +42 -0
  85. data/lib/rx_ruby/concurrency/scheduler.rb +150 -0
  86. data/lib/rx_ruby/concurrency/virtual_time_scheduler.rb +170 -0
  87. data/lib/rx_ruby/core/async_lock_observer.rb +46 -0
  88. data/lib/rx_ruby/core/auto_detach_observer.rb +59 -0
  89. data/lib/rx_ruby/core/checked_observer.rb +66 -0
  90. data/lib/rx_ruby/core/notification.rb +161 -0
  91. data/lib/rx_ruby/core/observable.rb +104 -0
  92. data/lib/rx_ruby/core/observe_on_observer.rb +50 -0
  93. data/lib/rx_ruby/core/observer.rb +119 -0
  94. data/lib/rx_ruby/core/scheduled_observer.rb +83 -0
  95. data/lib/rx_ruby/core/synchronized_observer.rb +47 -0
  96. data/lib/rx_ruby/core/time_interval.rb +17 -0
  97. data/lib/rx_ruby/internal/priority_queue.rb +122 -0
  98. data/lib/rx_ruby/internal/util.rb +9 -0
  99. data/lib/rx_ruby/joins/active_plan.rb +45 -0
  100. data/lib/rx_ruby/joins/join_observer.rb +51 -0
  101. data/lib/rx_ruby/joins/pattern.rb +14 -0
  102. data/lib/rx_ruby/joins/plan.rb +44 -0
  103. data/lib/rx_ruby/linq/connectable_observable.rb +34 -0
  104. data/lib/rx_ruby/linq/observable/_observable_timer_date_and_period.rb +22 -0
  105. data/lib/rx_ruby/linq/observable/_observable_timer_time_span.rb +14 -0
  106. data/lib/rx_ruby/linq/observable/_observable_timer_time_span_and_period.rb +20 -0
  107. data/lib/rx_ruby/linq/observable/aggregate.rb +7 -0
  108. data/lib/rx_ruby/linq/observable/and.rb +7 -0
  109. data/lib/rx_ruby/linq/observable/case.rb +15 -0
  110. data/lib/rx_ruby/linq/observable/concat_all.rb +7 -0
  111. data/lib/rx_ruby/linq/observable/concat_map.rb +35 -0
  112. data/lib/rx_ruby/linq/observable/concat_map_observer.rb +43 -0
  113. data/lib/rx_ruby/linq/observable/contains.rb +28 -0
  114. data/lib/rx_ruby/linq/observable/debounce.rb +41 -0
  115. data/lib/rx_ruby/linq/observable/delay.rb +81 -0
  116. data/lib/rx_ruby/linq/observable/delay_with_selector.rb +64 -0
  117. data/lib/rx_ruby/linq/observable/do.rb +42 -0
  118. data/lib/rx_ruby/linq/observable/for.rb +13 -0
  119. data/lib/rx_ruby/linq/observable/fork_join.rb +55 -0
  120. data/lib/rx_ruby/linq/observable/from.rb +34 -0
  121. data/lib/rx_ruby/linq/observable/group_join.rb +108 -0
  122. data/lib/rx_ruby/linq/observable/if.rb +17 -0
  123. data/lib/rx_ruby/linq/observable/interval.rb +5 -0
  124. data/lib/rx_ruby/linq/observable/multicast.rb +14 -0
  125. data/lib/rx_ruby/linq/observable/of.rb +11 -0
  126. data/lib/rx_ruby/linq/observable/pairs.rb +7 -0
  127. data/lib/rx_ruby/linq/observable/pluck.rb +7 -0
  128. data/lib/rx_ruby/linq/observable/publish.rb +11 -0
  129. data/lib/rx_ruby/linq/observable/start.rb +7 -0
  130. data/lib/rx_ruby/linq/observable/time_interval.rb +15 -0
  131. data/lib/rx_ruby/linq/observable/timer.rb +26 -0
  132. data/lib/rx_ruby/linq/observable/timestamp.rb +9 -0
  133. data/lib/rx_ruby/linq/observable/to_async.rb +40 -0
  134. data/lib/rx_ruby/linq/observable/when.rb +36 -0
  135. data/lib/rx_ruby/linq/observable/while.rb +41 -0
  136. data/lib/rx_ruby/operators/aggregates.rb +611 -0
  137. data/lib/rx_ruby/operators/creation.rb +220 -0
  138. data/lib/rx_ruby/operators/multiple.rb +735 -0
  139. data/lib/rx_ruby/operators/single.rb +399 -0
  140. data/lib/rx_ruby/operators/standard_query_operators.rb +279 -0
  141. data/lib/rx_ruby/operators/synchronization.rb +47 -0
  142. data/lib/rx_ruby/operators/time.rb +120 -0
  143. data/lib/rx_ruby/subjects/async_subject.rb +161 -0
  144. data/lib/rx_ruby/subjects/behavior_subject.rb +149 -0
  145. data/lib/rx_ruby/subjects/replay_subject.rb +39 -0
  146. data/lib/rx_ruby/subjects/subject.rb +131 -0
  147. data/lib/rx_ruby/subjects/subject_extensions.rb +45 -0
  148. data/lib/rx_ruby/subscriptions/composite_subscription.rb +91 -0
  149. data/lib/rx_ruby/subscriptions/ref_count_subscription.rb +88 -0
  150. data/lib/rx_ruby/subscriptions/scheduled_subscription.rb +32 -0
  151. data/lib/rx_ruby/subscriptions/serial_subscription.rb +60 -0
  152. data/lib/rx_ruby/subscriptions/single_assignment_subscription.rb +64 -0
  153. data/lib/rx_ruby/subscriptions/subscription.rb +56 -0
  154. data/lib/rx_ruby/testing/cold_observable.rb +45 -0
  155. data/lib/rx_ruby/testing/hot_observable.rb +47 -0
  156. data/lib/rx_ruby/testing/mock_observer.rb +33 -0
  157. data/lib/rx_ruby/testing/reactive_test.rb +94 -0
  158. data/lib/rx_ruby/testing/recorded.rb +17 -0
  159. data/lib/rx_ruby/testing/test_scheduler.rb +96 -0
  160. data/lib/rx_ruby/testing/test_subscription.rb +22 -0
  161. data/lib/rx_ruby/version.rb +3 -0
  162. data/license.txt +13 -0
  163. data/readme.md +152 -0
  164. data/rx_ruby.gemspec +22 -0
  165. data/test/rx_ruby/concurrency/helpers/historical_virtual_scheduler_helper.rb +135 -0
  166. data/test/rx_ruby/concurrency/helpers/immediate_local_scheduler_helper.rb +51 -0
  167. data/test/rx_ruby/concurrency/test_async_lock.rb +56 -0
  168. data/test/rx_ruby/concurrency/test_current_thread_scheduler.rb +44 -0
  169. data/test/rx_ruby/concurrency/test_default_scheduler.rb +44 -0
  170. data/test/rx_ruby/concurrency/test_historical_scheduler.rb +18 -0
  171. data/test/rx_ruby/concurrency/test_immediate_scheduler.rb +53 -0
  172. data/test/rx_ruby/concurrency/test_local_scheduler.rb +12 -0
  173. data/test/rx_ruby/concurrency/test_periodic_scheduler.rb +53 -0
  174. data/test/rx_ruby/concurrency/test_scheduled_item.rb +50 -0
  175. data/test/rx_ruby/concurrency/test_scheduler.rb +128 -0
  176. data/test/rx_ruby/concurrency/test_virtual_time_scheduler.rb +14 -0
  177. data/test/rx_ruby/core/test_notification.rb +129 -0
  178. data/test/rx_ruby/core/test_observable_creation.rb +483 -0
  179. data/test/rx_ruby/core/test_observer.rb +634 -0
  180. data/test/rx_ruby/internal/test_priority_queue.rb +71 -0
  181. data/test/rx_ruby/subscriptions/test_composite_subscription.rb +116 -0
  182. data/test/rx_ruby/subscriptions/test_serial_subscription.rb +62 -0
  183. data/test/rx_ruby/subscriptions/test_singleassignment_subscription.rb +61 -0
  184. data/test/rx_ruby/subscriptions/test_subscription.rb +27 -0
  185. data/test/test_helper.rb +11 -0
  186. metadata +291 -0
@@ -0,0 +1,51 @@
1
+ # Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
+
3
+ require 'singleton'
4
+ require 'thread'
5
+ require 'rx_ruby/concurrency/local_scheduler'
6
+ require 'rx_ruby/concurrency/periodic_scheduler'
7
+ require 'rx_ruby/subscriptions/subscription'
8
+ require 'rx_ruby/subscriptions/single_assignment_subscription'
9
+ require 'rx_ruby/subscriptions/composite_subscription'
10
+
11
+ module RxRuby
12
+
13
+ # Represents an object that schedules units of work on the platform's default scheduler.
14
+ class DefaultScheduler < RxRuby::LocalScheduler
15
+
16
+ include Singleton
17
+ include RxRuby::PeriodicScheduler
18
+
19
+ # Schedules an action to be executed.
20
+ def schedule_with_state(state, action)
21
+ raise 'action cannot be nil' unless action
22
+
23
+ d = SingleAssignmentSubscription.new
24
+
25
+ t = Thread.new do
26
+ d.subscription = action.call self, state unless d.unsubscribed?
27
+ end
28
+
29
+ CompositeSubscription.new [d, Subscription.create { t.exit }]
30
+ end
31
+
32
+ # Schedules an action to be executed after dueTime
33
+ def schedule_relative_with_state(state, due_time, action)
34
+ raise 'action cannot be nil' unless action
35
+
36
+ dt = Scheduler.normalize due_time
37
+ return self.schedule_with_state state, action if dt == 0
38
+
39
+ d = SingleAssignmentSubscription.new
40
+
41
+ t = Thread.new do
42
+ sleep dt
43
+ Thread.new {
44
+ d.subscription = action.call self, state unless d.unsubscribed?
45
+ }
46
+ end
47
+
48
+ CompositeSubscription.new [d, Subscription.create { t.exit }]
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,16 @@
1
+ # Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
+
3
+ require 'rx_ruby/concurrency/virtual_time_scheduler'
4
+ require 'rx_ruby/internal/priority_queue'
5
+
6
+ module RxRuby
7
+
8
+ # Provides a virtual time scheduler that uses Time for absolute time and Number for relative time.
9
+ class HistoricalScheduler < VirtualTimeScheduler
10
+
11
+ def initialize(clock = Time.at(0))
12
+ super
13
+ @clock = clock
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,68 @@
1
+ # Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
+
3
+ require 'singleton'
4
+ require 'thread'
5
+ require 'rx_ruby/concurrency/local_scheduler'
6
+ require 'rx_ruby/subscriptions/single_assignment_subscription'
7
+
8
+ module RxRuby
9
+
10
+ # Represents an object that schedules units of work to run immediately on the current thread.
11
+ class ImmediateScheduler < LocalScheduler
12
+
13
+ include Singleton
14
+
15
+ # Schedules an action to be executed.
16
+ def schedule_with_state(state, action)
17
+ raise ArgumentError.new 'action cannot be nil' unless action
18
+ action.call AsyncLockScheduler.new, state
19
+ end
20
+
21
+ def schedule_relative_with_state(state, due_time, action)
22
+ raise ArgumentError.new 'action cannot be nil' unless action
23
+
24
+ dt = RxRuby::Scheduler.normalize due_time
25
+ sleep dt if dt > 0
26
+ action.call AsyncLockScheduler.new, state
27
+ end
28
+
29
+ private
30
+
31
+ class AsyncLockScheduler < LocalScheduler
32
+
33
+ def initialize
34
+ @gate = nil
35
+ end
36
+
37
+ def schedule_with_state(state, action)
38
+ m = SingleAssignmentSubscription.new
39
+
40
+ @gate = AsyncLock.new if @gate.nil?
41
+
42
+ @gate.wait do
43
+ m.subscription = action.call self, state unless m.unsubscribed?
44
+ end
45
+
46
+ m
47
+ end
48
+
49
+ def schedule_relative_with_state(state, due_time, action)
50
+ return self.schedule_with_state state, action if due_time <= 0
51
+
52
+ m = SingleAssignmentSubscription.new
53
+
54
+ timer = Time.new
55
+
56
+ @gate = AsyncLock.new if @gate.nil?
57
+
58
+ @gate.wait do
59
+ sleep_time = Time.new - timer
60
+ sleep sleep_time if sleep_time > 0
61
+ m.subscription = action.call self, state unless m.unsubscribed?
62
+ end
63
+
64
+ m
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ # Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
+
3
+ require 'rx_ruby/concurrency/scheduler'
4
+
5
+ module RxRuby
6
+ # Abstract base class for machine-local schedulers, using the local system clock for time-based operations.
7
+ class LocalScheduler
8
+
9
+ include Scheduler
10
+
11
+ # Gets the scheduler's notion of current time.
12
+ def now
13
+ Time.now
14
+ end
15
+
16
+ # Schedules an action to be executed.
17
+ def schedule_with_state(state, action)
18
+ raise 'action cannot be nil' unless action
19
+
20
+ schedule_relative_with_state(state, 0, action)
21
+ end
22
+
23
+ # Schedules an action to be executed at dueTime.
24
+ def schedule_absolute_with_state(state, due_time, action)
25
+ raise 'action cannot be nil' unless action
26
+
27
+ schedule_relative_with_state(state, (due_time - self.now), action)
28
+ end
29
+
30
+ def schedule_relative_with_state(state, due_time, action)
31
+ raise ArgumentError.new 'action cannot be nil' unless action
32
+
33
+ dt = RxRuby::Scheduler.normalize due_time
34
+ sleep dt if dt > 0
35
+ action.call(self, state)
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,74 @@
1
+ # Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
+
3
+ module RxRuby
4
+
5
+ # Provides periodic scheduling capabilities
6
+ module PeriodicScheduler
7
+
8
+ # Schedules a periodic piece of work by dynamically discovering the scheduler's capabilities.
9
+ def schedule_periodic(period, action)
10
+ raise 'action cannot be nil' unless action
11
+ raise 'period cannot be less than zero' if period < 0
12
+
13
+ self.schedule_periodic_with_state(action, period, lambda {|a|
14
+ a.call
15
+ return a
16
+ })
17
+ end
18
+
19
+ # Schedules a periodic piece of work
20
+ def schedule_periodic_with_state(state, due_time, action)
21
+ raise 'action cannot be nil' unless action
22
+ raise 'due_time cannot be less than zero' if due_time < 0
23
+
24
+ state1 = state
25
+ gate = Mutex.new
26
+
27
+ PeriodicTimer.new due_time do
28
+ gate.synchronize do
29
+ state1 = action.call state1
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ # Internal timer
37
+ class PeriodicTimer
38
+ def initialize(seconds, &action)
39
+ @seconds = seconds
40
+ @unsubscribed = false
41
+ @gate = Mutex.new
42
+
43
+ self.run_loop(&action)
44
+ end
45
+
46
+ def unsubscribe
47
+ @gate.synchronize do
48
+ @unsubscribed = true unless @unsubscribed
49
+ end
50
+ end
51
+
52
+ def time_block
53
+ start_time = Time.new
54
+ yield
55
+ Time.new - start_time
56
+ end
57
+
58
+ def run_loop
59
+ Thread.new do
60
+ should_run = true
61
+
62
+ elapsed = 0
63
+ while should_run
64
+ sleep @seconds - elapsed
65
+ elapsed = time_block { yield }
66
+ @gate.synchronize do
67
+ should_run = !@unsubscribed
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,42 @@
1
+ # Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
+
3
+ require 'rx_ruby/subscriptions/single_assignment_subscription'
4
+
5
+ module RxRuby
6
+
7
+ # Represents a scheduled work item based on the materialization of an scheduler.schedule method call.
8
+ class ScheduledItem
9
+
10
+ include Comparable
11
+
12
+ attr_reader :due_time
13
+
14
+ def initialize(scheduler, state, due_time, &action)
15
+ @scheduler = scheduler
16
+ @state = state
17
+ @action = action
18
+ @due_time = due_time
19
+ @subscription = SingleAssignmentSubscription.new
20
+ end
21
+
22
+ # Gets whether the work item has received a cancellation request.
23
+ def cancelled?
24
+ @subscription.unsubscribed?
25
+ end
26
+
27
+ # Invokes the work item.
28
+ def invoke
29
+ @subscription.subscription = @action.call @scheduler, @state unless @subscription.unsubscribed?
30
+ end
31
+
32
+ def <=>(other)
33
+ return @due_time <=> other.due_time
34
+ end
35
+
36
+ # Cancels the work item by disposing the resource returned by invoke_core as soon as possible.
37
+ def cancel
38
+ @subscription.unsubscribe
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,150 @@
1
+ # Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
2
+
3
+ require 'thread'
4
+
5
+ module RxRuby
6
+
7
+ # Module for scheduling actions
8
+ module Scheduler
9
+
10
+ # Gets the current time according to the local machine's system clock.
11
+ def self.now
12
+ Time.now
13
+ end
14
+
15
+ # Schedules an action to be executed.
16
+ def schedule(action)
17
+ raise 'action cannot be nil' unless action
18
+ schedule_with_state(action, method(:invoke))
19
+ end
20
+
21
+ # Schedules an action to be executed after the specified relative due time.
22
+ def schedule_relative(due_time, action)
23
+ raise 'action cannot be nil' unless action
24
+ schedule_relative_with_state(action, due_time, method(:invoke))
25
+ end
26
+
27
+ # Schedules an action to be executed at the specified absolute due time.
28
+ def schedule_absolute(due_time, action)
29
+ raise 'action cannot be nil' unless action
30
+ schedule_absolute_with_state(action, due_time, method(:invoke))
31
+ end
32
+
33
+ # Schedules an action to be executed recursively.
34
+ def schedule_recursive(action)
35
+ raise 'action cannot be nil' unless action
36
+ schedule_recursive_with_state(action, lambda {|_action, _self| _action.call(lambda { _self.call(_action) }) })
37
+ end
38
+
39
+ # Schedules an action to be executed recursively.
40
+ def schedule_recursive_with_state(state, action)
41
+ raise 'action cannot be nil' unless action
42
+ schedule_with_state({ :state => state, :action => action}, method(:invoke_recursive))
43
+ end
44
+
45
+ # Schedules an action to be executed recursively after a specified relative due time.
46
+ def schedule_recursive_relative(due_time, action)
47
+ raise 'action cannot be nil' unless action
48
+ schedule_recursive_relative_with_state(action, due_time, lambda {|_action, _self| _action.call(lambda {|dt| _self.call(_action, dt) }) })
49
+ end
50
+
51
+ # Schedules an action to be executed recursively after a specified relative due time.
52
+ def schedule_recursive_relative_with_state(state, due_time, action)
53
+ raise 'action cannot be nil' unless action
54
+ schedule_relative_with_state(
55
+ { :state => state, :action => action},
56
+ due_time,
57
+ lambda { |sched, pair| invoke_recursive_time(sched, pair, 'schedule_relative_with_state') }
58
+ )
59
+ end
60
+
61
+ # Schedules an action to be executed recursively after a specified absolute due time.
62
+ def schedule_recursive_absolute(due_time, action)
63
+ raise 'action cannot be nil' unless action
64
+ schedule_recursive_absolute_with_state(action, due_time, lambda {|_action, _self| _action.call(lambda {|dt| _self.call(_action, dt) }) })
65
+ end
66
+
67
+ # Schedules an action to be executed recursively after a specified absolute due time.
68
+ def schedule_recursive_absolute_with_state(state, due_time, action)
69
+ raise 'action cannot be nil' unless action
70
+ schedule_absolute_with_state(
71
+ { :state => state, :action => action},
72
+ due_time,
73
+ lambda { |sched, pair| invoke_recursive_time(sched, pair, 'schedule_absolute_with_state') }
74
+ )
75
+ end
76
+
77
+ # Normalizes the specified TimeSpan value to a positive value.
78
+ def self.normalize(time_span)
79
+ time_span < 0 ? 0 : time_span
80
+ end
81
+
82
+ private
83
+
84
+ def invoke(scheduler, action)
85
+ action.call()
86
+ Subscription.empty
87
+ end
88
+
89
+ def invoke_recursive(scheduler, pair)
90
+ group = CompositeSubscription.new
91
+ gate = Mutex.new
92
+ state = pair[:state]
93
+ action = pair[:action]
94
+
95
+ recursive_action = lambda {|state1|
96
+ action.call(state1, lambda {|state2|
97
+ is_added = false
98
+ is_done = false
99
+ d = nil
100
+ d = scheduler.schedule_with_state(state2, lambda do |_, state3|
101
+ gate.synchronize do
102
+ if is_added
103
+ group.delete(d)
104
+ else
105
+ is_done = true
106
+ end
107
+ end
108
+
109
+ recursive_action.call(state3)
110
+ Subscription.empty
111
+ end)
112
+
113
+ gate.synchronize do
114
+ unless is_done
115
+ group.push(d)
116
+ is_added = true
117
+ end
118
+ end
119
+ })
120
+ }
121
+
122
+ recursive_action.call(state)
123
+ group
124
+ end
125
+
126
+ def invoke_recursive_time(scheduler, pair, method)
127
+ group = CompositeSubscription.new
128
+ gate = Mutex.new
129
+ state = pair[:state]
130
+ action = pair[:action]
131
+
132
+ recursive_action = ->(state1) do
133
+ internal_action = ->(state2, due_time1) do
134
+
135
+ d = scheduler.send(method, state2, due_time1, lambda do |_, state3|
136
+ gate.synchronize do
137
+ group.delete(d)
138
+ end
139
+ recursive_action.call(state3)
140
+ Subscription.empty
141
+ end)
142
+ end
143
+ action.call(state1, internal_action)
144
+ end
145
+
146
+ recursive_action.call(state)
147
+ group
148
+ end
149
+ end
150
+ end