fiber_stream 0.1.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 +7 -0
- data/CHANGELOG.md +25 -0
- data/LICENSE +19 -0
- data/README.md +361 -0
- data/examples/README.md +51 -0
- data/examples/async_http_requests.rb +132 -0
- data/examples/background_execution.rb +31 -0
- data/examples/backpressure_buffer.rb +66 -0
- data/examples/basic_pipeline.rb +28 -0
- data/examples/composable_pipeline.rb +43 -0
- data/examples/file_copy.rb +33 -0
- data/examples/line_processing.rb +20 -0
- data/examples/ractor_map_hashing.rb +43 -0
- data/examples/ractor_port_source.rb +45 -0
- data/lib/fiber_stream/errors.rb +44 -0
- data/lib/fiber_stream/flow.rb +190 -0
- data/lib/fiber_stream/pipeline.rb +49 -0
- data/lib/fiber_stream/pull/async_boundary.rb +85 -0
- data/lib/fiber_stream/pull/buffer_boundary.rb +123 -0
- data/lib/fiber_stream/pull/each.rb +31 -0
- data/lib/fiber_stream/pull/io_source.rb +89 -0
- data/lib/fiber_stream/pull/lines.rb +121 -0
- data/lib/fiber_stream/pull/map.rb +37 -0
- data/lib/fiber_stream/pull/parallel_map_boundary.rb +299 -0
- data/lib/fiber_stream/pull/ractor_map_boundary.rb +500 -0
- data/lib/fiber_stream/pull/ractor_port_source.rb +242 -0
- data/lib/fiber_stream/pull/select.rb +40 -0
- data/lib/fiber_stream/pull/take.rb +47 -0
- data/lib/fiber_stream/pull.rb +85 -0
- data/lib/fiber_stream/ractor_port.rb +17 -0
- data/lib/fiber_stream/running_pipeline.rb +156 -0
- data/lib/fiber_stream/sink.rb +176 -0
- data/lib/fiber_stream/source.rb +184 -0
- data/lib/fiber_stream/version.rb +5 -0
- data/lib/fiber_stream.rb +15 -0
- data/sig/fiber_stream.rbs +97 -0
- metadata +154 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FiberStream
|
|
4
|
+
class Sink
|
|
5
|
+
# Creates a sink that collects all stream elements into an Array.
|
|
6
|
+
#
|
|
7
|
+
# The sink consumes upstream until normal completion and returns the
|
|
8
|
+
# collected array as the stream materialized value.
|
|
9
|
+
def self.to_a
|
|
10
|
+
new do |stream|
|
|
11
|
+
values = []
|
|
12
|
+
|
|
13
|
+
loop do
|
|
14
|
+
value = stream.next
|
|
15
|
+
break if Pull.done?(value)
|
|
16
|
+
|
|
17
|
+
values << value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
values
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Creates a sink that returns the first stream element.
|
|
25
|
+
#
|
|
26
|
+
# The sink pulls at most one element. It returns `nil` when upstream
|
|
27
|
+
# completes before producing a value.
|
|
28
|
+
def self.first
|
|
29
|
+
new do |stream|
|
|
30
|
+
value = stream.next
|
|
31
|
+
Pull.done?(value) ? nil : value
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Creates a sink that folds all stream elements into an accumulator.
|
|
36
|
+
#
|
|
37
|
+
# The sink consumes upstream until normal completion. It returns the final
|
|
38
|
+
# accumulator, or the initial accumulator when upstream is empty. Exceptions
|
|
39
|
+
# raised by the block fail the stream and are re-raised from
|
|
40
|
+
# `Source#run_with`. FiberStream assigns the initial accumulator directly;
|
|
41
|
+
# it does not duplicate or freeze that object.
|
|
42
|
+
def self.fold(initial, &block)
|
|
43
|
+
raise ArgumentError, "missing block" unless block
|
|
44
|
+
|
|
45
|
+
new do |stream|
|
|
46
|
+
accumulator = initial
|
|
47
|
+
|
|
48
|
+
loop do
|
|
49
|
+
value = stream.next
|
|
50
|
+
break if Pull.done?(value)
|
|
51
|
+
|
|
52
|
+
accumulator = block.call(accumulator, value)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
accumulator
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Creates a sink that writes String chunks to an IO-like object.
|
|
60
|
+
#
|
|
61
|
+
# The sink consumes upstream until normal completion and returns the number
|
|
62
|
+
# of chunks successfully written. It requires a scheduler-backed
|
|
63
|
+
# non-blocking fiber before write, flush, or normal close operations. The IO
|
|
64
|
+
# object is closed only when `close: true` is passed, and flushed on normal
|
|
65
|
+
# completion only when `flush: true` is passed.
|
|
66
|
+
def self.io(io, close: false, flush: false)
|
|
67
|
+
raise TypeError, "io must respond to write" unless io.respond_to?(:write)
|
|
68
|
+
raise TypeError, "close must be true or false" unless [true, false].include?(close)
|
|
69
|
+
raise TypeError, "flush must be true or false" unless [true, false].include?(flush)
|
|
70
|
+
raise TypeError, "io must respond to close" if close && !io.respond_to?(:close)
|
|
71
|
+
raise TypeError, "io must respond to flush" if flush && !io.respond_to?(:flush)
|
|
72
|
+
|
|
73
|
+
new do |stream|
|
|
74
|
+
IOSink.new(io, close, flush).run(stream)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def initialize(&run)
|
|
79
|
+
@run = run
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private_class_method :new
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def run(stream)
|
|
87
|
+
@run.call(stream)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class IOSink
|
|
91
|
+
def initialize(io, close_io, flush_io)
|
|
92
|
+
@io = io
|
|
93
|
+
@close_io = close_io
|
|
94
|
+
@flush_io = flush_io
|
|
95
|
+
@chunks_written = 0
|
|
96
|
+
@io_closed = false
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def run(stream)
|
|
100
|
+
loop do
|
|
101
|
+
value = stream.next
|
|
102
|
+
break if Pull.done?(value)
|
|
103
|
+
|
|
104
|
+
write(value)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
finish
|
|
108
|
+
rescue StandardError
|
|
109
|
+
close_suppressing_error
|
|
110
|
+
raise
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def write(value)
|
|
116
|
+
unless value.is_a?(String)
|
|
117
|
+
raise TypeError, "Sink.io elements must be String"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
validate_scheduler!
|
|
121
|
+
@io.write(value)
|
|
122
|
+
@chunks_written += 1
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def finish
|
|
126
|
+
flush
|
|
127
|
+
close_on_normal_completion
|
|
128
|
+
@chunks_written
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def flush
|
|
132
|
+
return unless @flush_io
|
|
133
|
+
|
|
134
|
+
validate_scheduler!
|
|
135
|
+
@io.flush
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def close_on_normal_completion
|
|
139
|
+
return unless @close_io
|
|
140
|
+
|
|
141
|
+
validate_scheduler!
|
|
142
|
+
close_error = close_io
|
|
143
|
+
raise close_error if close_error
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def validate_scheduler!
|
|
147
|
+
return if Fiber.scheduler && !Fiber.current.blocking?
|
|
148
|
+
|
|
149
|
+
message =
|
|
150
|
+
if Fiber.scheduler
|
|
151
|
+
"Sink.io requires a non-blocking fiber"
|
|
152
|
+
else
|
|
153
|
+
"Sink.io requires Fiber.scheduler"
|
|
154
|
+
end
|
|
155
|
+
raise SchedulerRequiredError, message
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def close_suppressing_error
|
|
159
|
+
close_io
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def close_io
|
|
163
|
+
return nil unless @close_io
|
|
164
|
+
return nil if @io_closed
|
|
165
|
+
|
|
166
|
+
@io_closed = true
|
|
167
|
+
@io.close
|
|
168
|
+
nil
|
|
169
|
+
rescue StandardError => error
|
|
170
|
+
error
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private_constant :IOSink
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FiberStream
|
|
4
|
+
class Source
|
|
5
|
+
# Creates a source definition from an Enumerable.
|
|
6
|
+
#
|
|
7
|
+
# The enumerable is not consumed until values are pulled by `run_with`. Each
|
|
8
|
+
# materialization creates an Enumerator with `enumerable.to_enum(:each)`;
|
|
9
|
+
# FiberStream does not snapshot values or guarantee replayability for
|
|
10
|
+
# one-shot enumerables.
|
|
11
|
+
def self.each(enumerable)
|
|
12
|
+
new(-> { Pull.each(enumerable) })
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Creates a source definition from an IO-like object.
|
|
16
|
+
#
|
|
17
|
+
# The IO object is not read until values are pulled by `run_with`. Each
|
|
18
|
+
# materialization reads from the same IO object's current position; this
|
|
19
|
+
# source does not snapshot, reopen, or guarantee replayability. The IO is
|
|
20
|
+
# closed only when `close: true` is passed.
|
|
21
|
+
def self.io(io, chunk_size: 16 * 1024, close: false)
|
|
22
|
+
raise TypeError, "io must respond to readpartial" unless io.respond_to?(:readpartial)
|
|
23
|
+
raise TypeError, "chunk_size must be an Integer" unless chunk_size.is_a?(Integer)
|
|
24
|
+
raise ArgumentError, "chunk_size must be positive" unless chunk_size.positive?
|
|
25
|
+
raise TypeError, "close must be true or false" unless [true, false].include?(close)
|
|
26
|
+
raise TypeError, "io must respond to close" if close && !io.respond_to?(:close)
|
|
27
|
+
|
|
28
|
+
new(-> { Pull.io(io, chunk_size, close) })
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Creates a backpressure-aware source definition from Ractor ports.
|
|
32
|
+
#
|
|
33
|
+
# `port` is the data/control port received by FiberStream. `ack_port` is a
|
|
34
|
+
# producer-owned port that receives `RactorPort::Ack` and
|
|
35
|
+
# `RactorPort::Cancel` control messages. The producer must wait for an ack
|
|
36
|
+
# before sending each `RactorPort::Element`, `RactorPort::Complete`, or
|
|
37
|
+
# `RactorPort::Failure` message.
|
|
38
|
+
def self.ractor_port(port, ack_port:, ack_transfer: :copy, cancel: true)
|
|
39
|
+
raise TypeError, "port must respond to receive" unless port.respond_to?(:receive)
|
|
40
|
+
unless ack_port.respond_to?(:send) && ack_port.method(:send).owner != Kernel
|
|
41
|
+
raise TypeError, "ack_port must provide Ractor-style send"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
Flow.__send__(:validate_ractor_transfer_policy!, :ack_transfer, ack_transfer)
|
|
45
|
+
raise TypeError, "cancel must be true or false" unless [true, false].include?(cancel)
|
|
46
|
+
|
|
47
|
+
new(-> { Pull.ractor_port(port, ack_port, ack_transfer, cancel) })
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def initialize(source_factory, flows = [])
|
|
51
|
+
@source_factory = source_factory
|
|
52
|
+
@flows = flows
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns a new source definition that passes this source through `flow`.
|
|
56
|
+
#
|
|
57
|
+
# This method is lazy. It does not run the source, enumerate values, or call
|
|
58
|
+
# flow blocks.
|
|
59
|
+
def via(flow)
|
|
60
|
+
raise TypeError, "expected FiberStream::Flow" unless flow.is_a?(Flow)
|
|
61
|
+
|
|
62
|
+
self.class.__send__(:new, @source_factory, @flows + [flow])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns a new source definition that maps each element with `block`.
|
|
66
|
+
#
|
|
67
|
+
# This is a convenience wrapper around `via(FiberStream::Flow.map { ... })`
|
|
68
|
+
# and has the same lazy construction, error, and backpressure behavior as
|
|
69
|
+
# the underlying flow.
|
|
70
|
+
def map(&block)
|
|
71
|
+
via(Flow.map(&block))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns a new source definition that maps elements concurrently.
|
|
75
|
+
#
|
|
76
|
+
# This is a convenience wrapper around
|
|
77
|
+
# `via(FiberStream::Flow.parallel_map(concurrency:) { ... })` and preserves
|
|
78
|
+
# the same ordered delivery, scheduler requirement, validation, bounded
|
|
79
|
+
# upstream run-ahead, and cancellation behavior.
|
|
80
|
+
def parallel_map(concurrency:, &block)
|
|
81
|
+
via(Flow.parallel_map(concurrency: concurrency, &block))
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns a new source definition that maps elements in Ractor workers.
|
|
85
|
+
#
|
|
86
|
+
# This is a convenience wrapper around
|
|
87
|
+
# `via(FiberStream::Flow.ractor_map(workers:) { ... })` and preserves the
|
|
88
|
+
# same shareable mapper requirement, ordered delivery, transfer policy,
|
|
89
|
+
# bounded upstream run-ahead, and cooperative worker shutdown behavior.
|
|
90
|
+
def ractor_map(workers:, input_transfer: :copy, output_transfer: :copy, &block)
|
|
91
|
+
via(
|
|
92
|
+
Flow.ractor_map(
|
|
93
|
+
workers: workers,
|
|
94
|
+
input_transfer: input_transfer,
|
|
95
|
+
output_transfer: output_transfer,
|
|
96
|
+
&block
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Returns a new source definition that keeps elements matching `block`.
|
|
102
|
+
#
|
|
103
|
+
# This is a convenience wrapper around
|
|
104
|
+
# `via(FiberStream::Flow.select { ... })` and has the same truthiness and
|
|
105
|
+
# lazy construction behavior as the underlying flow.
|
|
106
|
+
def select(&block)
|
|
107
|
+
via(Flow.select(&block))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Returns a new source definition that emits at most `count` elements.
|
|
111
|
+
#
|
|
112
|
+
# This is a convenience wrapper around `via(FiberStream::Flow.take(count))`
|
|
113
|
+
# and preserves the same validation and upstream close behavior.
|
|
114
|
+
def take(count)
|
|
115
|
+
via(Flow.take(count))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Returns a new source definition with an asynchronous boundary.
|
|
119
|
+
#
|
|
120
|
+
# This is a convenience wrapper around `via(FiberStream::Flow.async)` and
|
|
121
|
+
# preserves the same scheduler requirement and cancellation behavior.
|
|
122
|
+
def async
|
|
123
|
+
via(Flow.async)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Returns a new source definition with a bounded asynchronous buffer.
|
|
127
|
+
#
|
|
128
|
+
# This is a convenience wrapper around
|
|
129
|
+
# `via(FiberStream::Flow.buffer(count))` and preserves the same validation,
|
|
130
|
+
# scheduler requirement, and cancellation behavior.
|
|
131
|
+
def buffer(count)
|
|
132
|
+
via(Flow.buffer(count))
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Returns a new source definition that splits String chunks into lines.
|
|
136
|
+
#
|
|
137
|
+
# This is a convenience wrapper around
|
|
138
|
+
# `via(FiberStream::Flow.lines(chomp:, max_length:))`.
|
|
139
|
+
def lines(chomp: true, max_length: nil)
|
|
140
|
+
via(Flow.lines(chomp: chomp, max_length: max_length))
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns a runnable pipeline from this source to `sink`.
|
|
144
|
+
#
|
|
145
|
+
# Construction is lazy. The source and sink are not materialized until
|
|
146
|
+
# `Pipeline#run` is called.
|
|
147
|
+
def to(sink)
|
|
148
|
+
raise TypeError, "expected FiberStream::Sink" unless sink.is_a?(Sink)
|
|
149
|
+
|
|
150
|
+
Pipeline.__send__(:new, self, sink)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Materializes and runs this source with `sink`.
|
|
154
|
+
#
|
|
155
|
+
# The stream runs in the current fiber until completion or failure. The
|
|
156
|
+
# method returns the sink's materialized value and closes the materialized
|
|
157
|
+
# pull chain on success, failure, or early sink completion.
|
|
158
|
+
def run_with(sink)
|
|
159
|
+
raise TypeError, "expected FiberStream::Sink" unless sink.is_a?(Sink)
|
|
160
|
+
|
|
161
|
+
primary_error = nil
|
|
162
|
+
|
|
163
|
+
begin
|
|
164
|
+
stream = @source_factory.call
|
|
165
|
+
@flows.each do |flow|
|
|
166
|
+
stream = flow.__send__(:attach, stream)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
sink.__send__(:run, stream)
|
|
170
|
+
rescue StandardError => error
|
|
171
|
+
primary_error = error
|
|
172
|
+
raise
|
|
173
|
+
ensure
|
|
174
|
+
begin
|
|
175
|
+
stream&.close
|
|
176
|
+
rescue StandardError => close_error
|
|
177
|
+
raise close_error unless primary_error
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private_class_method :new
|
|
183
|
+
end
|
|
184
|
+
end
|
data/lib/fiber_stream.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "fiber_stream/pull"
|
|
4
|
+
require_relative "fiber_stream/version"
|
|
5
|
+
require_relative "fiber_stream/errors"
|
|
6
|
+
require_relative "fiber_stream/ractor_port"
|
|
7
|
+
require_relative "fiber_stream/flow"
|
|
8
|
+
require_relative "fiber_stream/sink"
|
|
9
|
+
require_relative "fiber_stream/running_pipeline"
|
|
10
|
+
require_relative "fiber_stream/pipeline"
|
|
11
|
+
require_relative "fiber_stream/source"
|
|
12
|
+
|
|
13
|
+
module FiberStream
|
|
14
|
+
private_constant :Pull
|
|
15
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module FiberStream
|
|
2
|
+
type ractor_transfer_policy = :copy | :move
|
|
3
|
+
type ractor_map_error_kind = :input_transfer | :output_transfer | :worker | :worker_termination | :isolation
|
|
4
|
+
type ractor_port_source_error_kind = :invalid_message | :producer_failure | :receive | :ack_transfer | :cancel_transfer
|
|
5
|
+
|
|
6
|
+
class SchedulerRequiredError < RuntimeError
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class FrameTooLongError < RuntimeError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class PipelineCancelledError < RuntimeError
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class RactorPortSourceError < RuntimeError
|
|
16
|
+
attr_reader kind: ractor_port_source_error_kind
|
|
17
|
+
attr_reader cause_class_name: String
|
|
18
|
+
attr_reader cause_message: String
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class RactorMapError < RuntimeError
|
|
22
|
+
attr_reader sequence: Integer
|
|
23
|
+
attr_reader kind: ractor_map_error_kind
|
|
24
|
+
attr_reader cause_class_name: String
|
|
25
|
+
attr_reader cause_message: String
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
module RactorPort
|
|
29
|
+
class Element[Elem] < Data
|
|
30
|
+
attr_reader value: Elem
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Complete < Data
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Failure < Data
|
|
37
|
+
attr_reader cause_class_name: String
|
|
38
|
+
attr_reader cause_message: String
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class Ack < Data
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Cancel < Data
|
|
45
|
+
attr_reader reason: Symbol
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class Source[Elem]
|
|
50
|
+
def self.each: [Elem] (Enumerable[Elem] enumerable) -> Source[Elem]
|
|
51
|
+
def self.io: (untyped io, ?chunk_size: Integer, ?close: bool) -> Source[String]
|
|
52
|
+
def self.ractor_port: [Elem] (untyped port, ack_port: untyped, ?ack_transfer: ractor_transfer_policy, ?cancel: bool) -> Source[Elem]
|
|
53
|
+
def via: [Out] (Flow[Elem, Out] flow) -> Source[Out]
|
|
54
|
+
def map: [Out] () { (Elem) -> Out } -> Source[Out]
|
|
55
|
+
def parallel_map: [Out] (concurrency: Integer) { (Elem) -> Out } -> Source[Out]
|
|
56
|
+
def ractor_map: [Out] (workers: Integer, ?input_transfer: ractor_transfer_policy, ?output_transfer: ractor_transfer_policy) { (Elem) -> Out } -> Source[Out]
|
|
57
|
+
def select: () { (Elem) -> boolish } -> Source[Elem]
|
|
58
|
+
def take: (Integer count) -> Source[Elem]
|
|
59
|
+
def async: () -> Source[Elem]
|
|
60
|
+
def buffer: (Integer count) -> Source[Elem]
|
|
61
|
+
def lines: (?chomp: bool, ?max_length: Integer?) -> Source[String]
|
|
62
|
+
def to: [Mat] (Sink[Elem, Mat] sink) -> Pipeline[Mat]
|
|
63
|
+
def run_with: [Mat] (Sink[Elem, Mat] sink) -> Mat
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class Flow[In, Out]
|
|
67
|
+
def self.map: [In, Out] () { (In) -> Out } -> Flow[In, Out]
|
|
68
|
+
def self.parallel_map: [In, Out] (concurrency: Integer) { (In) -> Out } -> Flow[In, Out]
|
|
69
|
+
def self.ractor_map: [In, Out] (workers: Integer, ?input_transfer: ractor_transfer_policy, ?output_transfer: ractor_transfer_policy) { (In) -> Out } -> Flow[In, Out]
|
|
70
|
+
def self.select: [Elem] () { (Elem) -> boolish } -> Flow[Elem, Elem]
|
|
71
|
+
def self.take: [Elem] (Integer count) -> Flow[Elem, Elem]
|
|
72
|
+
def self.async: [Elem] () -> Flow[Elem, Elem]
|
|
73
|
+
def self.buffer: [Elem] (Integer count) -> Flow[Elem, Elem]
|
|
74
|
+
def self.lines: (?chomp: bool, ?max_length: Integer?) -> Flow[String, String]
|
|
75
|
+
def via: [Next] (Flow[Out, Next] flow) -> Flow[In, Next]
|
|
76
|
+
def to: [Mat] (Sink[Out, Mat] sink) -> Sink[In, Mat]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class Sink[In, Mat]
|
|
80
|
+
def self.to_a: [Elem] () -> Sink[Elem, Array[Elem]]
|
|
81
|
+
def self.first: [Elem] () -> Sink[Elem, Elem?]
|
|
82
|
+
def self.fold: [Elem, Acc] (Acc initial) { (Acc, Elem) -> Acc } -> Sink[Elem, Acc]
|
|
83
|
+
def self.io: (untyped io, ?close: bool, ?flush: bool) -> Sink[String, Integer]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class Pipeline[Mat]
|
|
87
|
+
def run: () -> Mat
|
|
88
|
+
def run_async: () -> RunningPipeline[Mat]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class RunningPipeline[Mat]
|
|
92
|
+
def wait: () -> Mat
|
|
93
|
+
def cancel: () -> self
|
|
94
|
+
def done?: () -> bool
|
|
95
|
+
def cancel_requested?: () -> bool
|
|
96
|
+
end
|
|
97
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fiber_stream
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Dai Akatsuka
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: async
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: minitest
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '5.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rake
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '13.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '13.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rbs
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '3.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rubocop
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '1.0'
|
|
75
|
+
- - "<"
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '2.0'
|
|
78
|
+
type: :development
|
|
79
|
+
prerelease: false
|
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '1.0'
|
|
85
|
+
- - "<"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '2.0'
|
|
88
|
+
description: A Ruby stream processing library built around Fiber and Fiber.scheduler.
|
|
89
|
+
executables: []
|
|
90
|
+
extensions: []
|
|
91
|
+
extra_rdoc_files: []
|
|
92
|
+
files:
|
|
93
|
+
- CHANGELOG.md
|
|
94
|
+
- LICENSE
|
|
95
|
+
- README.md
|
|
96
|
+
- examples/README.md
|
|
97
|
+
- examples/async_http_requests.rb
|
|
98
|
+
- examples/background_execution.rb
|
|
99
|
+
- examples/backpressure_buffer.rb
|
|
100
|
+
- examples/basic_pipeline.rb
|
|
101
|
+
- examples/composable_pipeline.rb
|
|
102
|
+
- examples/file_copy.rb
|
|
103
|
+
- examples/line_processing.rb
|
|
104
|
+
- examples/ractor_map_hashing.rb
|
|
105
|
+
- examples/ractor_port_source.rb
|
|
106
|
+
- lib/fiber_stream.rb
|
|
107
|
+
- lib/fiber_stream/errors.rb
|
|
108
|
+
- lib/fiber_stream/flow.rb
|
|
109
|
+
- lib/fiber_stream/pipeline.rb
|
|
110
|
+
- lib/fiber_stream/pull.rb
|
|
111
|
+
- lib/fiber_stream/pull/async_boundary.rb
|
|
112
|
+
- lib/fiber_stream/pull/buffer_boundary.rb
|
|
113
|
+
- lib/fiber_stream/pull/each.rb
|
|
114
|
+
- lib/fiber_stream/pull/io_source.rb
|
|
115
|
+
- lib/fiber_stream/pull/lines.rb
|
|
116
|
+
- lib/fiber_stream/pull/map.rb
|
|
117
|
+
- lib/fiber_stream/pull/parallel_map_boundary.rb
|
|
118
|
+
- lib/fiber_stream/pull/ractor_map_boundary.rb
|
|
119
|
+
- lib/fiber_stream/pull/ractor_port_source.rb
|
|
120
|
+
- lib/fiber_stream/pull/select.rb
|
|
121
|
+
- lib/fiber_stream/pull/take.rb
|
|
122
|
+
- lib/fiber_stream/ractor_port.rb
|
|
123
|
+
- lib/fiber_stream/running_pipeline.rb
|
|
124
|
+
- lib/fiber_stream/sink.rb
|
|
125
|
+
- lib/fiber_stream/source.rb
|
|
126
|
+
- lib/fiber_stream/version.rb
|
|
127
|
+
- sig/fiber_stream.rbs
|
|
128
|
+
homepage: https://github.com/dakatsuka/fiber_stream
|
|
129
|
+
licenses:
|
|
130
|
+
- MIT
|
|
131
|
+
metadata:
|
|
132
|
+
allowed_push_host: https://rubygems.org
|
|
133
|
+
homepage_uri: https://github.com/dakatsuka/fiber_stream
|
|
134
|
+
source_code_uri: https://github.com/dakatsuka/fiber_stream/tree/v0.1.0
|
|
135
|
+
changelog_uri: https://github.com/dakatsuka/fiber_stream/blob/main/CHANGELOG.md
|
|
136
|
+
rubygems_mfa_required: 'true'
|
|
137
|
+
rdoc_options: []
|
|
138
|
+
require_paths:
|
|
139
|
+
- lib
|
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '4.0'
|
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
|
+
requirements:
|
|
147
|
+
- - ">="
|
|
148
|
+
- !ruby/object:Gem::Version
|
|
149
|
+
version: '0'
|
|
150
|
+
requirements: []
|
|
151
|
+
rubygems_version: 4.0.6
|
|
152
|
+
specification_version: 4
|
|
153
|
+
summary: Asynchronous, non-blocking stream processing with backpressure.
|
|
154
|
+
test_files: []
|