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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b644b242893003aa43e9f2c9ec382abc77ed6f08aef0acf13612fd8c01af6109
4
- data.tar.gz: 9ceb2436e366cb6cc823fd1e1600a07d99f7ea3aa07ca299647050b410eab9e7
3
+ metadata.gz: e46f28055cb41f75b5a00b863d1771580ec7e610dd09d5360574e123c3b99867
4
+ data.tar.gz: b9ff8b9bf2724da050eb49933eac10c9d35953c30abd823fcc0482823a27a3a8
5
5
  SHA512:
6
- metadata.gz: 800ce4c7128151cdf980f61fb50e2aa7c4811f0bad753535ee438fc2925b3d84018a176f633a18d27783af158dc8667b18623261ab8cccc460364b50f5958cae
7
- data.tar.gz: 7cf60a1881e77bd39f2bf0dcff6cb9252c72130a24f1daaf2ede3fa62b9c90239a5d2d6a01f9d838b8f4ed79114e8811619c4f8955969a200b1e0e52152e4013
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
@@ -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.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
- @io = with_io
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 sep_string [String] The line separator (defaults to $/)
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.each_line do |line|
63
+ # progress_io.each do |line|
62
64
  # puts "Processing: #{line.chomp}"
63
65
  # end
64
- def each(sep_string = $/, &blk)
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
- @io.each(sep_string) do | line |
75
+ super(*args) do |line|
67
76
  yield(line).tap { notify_read }
68
77
  end
69
78
  end
70
- alias_method :each_line, :each
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
- @io.each_byte { |b| yield(b).tap { notify_read } }
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
- inner(:getc)
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
- inner(:gets, *args)
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
- inner(:read, *a)
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
- inner(:readbytes, *a)
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
- inner(:readchar)
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
- inner(:readline, *a)
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
- inner(:readlines, *a)
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
- inner(:seek, *a)
194
+ super(*a)
157
195
  end
158
196
 
159
197
  # def ungetc(*a)
160
- # inner(:ungetc, a)
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
- inner(:pos=, p)
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(@io.pos) if @progress_block
217
+ @progress_block.call(pos) if @progress_block
195
218
  end
196
219
  end
@@ -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.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: 2025-08-01 00:00:00.000000000 Z
10
+ date: 2026-01-13 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake