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,11 @@
1
+ module RxRuby
2
+ class << Observable
3
+ def of(*args)
4
+ scheduler = CurrentThreadScheduler.instance
5
+ if args.size > 0 && Scheduler === args[0]
6
+ scheduler = args.shift
7
+ end
8
+ of_array(args, scheduler)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module RxRuby
2
+ class << Observable
3
+ def pairs(obj, scheduler = CurrentThreadScheduler.instance)
4
+ of_enumerable(obj, scheduler)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module RxRuby
2
+ module Observable
3
+ def pluck(prop)
4
+ self.map {|x| x[prop]}
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ module RxRuby
2
+ module Observable
3
+ def publish(&selector)
4
+ if block_given?
5
+ multicast(lambda { Subject.new }, Proc.new)
6
+ else
7
+ multicast(Subject.new)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module RxRuby
2
+ class << Observable
3
+ def start(func, context, scheduler = DefaultScheduler.instance)
4
+ Observable.to_async(func, context, scheduler).call
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ module RxRuby
2
+ module Observable
3
+ def time_interval(scheduler = DefaultScheduler.instance)
4
+ Observable.defer {
5
+ last = scheduler.now
6
+ self.map {|x|
7
+ now = scheduler.now
8
+ span = now - last
9
+ last = now
10
+ TimeInterval.new(span, x)
11
+ }
12
+ }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module RxRuby
2
+ class << Observable
3
+ def timer(due_time, period_or_scheduler = DefaultScheduler.instance, scheduler = DefaultScheduler.instance)
4
+ case period_or_scheduler
5
+ when Numeric
6
+ period = period_or_scheduler
7
+ when Scheduler
8
+ scheduler = period_or_scheduler
9
+ end
10
+
11
+ if Time === due_time
12
+ if period.nil?
13
+ observable_timer_date(due_time, scheduler)
14
+ else
15
+ observable_timer_date_and_period(due_time, period, scheduler)
16
+ end
17
+ else
18
+ if period.nil?
19
+ observable_timer_time_span(due_time, scheduler)
20
+ else
21
+ observable_timer_time_span_and_period(due_time, period, scheduler)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module RxRuby
2
+ module Observable
3
+ def timestamp(scheduler = DefaultScheduler.instance)
4
+ map do |x|
5
+ { value: x, timestamp: scheduler.now }
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ module RxRuby
2
+ class << Observable
3
+ def to_async(func, context = nil, scheduler = DefaultScheduler.instance)
4
+ lambda() {|*args|
5
+ subject = AsyncSubject.new
6
+
7
+ scheduler.schedule lambda {
8
+ begin
9
+ if context
10
+ result = proc_bind(func, context).call(*args)
11
+ else
12
+ result = func.call(*args)
13
+ end
14
+ rescue => e
15
+ subject.on_error e
16
+ return
17
+ end
18
+ subject.on_next result
19
+ subject.on_completed
20
+ }
21
+ return subject.as_observable
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ # derived from Proc#to_method from Ruby Facets
28
+ # https://github.com/rubyworks/facets/blob/master/lib/core/facets/proc/to_method.rb
29
+ def proc_bind(block, object)
30
+ time = Time.now
31
+ method_name = "__bind_#{time.to_i}_#{time.usec}"
32
+ (class << object; self; end).class_eval do
33
+ define_method(method_name, &block)
34
+ method = instance_method(method_name)
35
+ remove_method(method_name)
36
+ method
37
+ end.bind(object)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,36 @@
1
+ module RxRuby
2
+ class << Observable
3
+ def when(*plans)
4
+ AnonymousObservable.new do |observer|
5
+ active_plans = []
6
+ external_subscriptions = {}
7
+ out_observer = Observer.configure {|o|
8
+ o.on_next(&observer.method(:on_next))
9
+ o.on_error {|err|
10
+ external_subscriptions.each {|_, v|
11
+ v.on_error err
12
+ }
13
+ }
14
+ o.on_completed(&observer.method(:on_completed))
15
+ }
16
+ begin
17
+ plans.each {|x|
18
+ active_plans.push x.activate(external_subscriptions, out_observer, lambda {|active_plan|
19
+ active_plans.delete(active_plan)
20
+ active_plans.length == 0 && observer.on_completed
21
+ })
22
+ }
23
+ rescue => e
24
+ Observable.raise_error(e).subscribe(observer)
25
+ end
26
+ group = CompositeSubscription.new
27
+ external_subscriptions.each {|_, join_observer|
28
+ join_observer.subscribe
29
+ group.push join_observer
30
+ }
31
+
32
+ group
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ module RxRuby
2
+ class << Observable
3
+ def while(condition, source)
4
+ enum = Enumerator.new {|y|
5
+ while condition.call
6
+ y << source
7
+ end
8
+ }
9
+ scheduler = ImmediateScheduler.instance
10
+
11
+ is_disposed = false
12
+ subscription = SerialSubscription.new
13
+
14
+ AnonymousObservable.new do |observer|
15
+ cancelable = scheduler.schedule_recursive lambda {|this|
16
+ return if is_disposed
17
+
18
+ begin
19
+ current_value = enum.next
20
+ rescue StopIteration => e
21
+ observer.on_completed
22
+ return
23
+ rescue => e
24
+ observer.on_error e
25
+ return
26
+ end
27
+
28
+ d = SingleAssignmentSubscription.new
29
+ subscription.subscription = d
30
+ d.subscription = current_value.subscribe(
31
+ observer.method(:on_next),
32
+ observer.method(:on_error),
33
+ lambda { this.call }
34
+ )
35
+ }
36
+
37
+ CompositeSubscription.new [subscription, cancelable, Subscription.create { is_disposed = true }]
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,611 @@
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/subscriptions/composite_subscription'
5
+ require 'rx_ruby/core/observer'
6
+ require 'rx_ruby/core/observable'
7
+ require 'rx_ruby/operators/single'
8
+ require 'rx_ruby/operators/standard_query_operators'
9
+
10
+ module RxRuby
11
+
12
+ module Observable
13
+
14
+ # Internal method to get the final value
15
+ # @return [RxRuby::Observable]
16
+ def final
17
+ AnonymousObservable.new do |observer|
18
+ value = nil
19
+ has_value = false
20
+
21
+ new_obs = Observer.configure do |o|
22
+ o.on_next do |x|
23
+ value = x
24
+ has_value = true
25
+ end
26
+
27
+ o.on_error(&observer.method(:on_error))
28
+
29
+ o.on_completed do
30
+ if has_value
31
+ observer.on_next value
32
+ observer.on_completed
33
+ else
34
+ observer.on_error(RuntimeError.new 'Sequence contains no elements')
35
+ end
36
+ end
37
+ end
38
+
39
+ subscribe new_obs
40
+ end
41
+ end
42
+
43
+ # Applies an accumulator function over an observable sequence, returning the result of the aggregation as a single
44
+ # element in the result sequence. The specified seed value is used as the initial accumulator value.
45
+ # For aggregation behavior with incremental intermediate results, see RxRuby::Observable.scan
46
+ # @return [RxRuby::Observable]
47
+ def reduce(*args, &block)
48
+ # Argument parsing to support:
49
+ # 1. (seed, Symbol) || (seed, &block)
50
+ # 2. (Symbol) || (&block)
51
+ if (args.length == 2 && args[1].is_a?(Symbol)) || (args.length == 1 && block_given?)
52
+ scan(*args, &block).start_with(args[0]).final
53
+ elsif (args.length == 1 && args[0].is_a?(Symbol)) || (args.length == 0 && block_given?)
54
+ scan(*args, &block).final
55
+ else
56
+ raise ArgumentError.new 'Invalid arguments'
57
+ end
58
+ end
59
+
60
+ # Determines whether all elements of an observable sequence satisfy a condition if block given, else if all are
61
+ # true
62
+ # @param [Proc] block
63
+ # @return [RxRuby::Observable]
64
+ def all?(&block)
65
+ block ||= lambda { |_| true }
66
+ select {|v| !(block.call v)}.
67
+ any?.
68
+ map {|b| !b }
69
+ end
70
+
71
+ # Determines whether no elements of an observable sequence satisfy a condition if block given, else if all are
72
+ # false
73
+ # @param [Proc] block
74
+ # @return [RxRuby::Observable]
75
+ def none?(&block)
76
+ block ||= lambda { |_| true }
77
+ select {|v| !(block.call v)}.
78
+ any?
79
+ end
80
+
81
+ # Determines whether any element of an observable sequence satisfies a condition if a block is given else if
82
+ # there are any items in the observable sequence.
83
+ # @return [RxRuby::Observable]
84
+ def any?(&block)
85
+ return map(&block).any? if block_given?
86
+ AnonymousObservable.new do |observer|
87
+ new_obs = Observer.configure do |o|
88
+ o.on_next do |_|
89
+ observer.on_next true
90
+ observer.on_completed
91
+ end
92
+
93
+ o.on_error(&observer.method(:on_error))
94
+
95
+ o.on_completed do
96
+ observer.on_next false
97
+ observer.on_completed
98
+ end
99
+ end
100
+
101
+ subscribe new_obs
102
+ end
103
+ end
104
+
105
+ # Computes the average of an observable sequence of values that are optionally obtained by invoking a
106
+ # transform function on each element of the input sequence if a block is given
107
+ # @param [Object] block
108
+ # @return [RxRuby::Observable]
109
+ def average(&block)
110
+ return map(&block).average if block_given?
111
+ scan({:sum => 0, :count => 0}) {|prev, current| {:sum => prev[:sum] + current, :count => prev[:count] + 1 }}.
112
+ final.
113
+ map {|x|
114
+ raise 'Sequence contains no elements' if x[:count] == 0
115
+ x[:sum] / x[:count]
116
+ }
117
+ end
118
+
119
+ # Determines whether an observable sequence contains a specified element.
120
+ # @param [Object] item The value to locate in the source sequence.
121
+ # @return [RxRuby::Observable] An observable sequence containing a single element determining whether the source
122
+ # sequence contains an element that has the specified value.
123
+ def contains?(item)
124
+ select {|x| x.eql? item}.any?
125
+ end
126
+
127
+ # Returns an observable sequence containing a number that represents how many elements in the specified
128
+ # observable sequence satisfy a condition if the block is given, else the number of items in the observable
129
+ # sequence
130
+ def count(&block)
131
+ return select(&block).count if block_given?
132
+ reduce(0) {|c, _| c + 1 }
133
+ end
134
+
135
+ # Returns the element at a specified index in a sequence.
136
+ # @param [Numeric] index The zero-based index of the element to retrieve.
137
+ # @return [RxRuby::Observable] An observable sequence that produces the element at the specified position in the
138
+ # source sequence.
139
+ def element_at(index)
140
+ raise ArgumentError.new 'index cannot be less than zero' if index < 0
141
+ AnonymousObservable.new do |observer|
142
+ i = index
143
+ new_obs = Observer.configure do |o|
144
+ o.on_next do |value|
145
+ if i == 0
146
+ observer.on_next value
147
+ observer.on_completed
148
+ end
149
+
150
+ i -= 1
151
+ end
152
+
153
+ o.on_error(&observer.method(:on_error))
154
+ o.on_completed { raise 'Sequence contains no elements' }
155
+ end
156
+
157
+ subscribe new_obs
158
+ end
159
+ end
160
+
161
+ # Returns the element at a specified index in a sequence or a default value if the index is out of range.
162
+ # @param [Numeric] index The zero-based index of the element to retrieve.
163
+ # @param [Object] default_value The default value to use if the index is out of range.
164
+ def element_at_or_default(index, default_value = nil)
165
+ raise ArgumentError.new 'index cannot be less than zero' if index < 0
166
+ AnonymousObservable.new do |observer|
167
+ i = index
168
+ new_obs = Observer.configure do |o|
169
+ o.on_next do |value|
170
+ if i == 0
171
+ observer.on_next value
172
+ observer.on_completed
173
+ end
174
+
175
+ i -= 1
176
+ end
177
+
178
+ o.on_error(&observer.method(:on_error))
179
+
180
+ o.on_completed do
181
+ observer.on_next default_value
182
+ observer.on_completed
183
+ end
184
+ end
185
+
186
+ subscribe new_obs
187
+ end
188
+ end
189
+
190
+ # Returns the first element of an observable sequence that satisfies the condition in the predicate if a block is
191
+ # given, else the first item in the observable sequence.
192
+ # @param [Proc] block Optional predicate function to evaluate for elements in the source sequence.
193
+ # @return [RxRuby::Observable] Sequence containing the first element in the observable sequence that satisfies the
194
+ # condition in the predicate if a block is given, else the first element.
195
+ def first(&block)
196
+ return select(&block).first if block_given?
197
+ AnonymousObservable.new do |observer|
198
+ new_obs = Observer.configure do |o|
199
+ o.on_next do |x|
200
+ observer.on_next x
201
+ observer.on_completed
202
+ end
203
+
204
+ o.on_error(&observer.method(:on_error))
205
+ o.on_completed { raise 'Sequence contains no elements' }
206
+ end
207
+
208
+ subscribe new_obs
209
+ end
210
+ end
211
+
212
+ # Returns the first element of an observable sequence that satisfies the condition in the predicate if given,
213
+ # or a default value if no such element exists.
214
+ # @param [Object] default_value The default value to use if the sequence is empty.
215
+ # @param [Proc] block An optional predicate function to evaluate for elements in the source sequence.
216
+ # @return [RxRuby::Observable] Sequence containing the first element in the observable sequence that satisfies the
217
+ # condition in the predicate if given, or a default value if no such element exists.
218
+ def first_or_default(default_value = nil, &block)
219
+ return select(&block).first_or_default(default_value) if block_given?
220
+ AnonymousObservable.new do |observer|
221
+ new_obs = Observer.configure do |o|
222
+ o.on_next do |x|
223
+ observer.on_next x
224
+ observer.on_completed
225
+ end
226
+
227
+ o.on_error(&observer.method(:on_error))
228
+
229
+ o.on_completed do
230
+ observer.on_next default_value
231
+ observer.on_completed
232
+ end
233
+ end
234
+
235
+ subscribe new_obs
236
+ end
237
+ end
238
+
239
+ # Determines whether an observable sequence is empty.
240
+ # @return [RxRuby::Observable] An observable sequence containing a single element determining whether the source
241
+ # sequence is empty.
242
+ def empty?
243
+ any?.map {|b| !b }
244
+ end
245
+
246
+ # Returns the last element of an observable sequence that satisfies the condition in the predicate if the block is
247
+ # given, else the last element in the observable sequence.
248
+ # @param [Proc] block An predicate function to evaluate for elements in the source sequence.
249
+ # @return {RxRuby::Observable} Sequence containing the last element in the observable sequence that satisfies the
250
+ # condition in the predicate if given, or the last element in the observable sequence.
251
+ def last(&block)
252
+ return select(&block).last if block_given?
253
+ AnonymousObservable.new do |observer|
254
+
255
+ value = nil
256
+ seen_value = false
257
+
258
+ new_obs = Observer.configure do |o|
259
+ o.on_next do |v|
260
+ value = v
261
+ seen_value = true
262
+ end
263
+
264
+ o.on_error(&observer.method(:on_error))
265
+
266
+ o.on_completed do
267
+ if seen_value
268
+ observer.on_next value
269
+ observer.on_completed
270
+ else
271
+ observer.on_error(RuntimeError.new 'Sequence contains no elements' )
272
+ end
273
+ end
274
+ end
275
+
276
+ subscribe new_obs
277
+ end
278
+ end
279
+
280
+ # Returns the last element of an observable sequence that satisfies the condition in the predicate if given, or
281
+ # a default value if no such element exists.
282
+ # @param [Object] default_value The default value to use if the sequence is empty.
283
+ # @param [Proc] block An predicate function to evaluate for elements in the source sequence.
284
+ # @return {RxRuby::Observable} Sequence containing the last element in the observable sequence that satisfies the
285
+ # condition in the predicate if given, or a default value if no such element exists.
286
+ def last_or_default(default_value = nil, &block)
287
+ return select(&block).last_or_default(default_value) if block_given?
288
+ AnonymousObservable.new do |observer|
289
+
290
+ value = nil
291
+ seen_value = false
292
+
293
+ new_obs = Observer.configure do |o|
294
+ o.on_next do |v|
295
+ value = v
296
+ seen_value = true
297
+ end
298
+
299
+ o.on_error(&observer.method(:on_error))
300
+
301
+ o.on_completed do
302
+ observer.on_next (seen_value ? value : default_value)
303
+ observer.on_completed
304
+ end
305
+ end
306
+
307
+ subscribe new_obs
308
+ end
309
+ end
310
+
311
+ # Returns the maximum element in an observable sequence.
312
+ # @param [Proc] block An optional selector function to produce an element.
313
+ # @return [RxRuby::Observable] The maximum element in an observable sequence.
314
+ def max(&block)
315
+ return map(&block).max if block_given?
316
+ max_by {x| x} .map {|x| x[0] }
317
+ end
318
+
319
+ # Returns the elements in an observable sequence with the maximum key value.
320
+ # @param [Proc] block Key selector function.
321
+ # @return [RxRuby::Observable] An observable sequence containing a list of zero or more elements that have a maximum
322
+ # key value.
323
+ def max_by(&block)
324
+ extrema_by(&block)
325
+ end
326
+
327
+ # Returns the minimum element in an observable sequence.
328
+ # @param [Proc] block An optional selector function to produce an element.
329
+ # @return [RxRuby::Observable] The minimum element in an observable sequence.
330
+ def min(&block)
331
+ return map(&block).min if block_given?
332
+ min_by {|x| x} .map {|x| x[0] }
333
+ end
334
+
335
+ # Returns the elements in an observable sequence with the minimum key value.
336
+ # @param [Proc] block Key selector function.
337
+ # @return [RxRuby::Observable] >An observable sequence containing a list of zero or more elements that have a
338
+ # minimum key value.
339
+ def min_by(&block)
340
+ extrema_by(true, &block)
341
+ end
342
+
343
+ # Determines whether two sequences are equal by comparing the elements pairwise.
344
+ # @param [RxRuby::Observable] other Other observable sequence to compare.
345
+ # @return [RxRuby::Observable] An observable sequence that contains a single element which indicates whether both
346
+ # sequences are of equal length and their corresponding elements are equal.
347
+ def sequence_eql?(other)
348
+ AnonymousObservable.new do |observer|
349
+ gate = Mutex.new
350
+ left_done = false
351
+ right_done = false
352
+ left_queue = []
353
+ right_queue = []
354
+
355
+ obs1 = Observer.configure do |o|
356
+ o.on_next do |x|
357
+ gate.synchronize do
358
+ if right_queue.length > 0
359
+ v = right_queue.shift
360
+ equal = x == v
361
+
362
+ unless equal
363
+ observer.on_next false
364
+ observer.on_completed
365
+ end
366
+ elsif right_done
367
+ observer.on_next false
368
+ observer.on_completed
369
+ else
370
+ left_queue.push x
371
+ end
372
+ end
373
+ end
374
+
375
+ o.on_error(&observer.method(:on_error))
376
+
377
+ o.on_completed do
378
+ gate.synchronize do
379
+ left_done = true
380
+ if left_queue.length == 0
381
+ if right_queue.length > 0
382
+ observer.on_next false
383
+ observer.on_completed
384
+ elsif right_done
385
+ observer.on_next true
386
+ observer.on_completed
387
+ end
388
+ end
389
+ end
390
+ end
391
+ end
392
+
393
+ subscription1 = subscribe obs1
394
+
395
+ obs2 = Observer.configure do |o|
396
+ o.on_next do |x|
397
+ gate.synchronize do
398
+ if left_queue.length > 0
399
+ v = left_queue.shift
400
+ equal = x == v
401
+
402
+ unless equal
403
+ observer.on_next false
404
+ observer.on_completed
405
+ end
406
+ elsif left_done
407
+ observer.on_next false
408
+ observer.on_completed
409
+ else
410
+ right_queue.push x
411
+ end
412
+ end
413
+ end
414
+
415
+ o.on_error(&observer.method(:on_error))
416
+
417
+ o.on_completed do
418
+ gate.synchronize do
419
+ right_done = true
420
+ if right_queue.length == 0
421
+ if left_queue.length > 0
422
+ observer.on_next false
423
+ observer.on_completed
424
+ elsif left_done
425
+ observer.on_next true
426
+ observer.on_completed
427
+ end
428
+ end
429
+ end
430
+ end
431
+ end
432
+
433
+ subscription2 = other.subscribe obs2
434
+
435
+ CompositeSubscription.new [subscription1, subscription2]
436
+ end
437
+ end
438
+
439
+ # Returns the only element of an observable sequence, and reports an exception if there is not exactly one
440
+ # element in the observable sequence.
441
+ # @param [Proc] block A predicate function to evaluate for elements in the source sequence.
442
+ # @return [RxRuby::Observable] >Sequence containing the single element in the observable sequence.
443
+ def single(&block)
444
+ return select(&block).single if block_given?
445
+ AnonymousObservable.new do |observer|
446
+ seen_value = false
447
+ value = nil
448
+
449
+ new_obs = Observer.configure do |o|
450
+ o.on_next do |x|
451
+ if seen_value
452
+ observer.on_error(RuntimeError.new 'More than one element produced')
453
+ else
454
+ value = x
455
+ seen_value = true
456
+ end
457
+ end
458
+
459
+ o.on_error(&observer.method(:on_error))
460
+
461
+ o.on_completed do
462
+ if seen_value
463
+ observer.on_next value
464
+ observer.on_completed
465
+ else
466
+ observer.on_error(RuntimeError.new 'Sequence contains no elements')
467
+ end
468
+ end
469
+ end
470
+
471
+ subscribe new_obs
472
+ end
473
+ end
474
+
475
+ # Returns the only element of an observable sequence, or a default value if the observable sequence is empty;
476
+ # this method reports an exception if there is more than one element in the observable sequence.
477
+ # @param [Object] default_value The default value if no value is provided
478
+ # @param [Proc] block A predicate function to evaluate for elements in the source sequence.
479
+ # @return [RxRuby::Observable] Sequence containing the single element in the observable sequence, or a default value
480
+ # if no such element exists.
481
+ def single_or_default(default_value = nil, &block)
482
+ return select(&block).single_or_default(default_value) if block_given?
483
+ AnonymousObservable.new do |observer|
484
+ seen_value = false
485
+ value = nil
486
+
487
+ new_obs = Observer.configure do |o|
488
+ o.on_next do |x|
489
+ if seen_value
490
+ observer.on_error(RuntimeError.new 'More than one element produced')
491
+ else
492
+ value = x
493
+ seen_value = true
494
+ end
495
+ end
496
+
497
+ o.on_error(&observer.method(:on_error))
498
+
499
+ o.on_completed do
500
+ observer.on_next (seen_value ? value : default_value)
501
+ observer.on_completed
502
+ end
503
+ end
504
+
505
+ subscribe new_obs
506
+ end
507
+ end
508
+
509
+ # Computes the sum of a sequence of values.
510
+ # @param [Proc] block Optional block used to obtain the value to sum.
511
+ # @return [RxRuby::Observable] An observable sequence containing a single element with the sum of the values in the
512
+ # source sequence.
513
+ def sum(&block)
514
+ return map(&block).sum if block_given?
515
+ reduce(0) {|acc, x| acc + x}
516
+ end
517
+
518
+ # Creates an array from an observable sequence.
519
+ # @return [RxRuby::Observable] An array created from an observable sequence.
520
+ def to_a
521
+ AnonymousObservable.new do |observer|
522
+ arr = []
523
+ self.subscribe(
524
+ arr.method(:push),
525
+ observer.method(:on_error),
526
+ lambda {
527
+ observer.on_next arr
528
+ observer.on_completed
529
+ })
530
+ end
531
+ end
532
+
533
+ class HashConfiguration
534
+ DEFAULT_SELECTOR = lambda {|x| x}
535
+
536
+ attr_reader :key_selector_block, :value_selector_block
537
+
538
+ def initialize
539
+ @key_selector_block = DEFAULT_SELECTOR
540
+ @value_selector_block = DEFAULT_SELECTOR
541
+ end
542
+
543
+ def key_selector(&key_selector_block)
544
+ @key_selector_block = key_selector_block
545
+ end
546
+
547
+ def value_selector(&value_selector_block)
548
+ @on_error_block = value_selector_block
549
+ end
550
+ end
551
+
552
+ # Creates a Hash from the observable collection. Note that any duplicate keys will be overwritten.
553
+ # @return [RxRuby::Observable] A Hash created from an observable sequence.
554
+ def to_h
555
+ h = HashConfiguration.new
556
+ yield h if block_given?
557
+ reduce(Hash.new) do |acc, x|
558
+ acc[h.key_selector_block.call x] = h.value_selector_block.call x
559
+ acc
560
+ end
561
+ end
562
+
563
+ private
564
+
565
+ def extrema_by(is_min = false, &block)
566
+ AnonymousObservable.new do |observer|
567
+ has_value = false
568
+ last_key = nil
569
+ list = []
570
+
571
+ new_obs = Observer.configure do |o|
572
+ o.on_next do |x|
573
+ key = nil
574
+ begin
575
+ key = block.call(x)
576
+ rescue => e
577
+ observer.on_error e
578
+ return
579
+ end
580
+
581
+ comparison = 0
582
+ if has_value
583
+ comparison = key<=>last_key
584
+ comparison = comparison * -1 if is_min
585
+ else
586
+ has_value = true
587
+ last_key = key
588
+ end
589
+
590
+ if comparison > 0
591
+ last_key = key
592
+ list = []
593
+ end
594
+ list.push x if comparison >= 0
595
+ end
596
+
597
+ o.on_error(&observer.method(:on_error))
598
+
599
+ o.on_completed do
600
+ observer.on_next list
601
+ observer.on_completed
602
+ end
603
+
604
+ end
605
+
606
+ subscribe new_obs
607
+ end
608
+ end
609
+
610
+ end
611
+ end