progressive_io 2.0.0 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/progressive_io.rb +58 -35
- data/test/test_progressive_io.rb +91 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e46f28055cb41f75b5a00b863d1771580ec7e610dd09d5360574e123c3b99867
|
|
4
|
+
data.tar.gz: b9ff8b9bf2724da050eb49933eac10c9d35953c30abd823fcc0482823a27a3a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e9c3bdfaf294545502b5dcde2381f67c4d343df02c64d9b56d290877f13619d283288a64517af7e5c5aa7f7359db6a0606684f693c0b8cdec9c32abfd2d27a43
|
|
7
|
+
data.tar.gz: 14fbce5195814ef1c583c664f8550b664e60d2d46a1465ef7ced873bd980247897fea75652f36444c4ff5511c67ba8be64dcd25644a310f907cd80d9f68ba236
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 2.0.2
|
|
2
|
+
|
|
3
|
+
* Fix `each_line` to return an Enumerator that properly references `:each_line` method
|
|
4
|
+
when called without a block. Previously, `each_line` was an alias of `each`, causing
|
|
5
|
+
the returned Enumerator to reference `:each` instead of `:each_line`.
|
|
6
|
+
* Change `each` and `each_line` to accept variadic arguments (`*args`) for proper
|
|
7
|
+
forwarding of separator and limit parameters.
|
|
8
|
+
|
|
9
|
+
## 2.0.1
|
|
10
|
+
|
|
11
|
+
* Revert back to using SimpleDelegator - it's just easier.
|
|
12
|
+
|
|
1
13
|
## 2.0.0
|
|
2
14
|
|
|
3
15
|
* Added comprehensive documentation with examples
|
data/lib/progressive_io.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'delegate'
|
|
4
|
+
|
|
3
5
|
# A wrapper class that provides progress tracking for IO operations.
|
|
4
6
|
#
|
|
5
7
|
# This class wraps an IO object and calls a progress block whenever data is read,
|
|
@@ -24,9 +26,9 @@
|
|
|
24
26
|
# content = progress_io.read
|
|
25
27
|
#
|
|
26
28
|
# @since 2.0.0
|
|
27
|
-
class ProgressiveIO
|
|
29
|
+
class ProgressiveIO < SimpleDelegator
|
|
28
30
|
# The version of the ProgressiveIO library
|
|
29
|
-
VERSION = '2.0.
|
|
31
|
+
VERSION = '2.0.2'
|
|
30
32
|
|
|
31
33
|
# @return [Proc, nil] The progress callback block that will be called when data is read
|
|
32
34
|
# The block receives one parameter: current position
|
|
@@ -46,28 +48,57 @@ class ProgressiveIO
|
|
|
46
48
|
# puts "Read #{pos} bytes"
|
|
47
49
|
# end
|
|
48
50
|
def initialize(with_io, &blk)
|
|
49
|
-
|
|
51
|
+
super(with_io)
|
|
50
52
|
@progress_block = blk.to_proc if blk
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
# Iterates over the IO stream line by line, calling the progress block for each line.
|
|
54
56
|
#
|
|
55
|
-
# @param
|
|
57
|
+
# @param args [Array] Arguments to pass to the underlying IO#each method (separator and/or limit)
|
|
56
58
|
# @yield [line] Each line from the IO stream
|
|
57
59
|
# @yieldparam line [String] A line from the IO stream
|
|
58
60
|
# @return [Enumerator] An enumerator if no block is given
|
|
59
61
|
#
|
|
60
62
|
# @example
|
|
61
|
-
# progress_io.
|
|
63
|
+
# progress_io.each do |line|
|
|
62
64
|
# puts "Processing: #{line.chomp}"
|
|
63
65
|
# end
|
|
64
|
-
|
|
66
|
+
#
|
|
67
|
+
# @example Using as an Enumerator
|
|
68
|
+
# progress_io.each.with_index do |line, idx|
|
|
69
|
+
# puts "Line #{idx}: #{line.chomp}"
|
|
70
|
+
# end
|
|
71
|
+
def each(*args, &blk)
|
|
72
|
+
return enum_for(__method__, *args) unless block_given?
|
|
73
|
+
|
|
65
74
|
# Report offset at each call of the iterator
|
|
66
|
-
|
|
75
|
+
super(*args) do |line|
|
|
67
76
|
yield(line).tap { notify_read }
|
|
68
77
|
end
|
|
69
78
|
end
|
|
70
|
-
|
|
79
|
+
|
|
80
|
+
# Iterates over the IO stream line by line, calling the progress block for each line.
|
|
81
|
+
# This is an alias-like method for {#each} that ensures proper Enumerator behavior.
|
|
82
|
+
#
|
|
83
|
+
# @param args [Array] Arguments to pass to the underlying IO#each_line method (separator and/or limit)
|
|
84
|
+
# @yield [line] Each line from the IO stream
|
|
85
|
+
# @yieldparam line [String] A line from the IO stream
|
|
86
|
+
# @return [Enumerator] An enumerator if no block is given
|
|
87
|
+
#
|
|
88
|
+
# @example
|
|
89
|
+
# progress_io.each_line do |line|
|
|
90
|
+
# puts "Processing: #{line.chomp}"
|
|
91
|
+
# end
|
|
92
|
+
#
|
|
93
|
+
# @example Using as an Enumerator
|
|
94
|
+
# progress_io.each_line.with_index do |line, idx|
|
|
95
|
+
# puts "Line #{idx}: #{line.chomp}"
|
|
96
|
+
# end
|
|
97
|
+
def each_line(*args, &blk)
|
|
98
|
+
return enum_for(__method__, *args) unless block_given?
|
|
99
|
+
|
|
100
|
+
each(*args, &blk)
|
|
101
|
+
end
|
|
71
102
|
|
|
72
103
|
# Iterates over the IO stream byte by byte, calling the progress block for each byte.
|
|
73
104
|
#
|
|
@@ -79,9 +110,16 @@ class ProgressiveIO
|
|
|
79
110
|
# progress_io.each_byte do |byte|
|
|
80
111
|
# puts "Byte: #{byte}"
|
|
81
112
|
# end
|
|
113
|
+
#
|
|
114
|
+
# @example Using as an Enumerator
|
|
115
|
+
# progress_io.each_byte.with_index do |byte, idx|
|
|
116
|
+
# puts "Byte #{idx}: #{byte}"
|
|
117
|
+
# end
|
|
82
118
|
def each_byte(&blk)
|
|
119
|
+
return enum_for(__method__) unless block_given?
|
|
120
|
+
|
|
83
121
|
# Report offset at each call of the iterator
|
|
84
|
-
|
|
122
|
+
super { |b| yield(b).tap { notify_read } }
|
|
85
123
|
end
|
|
86
124
|
|
|
87
125
|
# Reads a single character from the IO stream.
|
|
@@ -89,7 +127,7 @@ class ProgressiveIO
|
|
|
89
127
|
# @return [String, nil] The next character or nil if at end of stream
|
|
90
128
|
# @see IO#getc
|
|
91
129
|
def getc
|
|
92
|
-
|
|
130
|
+
super.tap { notify_read }
|
|
93
131
|
end
|
|
94
132
|
|
|
95
133
|
# Reads a line from the IO stream.
|
|
@@ -98,7 +136,7 @@ class ProgressiveIO
|
|
|
98
136
|
# @return [String, nil] The next line or nil if at end of stream
|
|
99
137
|
# @see IO#gets
|
|
100
138
|
def gets(*args)
|
|
101
|
-
|
|
139
|
+
super(*args).tap { notify_read }
|
|
102
140
|
end
|
|
103
141
|
|
|
104
142
|
# Reads data from the IO stream.
|
|
@@ -107,7 +145,7 @@ class ProgressiveIO
|
|
|
107
145
|
# @return [String, nil] The read data or nil if at end of stream
|
|
108
146
|
# @see IO#read
|
|
109
147
|
def read(*a)
|
|
110
|
-
|
|
148
|
+
super(*a).tap { notify_read }
|
|
111
149
|
end
|
|
112
150
|
|
|
113
151
|
# Reads a specific number of bytes from the IO stream.
|
|
@@ -116,7 +154,7 @@ class ProgressiveIO
|
|
|
116
154
|
# @return [String] The read bytes
|
|
117
155
|
# @see IO#readbytes
|
|
118
156
|
def readbytes(*a)
|
|
119
|
-
|
|
157
|
+
super(*a).tap { notify_read }
|
|
120
158
|
end
|
|
121
159
|
|
|
122
160
|
# Reads a single character from the IO stream.
|
|
@@ -125,7 +163,7 @@ class ProgressiveIO
|
|
|
125
163
|
# @raise [EOFError] If at end of stream
|
|
126
164
|
# @see IO#readchar
|
|
127
165
|
def readchar
|
|
128
|
-
|
|
166
|
+
super.tap { notify_read }
|
|
129
167
|
end
|
|
130
168
|
|
|
131
169
|
# Reads a line from the IO stream.
|
|
@@ -135,7 +173,7 @@ class ProgressiveIO
|
|
|
135
173
|
# @raise [EOFError] If at end of stream
|
|
136
174
|
# @see IO#readline
|
|
137
175
|
def readline(*a)
|
|
138
|
-
|
|
176
|
+
super(*a).tap { notify_read }
|
|
139
177
|
end
|
|
140
178
|
|
|
141
179
|
# Reads all lines from the IO stream.
|
|
@@ -144,7 +182,7 @@ class ProgressiveIO
|
|
|
144
182
|
# @return [Array<String>] Array of lines
|
|
145
183
|
# @see IO#readlines
|
|
146
184
|
def readlines(*a)
|
|
147
|
-
|
|
185
|
+
super(*a).tap { notify_read }
|
|
148
186
|
end
|
|
149
187
|
|
|
150
188
|
# Seeks to a position in the IO stream.
|
|
@@ -153,11 +191,11 @@ class ProgressiveIO
|
|
|
153
191
|
# @return [Integer] The new position
|
|
154
192
|
# @see IO#seek
|
|
155
193
|
def seek(*a)
|
|
156
|
-
|
|
194
|
+
super(*a)
|
|
157
195
|
end
|
|
158
196
|
|
|
159
197
|
# def ungetc(*a)
|
|
160
|
-
#
|
|
198
|
+
# super(*a).tap { notify_read }
|
|
161
199
|
# end
|
|
162
200
|
|
|
163
201
|
# Sets the position in the IO stream.
|
|
@@ -166,31 +204,16 @@ class ProgressiveIO
|
|
|
166
204
|
# @return [Integer] The new position
|
|
167
205
|
# @see IO#pos=
|
|
168
206
|
def pos=(p)
|
|
169
|
-
|
|
207
|
+
super(p).tap { notify_read }
|
|
170
208
|
end
|
|
171
209
|
|
|
172
210
|
private
|
|
173
211
|
|
|
174
|
-
# @return [IO] The wrapped IO object
|
|
175
|
-
def io
|
|
176
|
-
@io
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
# Delegates method calls to the wrapped IO object and calls the progress block.
|
|
180
|
-
#
|
|
181
|
-
# @param m [Symbol] The method name to call
|
|
182
|
-
# @param args [Array] Arguments to pass to the method
|
|
183
|
-
# @return [Object] The result of the method call
|
|
184
|
-
def inner(m, *args)
|
|
185
|
-
r = @io.respond_to?(:public_send) ? @io.public_send(m, *args) : @io.send(m, *args)
|
|
186
|
-
r.tap { notify_read }
|
|
187
|
-
end
|
|
188
|
-
|
|
189
212
|
# Calls the progress block with current position.
|
|
190
213
|
# This method is called whenever data is read from the IO stream.
|
|
191
214
|
#
|
|
192
215
|
# @return [void]
|
|
193
216
|
def notify_read
|
|
194
|
-
@progress_block.call(
|
|
217
|
+
@progress_block.call(pos) if @progress_block
|
|
195
218
|
end
|
|
196
219
|
end
|
data/test/test_progressive_io.rb
CHANGED
|
@@ -29,6 +29,73 @@ class TestProgressiveIO < Minitest::Test
|
|
|
29
29
|
assert_equal [[5], [9], [18], [22]], messages
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def test_each_returns_enumerator_without_block
|
|
33
|
+
io, messages = e("Mary\nHad\nA little\nLamb"), []
|
|
34
|
+
|
|
35
|
+
io.progress_block = lambda do | offset |
|
|
36
|
+
messages.push([offset])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
enum = io.each
|
|
40
|
+
assert_kind_of Enumerator, enum
|
|
41
|
+
|
|
42
|
+
# Enumerator should yield lines and still trigger progress callbacks
|
|
43
|
+
lines = enum.to_a
|
|
44
|
+
assert_equal ["Mary\n", "Had\n", "A little\n", "Lamb"], lines
|
|
45
|
+
assert_equal [[5], [9], [18], [22]], messages
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_each_line_returns_enumerator_without_block
|
|
49
|
+
io, messages = e("Mary\nHad\nA little\nLamb"), []
|
|
50
|
+
|
|
51
|
+
io.progress_block = lambda do | offset |
|
|
52
|
+
messages.push([offset])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
enum = io.each_line
|
|
56
|
+
assert_kind_of Enumerator, enum
|
|
57
|
+
|
|
58
|
+
# Enumerator should yield lines and still trigger progress callbacks
|
|
59
|
+
lines = enum.to_a
|
|
60
|
+
assert_equal ["Mary\n", "Had\n", "A little\n", "Lamb"], lines
|
|
61
|
+
assert_equal [[5], [9], [18], [22]], messages
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_each_enumerator_with_custom_separator
|
|
65
|
+
io, messages = e("Mary|Had|A little|Lamb"), []
|
|
66
|
+
|
|
67
|
+
io.progress_block = lambda do | offset |
|
|
68
|
+
messages.push([offset])
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
enum = io.each("|")
|
|
72
|
+
assert_kind_of Enumerator, enum
|
|
73
|
+
|
|
74
|
+
lines = enum.to_a
|
|
75
|
+
assert_equal ["Mary|", "Had|", "A little|", "Lamb"], lines
|
|
76
|
+
assert_equal [[5], [9], [18], [22]], messages
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_each_enumerator_chainable
|
|
80
|
+
io = e("Mary\nHad\nA little\nLamb")
|
|
81
|
+
|
|
82
|
+
# Should be chainable with Enumerator methods like with_index
|
|
83
|
+
result = io.each.with_index.map { |line, idx| [idx, line.chomp] }
|
|
84
|
+
assert_equal [[0, "Mary"], [1, "Had"], [2, "A little"], [3, "Lamb"]], result
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_each_line_enumerator_references_each_line_method
|
|
88
|
+
io = e("Mary\nHad\nLamb")
|
|
89
|
+
|
|
90
|
+
# The enumerator returned by each_line should reference :each_line, not :each
|
|
91
|
+
# This is important for proper Enumerator behavior and introspection
|
|
92
|
+
each_enum = io.each
|
|
93
|
+
each_line_enum = io.each_line
|
|
94
|
+
|
|
95
|
+
assert_match(/:each\b/, each_enum.inspect, "each enumerator should reference :each method")
|
|
96
|
+
assert_match(/:each_line\b/, each_line_enum.inspect, "each_line enumerator should reference :each_line method")
|
|
97
|
+
end
|
|
98
|
+
|
|
32
99
|
def test_each_byte
|
|
33
100
|
io, messages = e("123"), []
|
|
34
101
|
|
|
@@ -42,6 +109,30 @@ class TestProgressiveIO < Minitest::Test
|
|
|
42
109
|
assert_equal [[1], [2], [3]], messages
|
|
43
110
|
end
|
|
44
111
|
|
|
112
|
+
def test_each_byte_returns_enumerator_without_block
|
|
113
|
+
io, messages = e("123"), []
|
|
114
|
+
|
|
115
|
+
io.progress_block = lambda do | offset |
|
|
116
|
+
messages.push([offset])
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
enum = io.each_byte
|
|
120
|
+
assert_kind_of Enumerator, enum
|
|
121
|
+
|
|
122
|
+
# Enumerator should yield bytes and still trigger progress callbacks
|
|
123
|
+
bytes = enum.to_a
|
|
124
|
+
assert_equal [49, 50, 51], bytes
|
|
125
|
+
assert_equal [[1], [2], [3]], messages
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_each_byte_enumerator_chainable
|
|
129
|
+
io = e("123")
|
|
130
|
+
|
|
131
|
+
# Should be chainable with Enumerator methods like with_index
|
|
132
|
+
result = io.each_byte.with_index.map { |byte, idx| [idx, byte] }
|
|
133
|
+
assert_equal [[0, 49], [1, 50], [2, 51]], result
|
|
134
|
+
end
|
|
135
|
+
|
|
45
136
|
def test_getc
|
|
46
137
|
io = e("123")
|
|
47
138
|
io.progress_block = lambda do | offset |
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: progressive_io
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Julik Tarkhanov
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-01-13 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rake
|