rx_ruby 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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