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,399 @@
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/subscription'
4
+ require 'rx_ruby/subscriptions/composite_subscription'
5
+ require 'rx_ruby/subscriptions/ref_count_subscription'
6
+ require 'rx_ruby/subscriptions/single_assignment_subscription'
7
+ require 'rx_ruby/core/observer'
8
+ require 'rx_ruby/core/observable'
9
+ require 'rx_ruby/operators/creation'
10
+
11
+ module RxRuby
12
+
13
+ module Observable
14
+
15
+ # Hides the identity of an observable sequence.
16
+ def as_observable
17
+ AnonymousObservable.new {|observer| subscribe(observer) }
18
+ end
19
+
20
+ # Projects each element of an observable sequence into zero or more buffers which are produced based on element count information.
21
+ def buffer_with_count(count, skip = count)
22
+ raise ArgumentError.new 'Count must be greater than zero' if count <= 0
23
+ raise ArgumentError.new 'Skip must be greater than zero' if skip <= 0
24
+ window_with_count(count, skip).flat_map(&:to_a).find_all {|x| x.length > 0 }
25
+ end
26
+
27
+ # Dematerializes the explicit notification values of an observable sequence as implicit notifications.
28
+ def dematerialize
29
+ AnonymousObservable.new do |observer|
30
+
31
+ new_obs = RxRuby::Observer.configure do |o|
32
+ o.on_next {|x| x.accept observer }
33
+ o.on_error(&observer.method(:on_error))
34
+ o.on_completed(&observer.method(:on_completed))
35
+ end
36
+
37
+ subscribe new_obs
38
+ end
39
+ end
40
+
41
+ # Returns an observable sequence that contains only distinct contiguous elements according to the optional key_selector.
42
+ def distinct_until_changed(&key_selector)
43
+ key_selector ||= lambda {|x| x}
44
+ AnonymousObservable.new do |observer|
45
+ current_key = nil
46
+ has_current = nil
47
+
48
+ new_obs = RxRuby::Observer.configure do |o|
49
+ o.on_next do |value|
50
+ key = nil
51
+ begin
52
+ key = key_selector.call value
53
+ rescue => err
54
+ observer.on_error err
55
+ next
56
+ end
57
+
58
+ if !current_key || key != current_key
59
+ has_current = true
60
+ current_key = key
61
+ observer.on_next value
62
+ end
63
+ end
64
+
65
+ o.on_error(&observer.method(:on_error))
66
+ o.on_completed(&observer.method(:on_completed))
67
+ end
68
+
69
+ subscribe new_obs
70
+ end
71
+ end
72
+
73
+ # Invokes the observer's methods for each message in the source sequence.
74
+ # This method can be used for debugging, logging, etc. of query behavior by intercepting the message stream to run arbitrary actions for messages on the pipeline.
75
+ def tap(observer)
76
+ raise ArgumentError.new 'Observer cannot be nil' unless observer
77
+ AnonymousObservable.new do |obs|
78
+ new_obs = RxRuby::Observer.configure do |o|
79
+
80
+ o.on_next do |value|
81
+ begin
82
+ observer.on_next value
83
+ rescue => err
84
+ obs.on_error err
85
+ end
86
+
87
+ obs.on_next value
88
+ end
89
+
90
+ o.on_error do |err|
91
+ begin
92
+ observer.on_error err
93
+ rescue => e
94
+ obs.on_error e
95
+ end
96
+
97
+ obs.on_error err
98
+ end
99
+
100
+ o.on_completed do
101
+ begin
102
+ observer.on_completed
103
+ rescue => err
104
+ obs.on_error err
105
+ end
106
+
107
+ obs.on_completed
108
+ end
109
+
110
+ end
111
+
112
+ subscribe new_obs
113
+ end
114
+ end
115
+
116
+ # Invokes a specified action after the source observable sequence terminates gracefully or exceptionally.
117
+ def ensures
118
+ AnonymousObservable.new do |observer|
119
+ subscription = subscribe observer
120
+ Subscription.create do
121
+ begin
122
+ subscription.unsubscribe
123
+ ensure
124
+ yield
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ # Ignores all elements in an observable sequence leaving only the termination messages.
131
+ def ignore_elements
132
+ AnonymousObservable.new do |observer|
133
+ new_obs = RxRuby::Observer.configure do |o|
134
+ o.on_next {|_| }
135
+ o.on_error(&observer.method(:on_error))
136
+ o.on_completed(&observer.method(:on_completed))
137
+ end
138
+
139
+ subscribe new_obs
140
+ end
141
+ end
142
+
143
+ # Materializes the implicit notifications of an observable sequence as explicit notification values.
144
+ def materialize
145
+ AnonymousObservable.new do |observer|
146
+ new_obs = RxRuby::Observer.configure do |o|
147
+
148
+ o.on_next {|x| observer.on_next(Notification.create_on_next x) }
149
+
150
+ o.on_error do |err|
151
+ observer.on_next(Notification.create_on_next err)
152
+ observer.on_completed
153
+ end
154
+
155
+ o.on_completed do
156
+ observer.on_next(Notification.create_on_completed)
157
+ observer.on_completed
158
+ end
159
+ end
160
+
161
+ subscribe new_obs
162
+ end
163
+ end
164
+
165
+ # Repeats the observable sequence indefinitely.
166
+ def repeat_infinitely
167
+ Observable.concat(enumerator_repeat_infinitely(self))
168
+ end
169
+
170
+ # Repeats the observable sequence a specified number of times.
171
+ def repeat(repeat_count)
172
+ Observable.concat(enumerator_repeat_times(repeat_count, self))
173
+ end
174
+
175
+ # Repeats the source observable sequence until it successfully terminates.
176
+ def retry_infinitely
177
+ Observable.rescue_error(enumerator_repeat_infinitely(self))
178
+ end
179
+
180
+ # Repeats the source observable sequence the specified number of times or until it successfully terminates.
181
+ def retry(retry_count)
182
+ Observable.rescue_error(enumerator_repeat_times(retry_count, self))
183
+ end
184
+
185
+ # Applies an accumulator function over an observable sequence and returns each intermediate result.
186
+ # The optional seed value is used as the initial accumulator value.
187
+ # For aggregation behavior with no intermediate results, see Observable.reduce.
188
+ def scan(*args, &block)
189
+ has_seed = false
190
+ seed = nil
191
+ action = nil
192
+
193
+ # Argument parsing to support:
194
+ # 1. (seed, Symbol)
195
+ # 2. (seed, &block)
196
+ # 3. (Symbol)
197
+ # 4. (&block)
198
+ if args.length == 2 && args[1].is_a?(Symbol)
199
+ seed = args[0]
200
+ action = args[1].to_proc
201
+ has_seed = true
202
+ elsif args.length == 1 && block_given?
203
+ seed = args[0]
204
+ has_seed = true
205
+ action = block
206
+ elsif args.length == 1 && args[0].is_a?(Symbol)
207
+ action = args[0].to_proc
208
+ elsif args.length == 0 && block_given?
209
+ action = block
210
+ else
211
+ raise ArgumentError.new 'Invalid arguments'
212
+ end
213
+
214
+ AnonymousObservable.new do |observer|
215
+
216
+ has_accumulation = false
217
+ accumulation = nil
218
+ has_value = false
219
+
220
+ new_obs = Observer.configure do |o|
221
+ o.on_next do |x|
222
+ begin
223
+ has_value = true unless has_value
224
+
225
+ if has_accumulation
226
+ accumulation = action.call(accumulation, x)
227
+ else
228
+ accumulation = has_seed ? action.call(seed, x) : x
229
+ has_accumulation = true
230
+ end
231
+ rescue => err
232
+ observer.on_error err
233
+ return
234
+ end
235
+
236
+ observer.on_next accumulation
237
+ end
238
+
239
+ o.on_error(&observer.method(:on_error))
240
+
241
+ o.on_completed do
242
+ observer.on_next seed if !has_value && has_seed
243
+ observer.on_completed
244
+ end
245
+ end
246
+
247
+ subscribe new_obs
248
+ end
249
+ end
250
+
251
+ # Bypasses a specified number of elements at the end of an observable sequence.
252
+ # @param [Numeric] count The number of elements to bypass at the end of an observable sequence.
253
+ def skip_last(count)
254
+ raise ArgumentError.new 'Count cannot be less than zero' if count < 0
255
+ AnonymousObservable.new do |observer|
256
+ q = []
257
+ new_obs = Observer.configure do |o|
258
+
259
+ o.on_next do |x|
260
+ q.push x
261
+ observer.on_next(q.shift) if q.length > count
262
+ end
263
+
264
+ o.on_error(&observer.method(:on_error))
265
+ o.on_completed(&observer.method(:on_completed))
266
+ end
267
+
268
+ subscribe new_obs
269
+ end
270
+ end
271
+
272
+ # Prepends a sequence of values to an observable sequence.
273
+ def start_with(*args)
274
+ scheduler = CurrentThreadScheduler.instance
275
+ if args.size > 0 && Scheduler === args[0]
276
+ scheduler = args.shift
277
+ end
278
+ Observable.from_array(args, scheduler).concat(self)
279
+ end
280
+
281
+ # Returns a specified number of contiguous elements from the end of an observable sequence.
282
+ def take_last(count, scheduler = CurrentThreadScheduler.instance)
283
+ raise ArgumentError.new 'Count cannot be less than zero' if count < 0
284
+ AnonymousObservable.new do |observer|
285
+ q = []
286
+ g = CompositeSubscription.new
287
+
288
+ new_obs = Observer.configure do |o|
289
+ o.on_next do |x|
290
+ q.push x
291
+ q.shift if q.length > 0
292
+ end
293
+
294
+ o.on_error(&observer.method(:on_error))
295
+
296
+ o.on_completed do
297
+ g.push(scheduler.schedule_recursive lambda {|this|
298
+ if q.length > 0
299
+ observer.on_next(q.shift)
300
+ this.call
301
+ else
302
+ observer.on_completed
303
+ end
304
+ })
305
+ end
306
+
307
+ g.add(subscribe new_obs)
308
+ g
309
+ end
310
+ end
311
+ end
312
+
313
+ # Returns a list with the specified number of contiguous elements from the end of an observable sequence.
314
+ def take_last_buffer(count)
315
+ AnonymousObservable.new do |observer|
316
+ q = []
317
+ new_obs = Observer.configure do |o|
318
+ o.on_next do |x|
319
+ q.push x
320
+ q.shift if q.length > count
321
+ end
322
+
323
+ o.on_error(&observer.method(:on_error))
324
+
325
+ o.on_completed do
326
+ observer.on_next q
327
+ observer.on_completed
328
+ end
329
+ end
330
+
331
+ susbcribe new_obs
332
+ end
333
+ end
334
+
335
+ # Projects each element of an observable sequence into zero or more windows which are produced based on element count information.
336
+ def window_with_count(count, skip)
337
+ raise ArgumentError.new 'Count must be greater than zero' if count <= 0
338
+ raise ArgumentError.new 'Skip must be greater than zero' if skip <= 0
339
+
340
+ AnonymousObservable.new do |observer|
341
+ q = []
342
+ n = 0
343
+
344
+ m = SingleAssignmentSubscription.new
345
+ ref_count_disposable = RefCountSubscription.new m
346
+
347
+ create_window = lambda {
348
+ s = Subject.new
349
+ q.push s
350
+ observer.on_next(s.add_ref(ref_count_disposable))
351
+ }
352
+
353
+ create_window.call
354
+
355
+ new_obs = Observer.configure do |o|
356
+ o.on_next do |x|
357
+ q.each {|s| s.on_next x}
358
+
359
+ c = n - count + 1
360
+ q.shift.on_completed if c >=0 && c % skip == 0
361
+
362
+ n += 1
363
+ create_window.call if n % skip == 0
364
+ end
365
+
366
+ o.on_error do |err|
367
+ q.shift.on_error err while q.length > 0
368
+ observer.on_error err
369
+ end
370
+
371
+ o.on_completed do
372
+ q.shift.on_completed while q.length > 0
373
+ observer.on_completed
374
+ end
375
+ end
376
+
377
+ m.subscription = subscribe new_obs
378
+ ref_count_disposable
379
+ end
380
+ end
381
+
382
+ def enumerator_repeat_times(num, value)
383
+ Enumerator.new do |y|
384
+ num.times do |i|
385
+ y << value
386
+ end
387
+ end
388
+ end
389
+
390
+ def enumerator_repeat_infinitely(value)
391
+ Enumerator.new do |y|
392
+ while true
393
+ y << value
394
+ end
395
+ end
396
+ end
397
+
398
+ end
399
+ end
@@ -0,0 +1,279 @@
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/current_thread_scheduler'
4
+ require 'rx_ruby/concurrency/immediate_scheduler'
5
+ require 'rx_ruby/core/observer'
6
+ require 'rx_ruby/subscriptions/composite_subscription'
7
+ require 'rx_ruby/subscriptions/subscription'
8
+
9
+ module RxRuby
10
+
11
+ module Observable
12
+
13
+ # Standard Query Operators
14
+
15
+ # Returns the elements of the specified sequence or the type parameter's default value in a singleton sequence if the sequence is empty.
16
+ def default_if_empty(default_value = nil)
17
+ AnonymousObservable.new do |observer|
18
+ found = false
19
+ new_observer = Observer.configure do |o|
20
+
21
+ o.on_next do |x|
22
+ found = true
23
+ observer.on_next x
24
+ end
25
+
26
+ o.on_error(&observer.method(:on_error))
27
+
28
+ o.on_completed do
29
+ observer.on_next(default_value) unless found
30
+ observer.on_completed
31
+ end
32
+ end
33
+
34
+ subscribe(new_observer)
35
+ end
36
+ end
37
+
38
+ # Returns an observable sequence that contains only distinct elements according to the optional key_selector.
39
+ def distinct(&key_selector)
40
+ key_selector ||= lambda {|x| x}
41
+
42
+ AnonymousObservable.new do |observer|
43
+
44
+ h = Hash.new
45
+
46
+ new_observer = Observer.configure do |o|
47
+
48
+ o.on_next do |x|
49
+ key = nil
50
+ has_added = false
51
+
52
+ begin
53
+ key = key_selector.call x
54
+ key_s = key.to_s
55
+ unless h.key? key_s
56
+ has_added = true
57
+ h[key_s] = true
58
+ end
59
+ rescue => e
60
+ observer.on_error e
61
+ next
62
+ end
63
+
64
+ observer.on_next x if has_added
65
+ end
66
+
67
+ o.on_error(&observer.method(:on_error))
68
+ o.on_completed(&observer.method(:on_completed))
69
+ end
70
+
71
+ subscribe(new_observer)
72
+ end
73
+ end
74
+
75
+ # Projects each element of an observable sequence into a new form by incorporating the element's index.
76
+ def map_with_index(&block)
77
+ AnonymousObservable.new do |observer|
78
+ new_observer = Observer.configure do |o|
79
+ i = 0
80
+
81
+ o.on_next do |x|
82
+ result = nil
83
+ begin
84
+ result = block.call(x, i)
85
+ i += 1
86
+ rescue => e
87
+ observer.on_error e
88
+ next
89
+ end
90
+
91
+ observer.on_next result
92
+ end
93
+
94
+ o.on_error(&observer.method(:on_error))
95
+ o.on_completed(&observer.method(:on_completed))
96
+ end
97
+
98
+ subscribe(new_observer)
99
+ end
100
+ end
101
+
102
+ # Projects each element of an observable sequence into a new form.
103
+ def map(&block)
104
+ map_with_index {|x, _| block.call x }
105
+ end
106
+
107
+ # Projects each element of the source observable sequence to the other observable sequence and merges the resulting observable sequences into one observable sequence.
108
+ def flat_map(&block)
109
+ map(&block).merge_all
110
+ end
111
+
112
+ # Projects each element of an observable sequence to an observable sequence by incorporating the element's index and merges the resulting observable sequences into one observable sequence.
113
+ def flat_map_with_index(&block)
114
+ map_with_index(&block).merge_all
115
+ end
116
+
117
+ # Bypasses a specified number of elements in an observable sequence and then returns the remaining elements.
118
+ def skip(count)
119
+ AnonymousObservable.new do |observer|
120
+ remaining = count
121
+
122
+ new_observer = Observer.configure do |o|
123
+
124
+ o.on_next do |x|
125
+ if remaining <= 0
126
+ observer.on_next x
127
+ else
128
+ remaining -= 1
129
+ end
130
+ end
131
+
132
+ o.on_error(&observer.method(:on_error))
133
+ o.on_completed(&observer.method(:on_completed))
134
+ end
135
+
136
+
137
+ subscribe(new_observer)
138
+ end
139
+ end
140
+
141
+ # Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements.
142
+ def skip_while(&block)
143
+ skip_while_with_index {|x, _| block.call x }
144
+ end
145
+
146
+ # Bypasses elements in an observable sequence as long as a specified condition is true and then returns the remaining elements.
147
+ # The element's index is used in the logic of the predicate function.
148
+ def skip_while_with_index(&block)
149
+ AnonymousObservable.new do |observer|
150
+ running = false
151
+ i = 0
152
+
153
+ new_observer = Observer.configure do |o|
154
+
155
+ o.on_next do |x|
156
+ unless running
157
+ begin
158
+ running = !block.call(x, i)
159
+ i += 1
160
+ rescue => e
161
+ observer.on_error e
162
+ next
163
+ end
164
+
165
+ observer.on_next x if running
166
+ end
167
+ end
168
+
169
+ o.on_error(&observer.method(:on_error))
170
+ o.on_completed(&observer.method(:on_completed))
171
+ end
172
+
173
+ subscribe(new_observer)
174
+ end
175
+ end
176
+
177
+ # Returns a specified number of contiguous elements from the start of an observable sequence.
178
+ def take(count, scheduler = ImmediateScheduler.instance)
179
+ return Observable.empty(scheduler) if count == 0
180
+
181
+ AnonymousObservable.new do |observer|
182
+
183
+ remaining = count
184
+
185
+ new_observer = Observer.configure do |o|
186
+
187
+ o.on_next do |x|
188
+ if remaining > 0
189
+ remaining -= 1
190
+ observer.on_next x
191
+ observer.on_completed if remaining == 0
192
+ end
193
+ end
194
+
195
+ o.on_error(&observer.method(:on_error))
196
+ o.on_completed(&observer.method(:on_completed))
197
+ end
198
+
199
+ subscribe(new_observer)
200
+ end
201
+ end
202
+
203
+ # Returns elements from an observable sequence as long as a specified condition is true.
204
+ def take_while(&block)
205
+ take_while_with_index {|x, _| block.call x }
206
+ end
207
+
208
+ # Returns elements from an observable sequence as long as a specified condition is true.
209
+ # The element's index is used in the logic of the predicate function.
210
+ def take_while_with_index(&block)
211
+ AnonymousObservable.new do |observer|
212
+ running = true
213
+ i = 0
214
+
215
+ new_observer = Observer.configure do |o|
216
+
217
+ o.on_next do |x|
218
+ if running
219
+ begin
220
+ running = block.call(x, i)
221
+ i += 1
222
+ rescue => e
223
+ observer.on_error e
224
+ next
225
+ end
226
+
227
+ if running
228
+ observer.on_next x
229
+ else
230
+ observer.on_completed
231
+ end
232
+ end
233
+ end
234
+
235
+ o.on_error(&observer.method(:on_error))
236
+ o.on_completed(&observer.method(:on_completed))
237
+ end
238
+
239
+ subscribe(new_observer)
240
+ end
241
+ end
242
+
243
+ # Filters the elements of an observable sequence based on a predicate.
244
+ def select(&block)
245
+ select_with_index {|x, _| block.call x }
246
+ end
247
+ alias_method :find_all, :select
248
+
249
+ # Filters the elements of an observable sequence based on a predicate by incorporating the element's index.
250
+ def select_with_index(&block)
251
+ AnonymousObservable.new do |observer|
252
+ i = 0
253
+
254
+ new_observer = Observer.configure do |o|
255
+
256
+ o.on_next do |x|
257
+ should_run = false
258
+ begin
259
+ should_run = block.call(x, i)
260
+ i += 1
261
+ rescue => e
262
+ observer.on_error e
263
+ next
264
+ end
265
+
266
+ observer.on_next x if should_run
267
+ end
268
+
269
+ o.on_error(&observer.method(:on_error))
270
+ o.on_completed(&observer.method(:on_completed))
271
+ end
272
+
273
+ subscribe(new_observer)
274
+ end
275
+ end
276
+
277
+ alias_method :find_all_with_index, :select_with_index
278
+ end
279
+ end