fiber_stream 0.4.0 → 0.6.0
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 +4 -4
- data/CHANGELOG.md +48 -0
- data/README.md +168 -12
- data/examples/README.md +5 -0
- data/examples/ractor_unordered_map_hashing.rb +62 -0
- data/lib/fiber_stream/flow.rb +123 -0
- data/lib/fiber_stream/pull/compact.rb +39 -0
- data/lib/fiber_stream/pull/filter_map.rb +41 -0
- data/lib/fiber_stream/pull/map_concat.rb +56 -0
- data/lib/fiber_stream/pull/ractor_unordered_map_boundary.rb +521 -0
- data/lib/fiber_stream/pull/reject.rb +40 -0
- data/lib/fiber_stream/pull/tap.rb +38 -0
- data/lib/fiber_stream/pull/throttle.rb +43 -0
- data/lib/fiber_stream/pull.rb +39 -3
- data/lib/fiber_stream/rate_limiter.rb +163 -0
- data/lib/fiber_stream/sink.rb +68 -0
- data/lib/fiber_stream/source.rb +66 -0
- data/lib/fiber_stream/version.rb +1 -1
- data/lib/fiber_stream.rb +1 -0
- data/sig/fiber_stream.rbs +30 -0
- metadata +11 -2
data/lib/fiber_stream/pull.rb
CHANGED
|
@@ -85,6 +85,22 @@ module FiberStream
|
|
|
85
85
|
Map.new(upstream, transform)
|
|
86
86
|
end
|
|
87
87
|
|
|
88
|
+
def self.filter_map(upstream, transform)
|
|
89
|
+
FilterMap.new(upstream, transform)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.compact(upstream)
|
|
93
|
+
Compact.new(upstream)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def self.map_concat(upstream, transform)
|
|
97
|
+
MapConcat.new(upstream, transform)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.tap(upstream, observer)
|
|
101
|
+
Tap.new(upstream, observer)
|
|
102
|
+
end
|
|
103
|
+
|
|
88
104
|
def self.parallel_map(upstream, concurrency, transform)
|
|
89
105
|
ParallelMapBoundary.new(upstream, concurrency, transform)
|
|
90
106
|
end
|
|
@@ -97,10 +113,18 @@ module FiberStream
|
|
|
97
113
|
RactorMapBoundary.new(upstream, workers, input_transfer, output_transfer, transform)
|
|
98
114
|
end
|
|
99
115
|
|
|
116
|
+
def self.ractor_unordered_map(upstream, workers, input_transfer, output_transfer, transform)
|
|
117
|
+
RactorUnorderedMapBoundary.new(upstream, workers, input_transfer, output_transfer, transform)
|
|
118
|
+
end
|
|
119
|
+
|
|
100
120
|
def self.select(upstream, predicate)
|
|
101
121
|
Select.new(upstream, predicate)
|
|
102
122
|
end
|
|
103
123
|
|
|
124
|
+
def self.reject(upstream, predicate)
|
|
125
|
+
Reject.new(upstream, predicate)
|
|
126
|
+
end
|
|
127
|
+
|
|
104
128
|
def self.take(upstream, count)
|
|
105
129
|
Take.new(upstream, count)
|
|
106
130
|
end
|
|
@@ -133,6 +157,10 @@ module FiberStream
|
|
|
133
157
|
BufferBoundary.new(upstream, count)
|
|
134
158
|
end
|
|
135
159
|
|
|
160
|
+
def self.throttle(upstream, limiter)
|
|
161
|
+
Throttle.new(upstream, limiter)
|
|
162
|
+
end
|
|
163
|
+
|
|
136
164
|
def self.lines(upstream, chomp, max_length)
|
|
137
165
|
Lines.new(upstream, chomp, max_length)
|
|
138
166
|
end
|
|
@@ -154,7 +182,12 @@ require_relative "pull/concat"
|
|
|
154
182
|
require_relative "pull/zip"
|
|
155
183
|
require_relative "pull/merge"
|
|
156
184
|
require_relative "pull/map"
|
|
185
|
+
require_relative "pull/filter_map"
|
|
186
|
+
require_relative "pull/compact"
|
|
187
|
+
require_relative "pull/map_concat"
|
|
188
|
+
require_relative "pull/tap"
|
|
157
189
|
require_relative "pull/select"
|
|
190
|
+
require_relative "pull/reject"
|
|
158
191
|
require_relative "pull/take"
|
|
159
192
|
require_relative "pull/drop"
|
|
160
193
|
require_relative "pull/grouped"
|
|
@@ -165,15 +198,18 @@ require_relative "pull/lines"
|
|
|
165
198
|
require_relative "pull/split"
|
|
166
199
|
require_relative "pull/async_boundary"
|
|
167
200
|
require_relative "pull/buffer_boundary"
|
|
201
|
+
require_relative "pull/throttle"
|
|
168
202
|
require_relative "pull/parallel_map_boundary"
|
|
169
203
|
require_relative "pull/parallel_unordered_map_boundary"
|
|
170
204
|
require_relative "pull/ractor_map_boundary"
|
|
205
|
+
require_relative "pull/ractor_unordered_map_boundary"
|
|
171
206
|
|
|
172
207
|
module FiberStream
|
|
173
208
|
module Pull
|
|
174
209
|
private_constant :Each, :IOSource, :RactorPortSource, :RactorMergePortsSource, :RactorProducerSource, :Concat,
|
|
175
|
-
:Zip, :Merge, :Map, :
|
|
176
|
-
:
|
|
177
|
-
:RactorMapBoundary
|
|
210
|
+
:Zip, :Merge, :Map, :FilterMap, :Compact, :MapConcat, :Tap, :Select, :Reject, :Take, :Drop,
|
|
211
|
+
:Grouped, :Scan, :TakeWhile, :DropWhile, :Lines, :Split, :AsyncBoundary, :BufferBoundary,
|
|
212
|
+
:Throttle, :ParallelMapBoundary, :ParallelUnorderedMapBoundary, :RactorMapBoundary,
|
|
213
|
+
:RactorUnorderedMapBoundary
|
|
178
214
|
end
|
|
179
215
|
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FiberStream
|
|
4
|
+
# Scheduler-aware token-bucket rate limiter.
|
|
5
|
+
#
|
|
6
|
+
# `rate` permits refill every `per` seconds. `burst` is the maximum token
|
|
7
|
+
# capacity and defaults to `rate`. Immediate permit grants do not require a
|
|
8
|
+
# scheduler. When FiberStream must sleep, the current fiber must be
|
|
9
|
+
# non-blocking with an installed `Fiber.scheduler`.
|
|
10
|
+
class RateLimiter
|
|
11
|
+
Request = Data.define(:rate, :per, :burst, :permits, :now)
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def validate_options!(rate:, per: 1, burst: nil) # :nodoc:
|
|
15
|
+
rate = validate_rate(rate)
|
|
16
|
+
validate_duration(:per, per)
|
|
17
|
+
validate_burst(burst.nil? ? rate : burst)
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def validate_rate(rate)
|
|
24
|
+
raise TypeError, "rate must be an Integer" unless rate.is_a?(Integer)
|
|
25
|
+
raise ArgumentError, "rate must be positive" unless rate.positive?
|
|
26
|
+
|
|
27
|
+
rate
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate_burst(burst)
|
|
31
|
+
raise TypeError, "burst must be an Integer" unless burst.is_a?(Integer)
|
|
32
|
+
raise ArgumentError, "burst must be positive" unless burst.positive?
|
|
33
|
+
|
|
34
|
+
burst
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validate_duration(name, duration)
|
|
38
|
+
raise TypeError, "#{name} must be Numeric" unless duration.is_a?(Numeric)
|
|
39
|
+
raise ArgumentError, "#{name} must be finite and real" unless finite_real?(duration)
|
|
40
|
+
raise ArgumentError, "#{name} must be positive" unless duration.positive?
|
|
41
|
+
|
|
42
|
+
duration
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def finite_real?(value)
|
|
46
|
+
return false if value.is_a?(Complex)
|
|
47
|
+
return value.finite? if value.respond_to?(:finite?)
|
|
48
|
+
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def initialize(rate:, per: 1, burst: nil, &block)
|
|
54
|
+
self.class.validate_options!(rate:, per:, burst:)
|
|
55
|
+
|
|
56
|
+
@rate = rate
|
|
57
|
+
@per = per
|
|
58
|
+
@burst = burst.nil? ? @rate : burst
|
|
59
|
+
@policy = block
|
|
60
|
+
@mutex = Mutex.new
|
|
61
|
+
@tokens = @burst.to_f
|
|
62
|
+
@updated_at = monotonic_now
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Acquires `permits`, waiting when necessary.
|
|
66
|
+
#
|
|
67
|
+
# Waits are scheduler-backed and non-blocking. Requests larger than `burst`
|
|
68
|
+
# are rejected because the local token bucket could never satisfy them.
|
|
69
|
+
def acquire(permits: 1)
|
|
70
|
+
permits = validate_permits(permits)
|
|
71
|
+
|
|
72
|
+
if @policy
|
|
73
|
+
acquire_with_policy(permits)
|
|
74
|
+
else
|
|
75
|
+
acquire_with_token_bucket(permits)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def acquire_with_policy(permits)
|
|
84
|
+
loop do
|
|
85
|
+
wait = normalize_policy_wait(@policy.call(request_for(permits)))
|
|
86
|
+
return if wait <= 0
|
|
87
|
+
|
|
88
|
+
scheduler_sleep(wait)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def acquire_with_token_bucket(permits)
|
|
93
|
+
loop do
|
|
94
|
+
wait = nil
|
|
95
|
+
|
|
96
|
+
@mutex.synchronize do
|
|
97
|
+
refill_tokens
|
|
98
|
+
|
|
99
|
+
if @tokens >= permits
|
|
100
|
+
@tokens -= permits
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
wait = (permits - @tokens) / permits_per_second
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
scheduler_sleep(wait)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def request_for(permits)
|
|
112
|
+
Request.new(rate: @rate, per: @per, burst: @burst, permits:, now: monotonic_now)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def refill_tokens
|
|
116
|
+
now = monotonic_now
|
|
117
|
+
elapsed = now - @updated_at
|
|
118
|
+
@updated_at = now
|
|
119
|
+
@tokens = [@burst, @tokens + (elapsed * permits_per_second)].min
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def permits_per_second
|
|
123
|
+
@rate.to_f / @per.to_f
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def scheduler_sleep(duration)
|
|
127
|
+
validate_scheduler!
|
|
128
|
+
sleep(duration)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def validate_scheduler!
|
|
132
|
+
return if Fiber.scheduler && !Fiber.current.blocking?
|
|
133
|
+
|
|
134
|
+
message =
|
|
135
|
+
if Fiber.scheduler
|
|
136
|
+
"RateLimiter#acquire requires a non-blocking fiber"
|
|
137
|
+
else
|
|
138
|
+
"RateLimiter#acquire requires Fiber.scheduler"
|
|
139
|
+
end
|
|
140
|
+
raise SchedulerRequiredError, message
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def validate_permits(permits)
|
|
144
|
+
raise TypeError, "permits must be an Integer" unless permits.is_a?(Integer)
|
|
145
|
+
raise ArgumentError, "permits must be positive" unless permits.positive?
|
|
146
|
+
raise ArgumentError, "permits must be less than or equal to burst" if permits > @burst
|
|
147
|
+
|
|
148
|
+
permits
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def normalize_policy_wait(wait)
|
|
152
|
+
return 0 if wait.nil?
|
|
153
|
+
raise TypeError, "custom wait duration must be Numeric or nil" unless wait.is_a?(Numeric)
|
|
154
|
+
raise ArgumentError, "custom wait duration must be finite and real" unless self.class.send(:finite_real?, wait)
|
|
155
|
+
|
|
156
|
+
wait.positive? ? wait : 0
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def monotonic_now
|
|
160
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
data/lib/fiber_stream/sink.rb
CHANGED
|
@@ -29,6 +29,74 @@ module FiberStream
|
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# Creates a sink that returns the first element matching a predicate.
|
|
33
|
+
#
|
|
34
|
+
# The sink pulls upstream until the block returns a truthy value or
|
|
35
|
+
# upstream completes. It returns the original matching element, or `nil`
|
|
36
|
+
# when no element matches. Matching `nil` elements are returned as `nil`,
|
|
37
|
+
# following Ruby's `Enumerable#find` ambiguity.
|
|
38
|
+
def self.find(&block)
|
|
39
|
+
raise ArgumentError, "missing block" unless block
|
|
40
|
+
|
|
41
|
+
new do |stream|
|
|
42
|
+
loop do
|
|
43
|
+
value = stream.next
|
|
44
|
+
break nil if Pull.done?(value)
|
|
45
|
+
break value if block.call(value)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Creates a sink that returns whether any element matches a predicate.
|
|
51
|
+
#
|
|
52
|
+
# The sink pulls upstream until the block returns a truthy value or upstream
|
|
53
|
+
# completes. It returns `true` after a truthy predicate result and `false`
|
|
54
|
+
# when no element matches.
|
|
55
|
+
def self.any?(&block)
|
|
56
|
+
raise ArgumentError, "missing block" unless block
|
|
57
|
+
|
|
58
|
+
new do |stream|
|
|
59
|
+
loop do
|
|
60
|
+
value = stream.next
|
|
61
|
+
break false if Pull.done?(value)
|
|
62
|
+
break true if block.call(value)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Creates a sink that returns whether all elements match a predicate.
|
|
68
|
+
#
|
|
69
|
+
# The sink pulls upstream until the block returns false or nil, or upstream
|
|
70
|
+
# completes. It returns `false` after a falsey predicate result and `true`
|
|
71
|
+
# when every predicate result is truthy, including for empty upstream.
|
|
72
|
+
def self.all?(&block)
|
|
73
|
+
raise ArgumentError, "missing block" unless block
|
|
74
|
+
|
|
75
|
+
new do |stream|
|
|
76
|
+
loop do
|
|
77
|
+
value = stream.next
|
|
78
|
+
break true if Pull.done?(value)
|
|
79
|
+
break false unless block.call(value)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Creates a sink that counts all stream elements.
|
|
85
|
+
#
|
|
86
|
+
# The sink consumes upstream until normal completion and returns the number
|
|
87
|
+
# of elements observed. It does not store consumed elements.
|
|
88
|
+
def self.count
|
|
89
|
+
new do |stream|
|
|
90
|
+
count = 0
|
|
91
|
+
|
|
92
|
+
Pull.each_value(stream) do
|
|
93
|
+
count += 1
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
count
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
32
100
|
# Creates a sink that folds all stream elements into an accumulator.
|
|
33
101
|
#
|
|
34
102
|
# The sink consumes upstream until normal completion. It returns the final
|
data/lib/fiber_stream/source.rb
CHANGED
|
@@ -178,6 +178,35 @@ module FiberStream
|
|
|
178
178
|
via(Flow.map(&block))
|
|
179
179
|
end
|
|
180
180
|
|
|
181
|
+
# Returns a new source definition that emits truthy transformed values.
|
|
182
|
+
#
|
|
183
|
+
# This is a convenience wrapper around
|
|
184
|
+
# `via(FiberStream::Flow.filter_map { ... })` and has the same falsey-drop,
|
|
185
|
+
# lazy construction, error, and backpressure behavior as the underlying
|
|
186
|
+
# flow.
|
|
187
|
+
def filter_map(&block)
|
|
188
|
+
via(Flow.filter_map(&block))
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Returns a new source definition that drops nil elements.
|
|
192
|
+
#
|
|
193
|
+
# This is a convenience wrapper around `via(FiberStream::Flow.compact)` and
|
|
194
|
+
# preserves the same nil-only filtering, lazy construction, and
|
|
195
|
+
# backpressure behavior as the underlying flow.
|
|
196
|
+
def compact
|
|
197
|
+
via(Flow.compact)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Returns a new source definition that emits each mapped expansion.
|
|
201
|
+
#
|
|
202
|
+
# This is a convenience wrapper around
|
|
203
|
+
# `via(FiberStream::Flow.map_concat { ... })` and has the same one-level
|
|
204
|
+
# flattening, lazy construction, error, and backpressure behavior as the
|
|
205
|
+
# underlying flow.
|
|
206
|
+
def map_concat(&block)
|
|
207
|
+
via(Flow.map_concat(&block))
|
|
208
|
+
end
|
|
209
|
+
|
|
181
210
|
# Returns a new source definition that maps elements concurrently.
|
|
182
211
|
#
|
|
183
212
|
# This is a convenience wrapper around
|
|
@@ -217,6 +246,25 @@ module FiberStream
|
|
|
217
246
|
)
|
|
218
247
|
end
|
|
219
248
|
|
|
249
|
+
# Returns a new source definition that maps elements in Ractor workers and
|
|
250
|
+
# emits mapped values as workers complete.
|
|
251
|
+
#
|
|
252
|
+
# This is a convenience wrapper around
|
|
253
|
+
# `via(FiberStream::Flow.ractor_unordered_map(workers:) { ... })` and
|
|
254
|
+
# preserves the same shareable mapper requirement, unordered delivery,
|
|
255
|
+
# transfer policy, bounded upstream run-ahead, and cooperative worker
|
|
256
|
+
# shutdown behavior.
|
|
257
|
+
def ractor_unordered_map(workers:, input_transfer: :copy, output_transfer: :copy, &block)
|
|
258
|
+
via(
|
|
259
|
+
Flow.ractor_unordered_map(
|
|
260
|
+
workers: workers,
|
|
261
|
+
input_transfer: input_transfer,
|
|
262
|
+
output_transfer: output_transfer,
|
|
263
|
+
&block
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
end
|
|
267
|
+
|
|
220
268
|
# Returns a new source definition that keeps elements matching `block`.
|
|
221
269
|
#
|
|
222
270
|
# This is a convenience wrapper around
|
|
@@ -226,6 +274,15 @@ module FiberStream
|
|
|
226
274
|
via(Flow.select(&block))
|
|
227
275
|
end
|
|
228
276
|
|
|
277
|
+
# Returns a new source definition that drops elements matching `block`.
|
|
278
|
+
#
|
|
279
|
+
# This is a convenience wrapper around
|
|
280
|
+
# `via(FiberStream::Flow.reject { ... })` and has the same truthiness and
|
|
281
|
+
# lazy construction behavior as the underlying flow.
|
|
282
|
+
def reject(&block)
|
|
283
|
+
via(Flow.reject(&block))
|
|
284
|
+
end
|
|
285
|
+
|
|
229
286
|
# Returns a new source definition that emits at most `count` elements.
|
|
230
287
|
#
|
|
231
288
|
# This is a convenience wrapper around `via(FiberStream::Flow.take(count))`
|
|
@@ -297,6 +354,15 @@ module FiberStream
|
|
|
297
354
|
via(Flow.buffer(count))
|
|
298
355
|
end
|
|
299
356
|
|
|
357
|
+
# Returns a new source definition that rate-limits emitted elements.
|
|
358
|
+
#
|
|
359
|
+
# This is a convenience wrapper around `via(FiberStream::Flow.throttle(...))`.
|
|
360
|
+
# The `rate:` form creates a fresh default limiter for each materialization;
|
|
361
|
+
# pass `limiter:` to share quota state across sources or runs.
|
|
362
|
+
def throttle(**options)
|
|
363
|
+
via(Flow.throttle(**options))
|
|
364
|
+
end
|
|
365
|
+
|
|
300
366
|
# Returns a new source definition that splits String chunks into lines.
|
|
301
367
|
#
|
|
302
368
|
# This is a convenience wrapper around
|
data/lib/fiber_stream/version.rb
CHANGED
data/lib/fiber_stream.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "fiber_stream/pull"
|
|
4
4
|
require_relative "fiber_stream/version"
|
|
5
5
|
require_relative "fiber_stream/errors"
|
|
6
|
+
require_relative "fiber_stream/rate_limiter"
|
|
6
7
|
require_relative "fiber_stream/internal/ractor_transfer_policy"
|
|
7
8
|
require_relative "fiber_stream/ractor_port"
|
|
8
9
|
require_relative "fiber_stream/ractor_producer"
|
data/sig/fiber_stream.rbs
CHANGED
|
@@ -14,6 +14,19 @@ module FiberStream
|
|
|
14
14
|
class PipelineCancelledError < RuntimeError
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
class RateLimiter
|
|
18
|
+
class Request < Data
|
|
19
|
+
attr_reader rate: Integer
|
|
20
|
+
attr_reader per: Numeric
|
|
21
|
+
attr_reader burst: Integer
|
|
22
|
+
attr_reader permits: Integer
|
|
23
|
+
attr_reader now: Float
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def initialize: (rate: Integer, ?per: Numeric, ?burst: Integer?) ?{ (Request request) -> Numeric? } -> void
|
|
27
|
+
def acquire: (?permits: Integer) -> nil
|
|
28
|
+
end
|
|
29
|
+
|
|
17
30
|
class RactorPortSourceError < RuntimeError
|
|
18
31
|
attr_reader kind: ractor_port_source_error_kind
|
|
19
32
|
attr_reader cause_class_name: String
|
|
@@ -74,7 +87,12 @@ module FiberStream
|
|
|
74
87
|
def parallel_map: [Out] (concurrency: Integer) { (Elem) -> Out } -> Source[Out]
|
|
75
88
|
def parallel_unordered_map: [Out] (concurrency: Integer) { (Elem) -> Out } -> Source[Out]
|
|
76
89
|
def ractor_map: [Out] (workers: Integer, ?input_transfer: ractor_transfer_policy, ?output_transfer: ractor_transfer_policy) { (Elem) -> Out } -> Source[Out]
|
|
90
|
+
def ractor_unordered_map: [Out] (workers: Integer, ?input_transfer: ractor_transfer_policy, ?output_transfer: ractor_transfer_policy) { (Elem) -> Out } -> Source[Out]
|
|
91
|
+
def filter_map: [Out] () { (Elem) -> (Out | false | nil) } -> Source[Out]
|
|
92
|
+
def compact: () -> Source[Elem]
|
|
93
|
+
def map_concat: [Out] () { (Elem) -> Enumerable[Out] } -> Source[Out]
|
|
77
94
|
def select: () { (Elem) -> boolish } -> Source[Elem]
|
|
95
|
+
def reject: () { (Elem) -> boolish } -> Source[Elem]
|
|
78
96
|
def take: (Integer count) -> Source[Elem]
|
|
79
97
|
def drop: (Integer count) -> Source[Elem]
|
|
80
98
|
def grouped: (Integer count) -> Source[Array[Elem]]
|
|
@@ -83,6 +101,7 @@ module FiberStream
|
|
|
83
101
|
def drop_while: () { (Elem) -> boolish } -> Source[Elem]
|
|
84
102
|
def async: () -> Source[Elem]
|
|
85
103
|
def buffer: (Integer count) -> Source[Elem]
|
|
104
|
+
def throttle: (?rate: Integer, ?per: Numeric, ?burst: Integer?, ?limiter: untyped) -> Source[Elem]
|
|
86
105
|
def lines: (?chomp: bool, ?max_length: Integer?) -> Source[String]
|
|
87
106
|
def split: (String separator, ?keep_separator: bool, ?max_length: Integer?) -> Source[String]
|
|
88
107
|
def to: [Mat] (Sink[Elem, Mat] sink) -> Pipeline[Mat]
|
|
@@ -91,10 +110,16 @@ module FiberStream
|
|
|
91
110
|
|
|
92
111
|
class Flow[In, Out]
|
|
93
112
|
def self.map: [In, Out] () { (In) -> Out } -> Flow[In, Out]
|
|
113
|
+
def self.tap: [Elem] () { (Elem) -> void } -> Flow[Elem, Elem]
|
|
94
114
|
def self.parallel_map: [In, Out] (concurrency: Integer) { (In) -> Out } -> Flow[In, Out]
|
|
95
115
|
def self.parallel_unordered_map: [In, Out] (concurrency: Integer) { (In) -> Out } -> Flow[In, Out]
|
|
96
116
|
def self.ractor_map: [In, Out] (workers: Integer, ?input_transfer: ractor_transfer_policy, ?output_transfer: ractor_transfer_policy) { (In) -> Out } -> Flow[In, Out]
|
|
117
|
+
def self.ractor_unordered_map: [In, Out] (workers: Integer, ?input_transfer: ractor_transfer_policy, ?output_transfer: ractor_transfer_policy) { (In) -> Out } -> Flow[In, Out]
|
|
118
|
+
def self.filter_map: [In, Out] () { (In) -> (Out | false | nil) } -> Flow[In, Out]
|
|
119
|
+
def self.compact: [Elem] () -> Flow[Elem, Elem]
|
|
120
|
+
def self.map_concat: [In, Out] () { (In) -> Enumerable[Out] } -> Flow[In, Out]
|
|
97
121
|
def self.select: [Elem] () { (Elem) -> boolish } -> Flow[Elem, Elem]
|
|
122
|
+
def self.reject: [Elem] () { (Elem) -> boolish } -> Flow[Elem, Elem]
|
|
98
123
|
def self.take: [Elem] (Integer count) -> Flow[Elem, Elem]
|
|
99
124
|
def self.drop: [Elem] (Integer count) -> Flow[Elem, Elem]
|
|
100
125
|
def self.grouped: [Elem] (Integer count) -> Flow[Elem, Array[Elem]]
|
|
@@ -103,6 +128,7 @@ module FiberStream
|
|
|
103
128
|
def self.drop_while: [Elem] () { (Elem) -> boolish } -> Flow[Elem, Elem]
|
|
104
129
|
def self.async: [Elem] () -> Flow[Elem, Elem]
|
|
105
130
|
def self.buffer: [Elem] (Integer count) -> Flow[Elem, Elem]
|
|
131
|
+
def self.throttle: [Elem] (?rate: Integer, ?per: Numeric, ?burst: Integer?, ?limiter: untyped) -> Flow[Elem, Elem]
|
|
106
132
|
def self.lines: (?chomp: bool, ?max_length: Integer?) -> Flow[String, String]
|
|
107
133
|
def self.split: (String separator, ?keep_separator: bool, ?max_length: Integer?) -> Flow[String, String]
|
|
108
134
|
def via: [Next] (Flow[Out, Next] flow) -> Flow[In, Next]
|
|
@@ -112,6 +138,10 @@ module FiberStream
|
|
|
112
138
|
class Sink[In, Mat]
|
|
113
139
|
def self.to_a: [Elem] () -> Sink[Elem, Array[Elem]]
|
|
114
140
|
def self.first: [Elem] () -> Sink[Elem, Elem?]
|
|
141
|
+
def self.find: [Elem] () { (Elem) -> boolish } -> Sink[Elem, Elem?]
|
|
142
|
+
def self.any?: [Elem] () { (Elem) -> boolish } -> Sink[Elem, bool]
|
|
143
|
+
def self.all?: [Elem] () { (Elem) -> boolish } -> Sink[Elem, bool]
|
|
144
|
+
def self.count: [Elem] () -> Sink[Elem, Integer]
|
|
115
145
|
def self.fold: [Elem, Acc] (Acc initial) { (Acc, Elem) -> Acc } -> Sink[Elem, Acc]
|
|
116
146
|
def self.foreach: [Elem] () { (Elem) -> void } -> Sink[Elem, Integer]
|
|
117
147
|
def self.io: (untyped io, ?close: bool, ?flush: bool) -> Sink[String, Integer]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fiber_stream
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dai Akatsuka
|
|
@@ -120,6 +120,7 @@ files:
|
|
|
120
120
|
- examples/ractor_merge_ports_and_map.rb
|
|
121
121
|
- examples/ractor_port_source.rb
|
|
122
122
|
- examples/ractor_producer_sources.rb
|
|
123
|
+
- examples/ractor_unordered_map_hashing.rb
|
|
123
124
|
- lib/fiber_stream.rb
|
|
124
125
|
- lib/fiber_stream/errors.rb
|
|
125
126
|
- lib/fiber_stream/flow.rb
|
|
@@ -128,14 +129,17 @@ files:
|
|
|
128
129
|
- lib/fiber_stream/pull.rb
|
|
129
130
|
- lib/fiber_stream/pull/async_boundary.rb
|
|
130
131
|
- lib/fiber_stream/pull/buffer_boundary.rb
|
|
132
|
+
- lib/fiber_stream/pull/compact.rb
|
|
131
133
|
- lib/fiber_stream/pull/concat.rb
|
|
132
134
|
- lib/fiber_stream/pull/drop.rb
|
|
133
135
|
- lib/fiber_stream/pull/drop_while.rb
|
|
134
136
|
- lib/fiber_stream/pull/each.rb
|
|
137
|
+
- lib/fiber_stream/pull/filter_map.rb
|
|
135
138
|
- lib/fiber_stream/pull/grouped.rb
|
|
136
139
|
- lib/fiber_stream/pull/io_source.rb
|
|
137
140
|
- lib/fiber_stream/pull/lines.rb
|
|
138
141
|
- lib/fiber_stream/pull/map.rb
|
|
142
|
+
- lib/fiber_stream/pull/map_concat.rb
|
|
139
143
|
- lib/fiber_stream/pull/merge.rb
|
|
140
144
|
- lib/fiber_stream/pull/parallel_map_boundary.rb
|
|
141
145
|
- lib/fiber_stream/pull/parallel_unordered_map_boundary.rb
|
|
@@ -143,14 +147,19 @@ files:
|
|
|
143
147
|
- lib/fiber_stream/pull/ractor_merge_ports_source.rb
|
|
144
148
|
- lib/fiber_stream/pull/ractor_port_source.rb
|
|
145
149
|
- lib/fiber_stream/pull/ractor_producer_source.rb
|
|
150
|
+
- lib/fiber_stream/pull/ractor_unordered_map_boundary.rb
|
|
151
|
+
- lib/fiber_stream/pull/reject.rb
|
|
146
152
|
- lib/fiber_stream/pull/scan.rb
|
|
147
153
|
- lib/fiber_stream/pull/select.rb
|
|
148
154
|
- lib/fiber_stream/pull/split.rb
|
|
149
155
|
- lib/fiber_stream/pull/take.rb
|
|
150
156
|
- lib/fiber_stream/pull/take_while.rb
|
|
157
|
+
- lib/fiber_stream/pull/tap.rb
|
|
158
|
+
- lib/fiber_stream/pull/throttle.rb
|
|
151
159
|
- lib/fiber_stream/pull/zip.rb
|
|
152
160
|
- lib/fiber_stream/ractor_port.rb
|
|
153
161
|
- lib/fiber_stream/ractor_producer.rb
|
|
162
|
+
- lib/fiber_stream/rate_limiter.rb
|
|
154
163
|
- lib/fiber_stream/running_pipeline.rb
|
|
155
164
|
- lib/fiber_stream/sink.rb
|
|
156
165
|
- lib/fiber_stream/source.rb
|
|
@@ -162,7 +171,7 @@ licenses:
|
|
|
162
171
|
metadata:
|
|
163
172
|
allowed_push_host: https://rubygems.org
|
|
164
173
|
homepage_uri: https://github.com/dakatsuka/fiber_stream
|
|
165
|
-
source_code_uri: https://github.com/dakatsuka/fiber_stream/tree/v0.
|
|
174
|
+
source_code_uri: https://github.com/dakatsuka/fiber_stream/tree/v0.6.0
|
|
166
175
|
changelog_uri: https://github.com/dakatsuka/fiber_stream/blob/main/CHANGELOG.md
|
|
167
176
|
rubygems_mfa_required: 'true'
|
|
168
177
|
rdoc_options: []
|