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.
- checksums.yaml +7 -0
- data/.gitattributes +22 -0
- data/.gitignore +173 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/Rakefile +11 -0
- data/examples/aggregate.rb +39 -0
- data/examples/amb.rb +25 -0
- data/examples/ambproto.rb +24 -0
- data/examples/and.rb +26 -0
- data/examples/as_observable.rb +25 -0
- data/examples/average.rb +43 -0
- data/examples/buffer_with_count.rb +44 -0
- data/examples/buffer_with_time.rb +51 -0
- data/examples/case.rb +29 -0
- data/examples/catch.rb +20 -0
- data/examples/catchproto.rb +39 -0
- data/examples/combine_latest.rb +35 -0
- data/examples/combine_latestproto.rb +33 -0
- data/examples/concat.rb +22 -0
- data/examples/concat_all.rb +27 -0
- data/examples/concat_map.rb +61 -0
- data/examples/concat_map_observer.rb +29 -0
- data/examples/concatproto.rb +25 -0
- data/examples/connect.rb +41 -0
- data/examples/contains.rb +37 -0
- data/examples/count.rb +36 -0
- data/examples/create.rb +55 -0
- data/examples/debounce.rb +35 -0
- data/examples/default_if_empty.rb +35 -0
- data/examples/defer.rb +20 -0
- data/examples/delay.rb +49 -0
- data/examples/delay_with_selector.rb +63 -0
- data/examples/dematerialize.rb +22 -0
- data/examples/disposable.rb +12 -0
- data/examples/distinct.rb +43 -0
- data/examples/distinct_until_changed.rb +43 -0
- data/examples/do.rb +59 -0
- data/examples/empty.rb +16 -0
- data/examples/for.rb +26 -0
- data/examples/fork_join.rb +23 -0
- data/examples/from.rb +106 -0
- data/examples/from_array.rb +21 -0
- data/examples/from_callback.rb +21 -0
- data/examples/generate.rb +24 -0
- data/examples/group_join.rb +39 -0
- data/examples/if.rb +46 -0
- data/examples/intervals.rb +26 -0
- data/examples/merge.rb +36 -0
- data/examples/merge_all.rb +27 -0
- data/examples/multicast.rb +32 -0
- data/examples/never.rb +15 -0
- data/examples/of.rb +19 -0
- data/examples/on_error_resume_next.rb +21 -0
- data/examples/pairs.rb +26 -0
- data/examples/publish.rb +79 -0
- data/examples/range.rb +19 -0
- data/examples/reduce.rb +18 -0
- data/examples/repeat.rb +19 -0
- data/examples/return.rb +17 -0
- data/examples/scan.rb +41 -0
- data/examples/start.rb +29 -0
- data/examples/throw.rb +17 -0
- data/examples/time_intervals.rb +28 -0
- data/examples/timer.rb +26 -0
- data/examples/timestamp.rb +28 -0
- data/examples/to_a.rb +23 -0
- data/examples/to_async.rb +26 -0
- data/examples/using.rb +52 -0
- data/examples/when.rb +26 -0
- data/examples/while.rb +25 -0
- data/examples/window_with_time.rb +78 -0
- data/examples/zip.rb +27 -0
- data/examples/zip_array.rb +25 -0
- data/lib/core_ext/enumerable.rb +22 -0
- data/lib/rx_ruby.rb +27 -0
- data/lib/rx_ruby/concurrency/async_lock.rb +57 -0
- data/lib/rx_ruby/concurrency/current_thread_scheduler.rb +75 -0
- data/lib/rx_ruby/concurrency/default_scheduler.rb +51 -0
- data/lib/rx_ruby/concurrency/historical_scheduler.rb +16 -0
- data/lib/rx_ruby/concurrency/immediate_scheduler.rb +68 -0
- data/lib/rx_ruby/concurrency/local_scheduler.rb +39 -0
- data/lib/rx_ruby/concurrency/periodic_scheduler.rb +74 -0
- data/lib/rx_ruby/concurrency/scheduled_item.rb +42 -0
- data/lib/rx_ruby/concurrency/scheduler.rb +150 -0
- data/lib/rx_ruby/concurrency/virtual_time_scheduler.rb +170 -0
- data/lib/rx_ruby/core/async_lock_observer.rb +46 -0
- data/lib/rx_ruby/core/auto_detach_observer.rb +59 -0
- data/lib/rx_ruby/core/checked_observer.rb +66 -0
- data/lib/rx_ruby/core/notification.rb +161 -0
- data/lib/rx_ruby/core/observable.rb +104 -0
- data/lib/rx_ruby/core/observe_on_observer.rb +50 -0
- data/lib/rx_ruby/core/observer.rb +119 -0
- data/lib/rx_ruby/core/scheduled_observer.rb +83 -0
- data/lib/rx_ruby/core/synchronized_observer.rb +47 -0
- data/lib/rx_ruby/core/time_interval.rb +17 -0
- data/lib/rx_ruby/internal/priority_queue.rb +122 -0
- data/lib/rx_ruby/internal/util.rb +9 -0
- data/lib/rx_ruby/joins/active_plan.rb +45 -0
- data/lib/rx_ruby/joins/join_observer.rb +51 -0
- data/lib/rx_ruby/joins/pattern.rb +14 -0
- data/lib/rx_ruby/joins/plan.rb +44 -0
- data/lib/rx_ruby/linq/connectable_observable.rb +34 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_date_and_period.rb +22 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_time_span.rb +14 -0
- data/lib/rx_ruby/linq/observable/_observable_timer_time_span_and_period.rb +20 -0
- data/lib/rx_ruby/linq/observable/aggregate.rb +7 -0
- data/lib/rx_ruby/linq/observable/and.rb +7 -0
- data/lib/rx_ruby/linq/observable/case.rb +15 -0
- data/lib/rx_ruby/linq/observable/concat_all.rb +7 -0
- data/lib/rx_ruby/linq/observable/concat_map.rb +35 -0
- data/lib/rx_ruby/linq/observable/concat_map_observer.rb +43 -0
- data/lib/rx_ruby/linq/observable/contains.rb +28 -0
- data/lib/rx_ruby/linq/observable/debounce.rb +41 -0
- data/lib/rx_ruby/linq/observable/delay.rb +81 -0
- data/lib/rx_ruby/linq/observable/delay_with_selector.rb +64 -0
- data/lib/rx_ruby/linq/observable/do.rb +42 -0
- data/lib/rx_ruby/linq/observable/for.rb +13 -0
- data/lib/rx_ruby/linq/observable/fork_join.rb +55 -0
- data/lib/rx_ruby/linq/observable/from.rb +34 -0
- data/lib/rx_ruby/linq/observable/group_join.rb +108 -0
- data/lib/rx_ruby/linq/observable/if.rb +17 -0
- data/lib/rx_ruby/linq/observable/interval.rb +5 -0
- data/lib/rx_ruby/linq/observable/multicast.rb +14 -0
- data/lib/rx_ruby/linq/observable/of.rb +11 -0
- data/lib/rx_ruby/linq/observable/pairs.rb +7 -0
- data/lib/rx_ruby/linq/observable/pluck.rb +7 -0
- data/lib/rx_ruby/linq/observable/publish.rb +11 -0
- data/lib/rx_ruby/linq/observable/start.rb +7 -0
- data/lib/rx_ruby/linq/observable/time_interval.rb +15 -0
- data/lib/rx_ruby/linq/observable/timer.rb +26 -0
- data/lib/rx_ruby/linq/observable/timestamp.rb +9 -0
- data/lib/rx_ruby/linq/observable/to_async.rb +40 -0
- data/lib/rx_ruby/linq/observable/when.rb +36 -0
- data/lib/rx_ruby/linq/observable/while.rb +41 -0
- data/lib/rx_ruby/operators/aggregates.rb +611 -0
- data/lib/rx_ruby/operators/creation.rb +220 -0
- data/lib/rx_ruby/operators/multiple.rb +735 -0
- data/lib/rx_ruby/operators/single.rb +399 -0
- data/lib/rx_ruby/operators/standard_query_operators.rb +279 -0
- data/lib/rx_ruby/operators/synchronization.rb +47 -0
- data/lib/rx_ruby/operators/time.rb +120 -0
- data/lib/rx_ruby/subjects/async_subject.rb +161 -0
- data/lib/rx_ruby/subjects/behavior_subject.rb +149 -0
- data/lib/rx_ruby/subjects/replay_subject.rb +39 -0
- data/lib/rx_ruby/subjects/subject.rb +131 -0
- data/lib/rx_ruby/subjects/subject_extensions.rb +45 -0
- data/lib/rx_ruby/subscriptions/composite_subscription.rb +91 -0
- data/lib/rx_ruby/subscriptions/ref_count_subscription.rb +88 -0
- data/lib/rx_ruby/subscriptions/scheduled_subscription.rb +32 -0
- data/lib/rx_ruby/subscriptions/serial_subscription.rb +60 -0
- data/lib/rx_ruby/subscriptions/single_assignment_subscription.rb +64 -0
- data/lib/rx_ruby/subscriptions/subscription.rb +56 -0
- data/lib/rx_ruby/testing/cold_observable.rb +45 -0
- data/lib/rx_ruby/testing/hot_observable.rb +47 -0
- data/lib/rx_ruby/testing/mock_observer.rb +33 -0
- data/lib/rx_ruby/testing/reactive_test.rb +94 -0
- data/lib/rx_ruby/testing/recorded.rb +17 -0
- data/lib/rx_ruby/testing/test_scheduler.rb +96 -0
- data/lib/rx_ruby/testing/test_subscription.rb +22 -0
- data/lib/rx_ruby/version.rb +3 -0
- data/license.txt +13 -0
- data/readme.md +152 -0
- data/rx_ruby.gemspec +22 -0
- data/test/rx_ruby/concurrency/helpers/historical_virtual_scheduler_helper.rb +135 -0
- data/test/rx_ruby/concurrency/helpers/immediate_local_scheduler_helper.rb +51 -0
- data/test/rx_ruby/concurrency/test_async_lock.rb +56 -0
- data/test/rx_ruby/concurrency/test_current_thread_scheduler.rb +44 -0
- data/test/rx_ruby/concurrency/test_default_scheduler.rb +44 -0
- data/test/rx_ruby/concurrency/test_historical_scheduler.rb +18 -0
- data/test/rx_ruby/concurrency/test_immediate_scheduler.rb +53 -0
- data/test/rx_ruby/concurrency/test_local_scheduler.rb +12 -0
- data/test/rx_ruby/concurrency/test_periodic_scheduler.rb +53 -0
- data/test/rx_ruby/concurrency/test_scheduled_item.rb +50 -0
- data/test/rx_ruby/concurrency/test_scheduler.rb +128 -0
- data/test/rx_ruby/concurrency/test_virtual_time_scheduler.rb +14 -0
- data/test/rx_ruby/core/test_notification.rb +129 -0
- data/test/rx_ruby/core/test_observable_creation.rb +483 -0
- data/test/rx_ruby/core/test_observer.rb +634 -0
- data/test/rx_ruby/internal/test_priority_queue.rb +71 -0
- data/test/rx_ruby/subscriptions/test_composite_subscription.rb +116 -0
- data/test/rx_ruby/subscriptions/test_serial_subscription.rb +62 -0
- data/test/rx_ruby/subscriptions/test_singleassignment_subscription.rb +61 -0
- data/test/rx_ruby/subscriptions/test_subscription.rb +27 -0
- data/test/test_helper.rb +11 -0
- metadata +291 -0
@@ -0,0 +1,170 @@
|
|
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
|
+
require 'rx_ruby/internal/priority_queue'
|
5
|
+
require 'rx_ruby/subscriptions/subscription'
|
6
|
+
|
7
|
+
module RxRuby
|
8
|
+
|
9
|
+
# Base class for virtual time schedulers using a priority queue for scheduled items.
|
10
|
+
class VirtualTimeScheduler
|
11
|
+
|
12
|
+
include Scheduler
|
13
|
+
|
14
|
+
attr_reader :clock
|
15
|
+
|
16
|
+
def initialize(initial_clock)
|
17
|
+
@clock = initial_clock.to_i
|
18
|
+
@queue = PriorityQueue.new
|
19
|
+
@enabled = false
|
20
|
+
end
|
21
|
+
|
22
|
+
# Gets the scheduler's notion of current time.
|
23
|
+
def now
|
24
|
+
clock
|
25
|
+
end
|
26
|
+
|
27
|
+
# Gets whether the scheduler is enabled to run work.
|
28
|
+
def enabled?
|
29
|
+
@enabled
|
30
|
+
end
|
31
|
+
|
32
|
+
# Starts the virtual time scheduler.
|
33
|
+
def start
|
34
|
+
unless @enabled
|
35
|
+
@enabled = true
|
36
|
+
|
37
|
+
begin
|
38
|
+
next_item = self.get_next
|
39
|
+
|
40
|
+
unless next_item.nil?
|
41
|
+
@clock = next_item.due_time if next_item.due_time > @clock
|
42
|
+
next_item.invoke
|
43
|
+
else
|
44
|
+
@enabled = false
|
45
|
+
end
|
46
|
+
|
47
|
+
end while @enabled
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Stops the virtual time scheduler.
|
52
|
+
def stop
|
53
|
+
@enabled = false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Schedules an action to be executed.
|
57
|
+
def schedule_with_state(state, action)
|
58
|
+
raise 'action cannot be nil' unless action
|
59
|
+
schedule_at_absolute_with_state(state, clock, action)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Schedules an action to be executed at due_time.
|
63
|
+
def schedule_at_relative(due_time, action)
|
64
|
+
raise 'action cannot be nil' unless action
|
65
|
+
|
66
|
+
schedule_at_relative_with_state(action, due_time, method(:invoke))
|
67
|
+
end
|
68
|
+
|
69
|
+
# Schedules an action to be executed at due_time.
|
70
|
+
def schedule_at_relative_with_state(state, due_time, action)
|
71
|
+
raise 'action cannot be nil' unless action
|
72
|
+
|
73
|
+
schedule_at_absolute_with_state(state, @clock + due_time, action)
|
74
|
+
end
|
75
|
+
alias_method :schedule_relative_with_state, :schedule_at_relative_with_state
|
76
|
+
|
77
|
+
# Schedules an action to be executed at due_time.
|
78
|
+
def schedule_at_absolute(due_time, action)
|
79
|
+
raise 'action cannot be nil' unless action
|
80
|
+
|
81
|
+
schedule_at_absolute_with_state(action, due_time, method(:invoke))
|
82
|
+
end
|
83
|
+
|
84
|
+
# Schedules an action to be executed at due_time.
|
85
|
+
def schedule_at_absolute_with_state(state, due_time, action)
|
86
|
+
raise 'action cannot be nil' unless action
|
87
|
+
|
88
|
+
si = nil
|
89
|
+
run = lambda {|scheduler, state1|
|
90
|
+
@queue.delete si
|
91
|
+
action.call(scheduler, state1)
|
92
|
+
}
|
93
|
+
|
94
|
+
si = ScheduledItem.new(self, state, due_time, &run)
|
95
|
+
@queue.push si
|
96
|
+
|
97
|
+
Subscription.create { si.cancel }
|
98
|
+
end
|
99
|
+
alias_method :schedule_absolute_with_state, :schedule_at_absolute_with_state
|
100
|
+
|
101
|
+
# Advances the scheduler's clock to the specified time, running all work till that point.
|
102
|
+
def advance_to(time)
|
103
|
+
due_to_clock = time<=>clock
|
104
|
+
raise 'Time is out of range' if due_to_clock < 0
|
105
|
+
|
106
|
+
return if due_to_clock == 0
|
107
|
+
|
108
|
+
unless @enabled
|
109
|
+
@enabled = true
|
110
|
+
|
111
|
+
begin
|
112
|
+
next_item = self.get_next
|
113
|
+
if !next_item.nil? && next_item.due_time <= time
|
114
|
+
@clock = next_item.due_time if next_item.due_time > @clock
|
115
|
+
next_item.invoke
|
116
|
+
else
|
117
|
+
@enabled = false
|
118
|
+
end
|
119
|
+
|
120
|
+
end while @enabled
|
121
|
+
|
122
|
+
@clock = time
|
123
|
+
else
|
124
|
+
raise 'Cannot advance while running'
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
# Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan.
|
130
|
+
def advance_by(time)
|
131
|
+
dt = @clock + time
|
132
|
+
|
133
|
+
due_to_clock = dt<=>clock
|
134
|
+
raise 'Time is out of range' if due_to_clock < 0
|
135
|
+
|
136
|
+
return if due_to_clock == 0
|
137
|
+
raise 'Cannot advance while running' if @enabled
|
138
|
+
|
139
|
+
self.advance_to dt
|
140
|
+
end
|
141
|
+
|
142
|
+
# Advances the scheduler's clock by the specified relative time.
|
143
|
+
def sleep(time)
|
144
|
+
dt = @clock + time
|
145
|
+
|
146
|
+
due_to_clock = dt<=>@clock
|
147
|
+
raise 'Time is out of range' if due_to_clock < 0
|
148
|
+
|
149
|
+
@clock = dt
|
150
|
+
end
|
151
|
+
|
152
|
+
# Gets the next scheduled item to be executed
|
153
|
+
def get_next
|
154
|
+
while next_item = @queue.peek
|
155
|
+
if next_item.cancelled?
|
156
|
+
@queue.shift
|
157
|
+
else
|
158
|
+
return next_item
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
return nil
|
163
|
+
end
|
164
|
+
|
165
|
+
def invoke(scheduler, action)
|
166
|
+
action.call
|
167
|
+
Subscription.empty
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,46 @@
|
|
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/async_lock'
|
4
|
+
require 'rx_ruby/core/observer'
|
5
|
+
|
6
|
+
module RxRuby
|
7
|
+
|
8
|
+
module Observer
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Synchronizes access to the observer such that its callback methods cannot be called concurrently, using the specified asynchronous lock to protect against concurrent and reentrant access.
|
12
|
+
# This overload is useful when coordinating multiple observers that access shared state by synchronizing on a common asynchronous lock.
|
13
|
+
def prevent_reentrancy(observer, gate = AsyncLock.new)
|
14
|
+
AsyncLockObserver.new(observer, gate)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class AsyncLockObserver < RxRuby::ObserverBase
|
20
|
+
|
21
|
+
def on_next_core(value)
|
22
|
+
@gate.wait { @observer.on_next value }
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_error_core(error)
|
26
|
+
@gate.wait { @observer.on_error error }
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_completed_core
|
30
|
+
@gate.wait { @observer.on_completed }
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(observer, gate)
|
34
|
+
@observer = observer
|
35
|
+
@gate = gate
|
36
|
+
|
37
|
+
config = ObserverConfiguration.new
|
38
|
+
config.on_next(&method(:on_next_core))
|
39
|
+
config.on_error(&method(:on_error_core))
|
40
|
+
config.on_completed(&method(:on_completed_core))
|
41
|
+
|
42
|
+
super(config)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,59 @@
|
|
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/core/observer'
|
4
|
+
require 'rx_ruby/subscriptions/single_assignment_subscription'
|
5
|
+
|
6
|
+
module RxRuby
|
7
|
+
|
8
|
+
class AutoDetachObserver < RxRuby::ObserverBase
|
9
|
+
|
10
|
+
def on_next_core(value)
|
11
|
+
no_error = false
|
12
|
+
begin
|
13
|
+
@observer.on_next(value)
|
14
|
+
no_error = true
|
15
|
+
ensure
|
16
|
+
unsubscribe unless no_error
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_error_core(error)
|
21
|
+
begin
|
22
|
+
@observer.on_error(error)
|
23
|
+
ensure
|
24
|
+
unsubscribe
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_completed_core
|
29
|
+
begin
|
30
|
+
@observer.on_completed
|
31
|
+
ensure
|
32
|
+
unsubscribe
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(observer)
|
37
|
+
@observer = observer
|
38
|
+
@m = SingleAssignmentSubscription.new
|
39
|
+
|
40
|
+
config = ObserverConfiguration.new
|
41
|
+
config.on_next(&method(:on_next_core))
|
42
|
+
config.on_error(&method(:on_error_core))
|
43
|
+
config.on_completed(&method(:on_completed_core))
|
44
|
+
|
45
|
+
super(config)
|
46
|
+
end
|
47
|
+
|
48
|
+
def subscription=(new_subscription)
|
49
|
+
@m.subscription = new_subscription
|
50
|
+
end
|
51
|
+
|
52
|
+
def unsubscribe
|
53
|
+
super
|
54
|
+
@m.unsubscribe
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,66 @@
|
|
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
|
+
require 'rx_ruby/core/observer'
|
5
|
+
|
6
|
+
module RxRuby
|
7
|
+
|
8
|
+
module Observer
|
9
|
+
# Checks access to the observer for grammar violations. This includes checking for multiple on_error or on_completed calls, as well as reentrancy in any of the observer methods.
|
10
|
+
# If a violation is detected, an error is thrown from the offending observer method call.
|
11
|
+
def checked
|
12
|
+
CheckedObserver.new(self)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class CheckedObserver
|
17
|
+
include Observer
|
18
|
+
|
19
|
+
def initialize(observer)
|
20
|
+
@observer = observer
|
21
|
+
@state = :idle
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_next(value)
|
25
|
+
check_access
|
26
|
+
begin
|
27
|
+
@observer.on_next value
|
28
|
+
ensure
|
29
|
+
Mutex.new.synchronize { @state = :idle }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_error(error)
|
34
|
+
check_access
|
35
|
+
begin
|
36
|
+
@observer.on_error error
|
37
|
+
ensure
|
38
|
+
Mutex.new.synchronize { @state = :done }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_completed
|
43
|
+
check_access
|
44
|
+
begin
|
45
|
+
@observer.on_completed
|
46
|
+
ensure
|
47
|
+
Mutex.new.synchronize { @state = :done }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def check_access
|
54
|
+
Mutex.new.synchronize do
|
55
|
+
old = @state
|
56
|
+
@state = :busy if @state == :idle
|
57
|
+
case old
|
58
|
+
when :busy
|
59
|
+
raise 'Re-entrancy detected'
|
60
|
+
when :done
|
61
|
+
raise 'Observer terminated'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,161 @@
|
|
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/immediate_scheduler'
|
4
|
+
require 'rx_ruby/core/observable'
|
5
|
+
|
6
|
+
module RxRuby
|
7
|
+
|
8
|
+
module Observer
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Creates an observer from a notification callback.
|
12
|
+
def from_notifier
|
13
|
+
raise ArgumentError.new 'Block required' unless block_given?
|
14
|
+
|
15
|
+
configure do |o|
|
16
|
+
o.on_next {|x| yield Notification.create_on_next(x) }
|
17
|
+
o.on_error {|err| yield Notification.create_on_error(err) }
|
18
|
+
o.on_completed { yield Notification.create_on_completed }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Represents a notification to an observer.
|
25
|
+
module Notification
|
26
|
+
|
27
|
+
class << self
|
28
|
+
|
29
|
+
# Creates an object that represents an on_next notification to an observer.
|
30
|
+
def create_on_next(value)
|
31
|
+
OnNextNotification.new value
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates an object that represents an on_error notification to an observer.
|
35
|
+
def create_on_error(error)
|
36
|
+
OnErrorNotification.new error
|
37
|
+
end
|
38
|
+
|
39
|
+
# Creates an object that represents an on_completed notification to an observer.
|
40
|
+
def create_on_completed
|
41
|
+
OnCompletedNotification.new
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
# Determines whether this is an on_next notification.
|
47
|
+
def on_next?
|
48
|
+
@kind == :on_next
|
49
|
+
end
|
50
|
+
|
51
|
+
# Determines whether this is an on_error notification.
|
52
|
+
def on_error?
|
53
|
+
@kind == :on_error
|
54
|
+
end
|
55
|
+
|
56
|
+
# Determines whether this is an on_completed notification.
|
57
|
+
def on_completed?
|
58
|
+
@kind == :on_completed
|
59
|
+
end
|
60
|
+
|
61
|
+
# Determines whether this notification has a value.
|
62
|
+
def has_value?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns an observable sequence with a single notification.
|
67
|
+
def to_observable(scheduler = ImmediateScheduler.instance)
|
68
|
+
AnonymousObservable.new do |observer|
|
69
|
+
scheduler.schedule lambda {
|
70
|
+
accept observer
|
71
|
+
observer.on_completed if on_next?
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
# Represents an on_next notification to an observer.
|
79
|
+
class OnNextNotification
|
80
|
+
include Notification
|
81
|
+
|
82
|
+
attr_reader :value
|
83
|
+
|
84
|
+
def initialize(value)
|
85
|
+
@value = value
|
86
|
+
@kind = :on_next
|
87
|
+
end
|
88
|
+
|
89
|
+
# Determines whether this notification has a value.
|
90
|
+
def has_value?
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
def ==(other)
|
95
|
+
other.class == self.class && other.on_next? && value == other.value
|
96
|
+
end
|
97
|
+
alias_method :eql?, :==
|
98
|
+
|
99
|
+
def to_s
|
100
|
+
"on_next(#{value})"
|
101
|
+
end
|
102
|
+
|
103
|
+
# Invokes the observer's method corresponding to the notification.
|
104
|
+
def accept(observer)
|
105
|
+
observer.on_next value
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
# Represents an on_error notification to an observer.
|
111
|
+
class OnErrorNotification
|
112
|
+
include Notification
|
113
|
+
|
114
|
+
attr_reader :error
|
115
|
+
|
116
|
+
def initialize(error)
|
117
|
+
@error = error
|
118
|
+
@kind = :on_error
|
119
|
+
end
|
120
|
+
|
121
|
+
def ==(other)
|
122
|
+
other.class == self.class && other.on_error? && error == other.error
|
123
|
+
end
|
124
|
+
alias_method :eql?, :==
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
"on_error(#{error})"
|
128
|
+
end
|
129
|
+
|
130
|
+
# Invokes the observer's method corresponding to the notification.
|
131
|
+
def accept(observer)
|
132
|
+
observer.on_error error
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
# Represents an on_completed notification to an observer.
|
138
|
+
class OnCompletedNotification
|
139
|
+
include Notification
|
140
|
+
|
141
|
+
def initialize
|
142
|
+
@kind = :on_completed
|
143
|
+
end
|
144
|
+
|
145
|
+
def ==(other)
|
146
|
+
other.class == self.class && other.on_completed?
|
147
|
+
end
|
148
|
+
alias_method :eql?, :==
|
149
|
+
|
150
|
+
def to_s
|
151
|
+
"on_completed()"
|
152
|
+
end
|
153
|
+
|
154
|
+
# Invokes the observer's method corresponding to the notification.
|
155
|
+
def accept(observer)
|
156
|
+
observer.on_completed
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|