csv 3.2.2 → 3.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/NEWS.md +49 -5
- data/doc/csv/options/generating/write_headers.rdoc +1 -1
- data/doc/csv/recipes/generating.rdoc +1 -1
- data/doc/csv/recipes/parsing.rdoc +1 -1
- data/lib/csv/input_record_separator.rb +1 -14
- data/lib/csv/parser.rb +161 -54
- data/lib/csv/table.rb +14 -4
- data/lib/csv/version.rb +1 -1
- data/lib/csv.rb +25 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 915b3ed5a51bf4836f08f7bb06efc3b07bdc90e09209a5253092130e2cad2ab6
|
4
|
+
data.tar.gz: 6bce2e39329afcf200691b4b2f422b6a48d45da66368f5d5e136e0c761cd6217
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c1434c8e91c16de40d19d4d1200f193248e786720b67f2bbecf26a481859fe814b8cbaa02d22027668ff02588541266c8ff5d00b9fc1cfc2163b358b8e9ece9
|
7
|
+
data.tar.gz: 1978e933549049129f0ec99e80a10f2838b3c75a282103aa177d8421fe7589d428308e2786b29a961a4a7a5565ede77e3b1ef44ba8f4bc91b593a5a884ded7aa
|
data/NEWS.md
CHANGED
@@ -1,5 +1,54 @@
|
|
1
1
|
# News
|
2
2
|
|
3
|
+
## 3.2.3 - 2022-04-09
|
4
|
+
|
5
|
+
### Improvements
|
6
|
+
|
7
|
+
* Added contents summary to `CSV::Table#inspect`.
|
8
|
+
[GitHub#229][Patch by Eriko Sugiyama]
|
9
|
+
[GitHub#235][Patch by Sampat Badhe]
|
10
|
+
|
11
|
+
* Suppressed `$INPUT_RECORD_SEPARATOR` deprecation warning by
|
12
|
+
`Warning.warn`.
|
13
|
+
[GitHub#233][Reported by Jean byroot Boussier]
|
14
|
+
|
15
|
+
* Improved error message for liberal parsing with quoted values.
|
16
|
+
[GitHub#231][Patch by Nikolay Rys]
|
17
|
+
|
18
|
+
* Fixed typos in documentation.
|
19
|
+
[GitHub#236][Patch by Sampat Badhe]
|
20
|
+
|
21
|
+
* Added `:max_field_size` option and deprecated `:field_size_limit` option.
|
22
|
+
[GitHub#238][Reported by Dan Buettner]
|
23
|
+
|
24
|
+
* Added `:symbol_raw` to built-in header converters.
|
25
|
+
[GitHub#237][Reported by taki]
|
26
|
+
[GitHub#239][Patch by Eriko Sugiyama]
|
27
|
+
|
28
|
+
### Fixes
|
29
|
+
|
30
|
+
* Fixed a bug that some texts may be dropped unexpectedly.
|
31
|
+
[Bug #18245][ruby-core:105587][Reported by Hassan Abdul Rehman]
|
32
|
+
|
33
|
+
* Fixed a bug that `:field_size_limit` doesn't work with not complex row.
|
34
|
+
[GitHub#238][Reported by Dan Buettner]
|
35
|
+
|
36
|
+
### Thanks
|
37
|
+
|
38
|
+
* Hassan Abdul Rehman
|
39
|
+
|
40
|
+
* Eriko Sugiyama
|
41
|
+
|
42
|
+
* Jean byroot Boussier
|
43
|
+
|
44
|
+
* Nikolay Rys
|
45
|
+
|
46
|
+
* Sampat Badhe
|
47
|
+
|
48
|
+
* Dan Buettner
|
49
|
+
|
50
|
+
* taki
|
51
|
+
|
3
52
|
## 3.2.2 - 2021-12-24
|
4
53
|
|
5
54
|
### Improvements
|
@@ -15,9 +64,6 @@
|
|
15
64
|
* Fixed a bug that all of `ARGF` contents may not be consumed.
|
16
65
|
[GitHub#228][Reported by Rafael Navaza]
|
17
66
|
|
18
|
-
* Fixed a bug that some texts may be dropped unexpectedly.
|
19
|
-
[Bug #18245][ruby-core:105587][Reported by Hassan Abdul Rehman]
|
20
|
-
|
21
67
|
### Thanks
|
22
68
|
|
23
69
|
* adamroyjones
|
@@ -26,8 +72,6 @@
|
|
26
72
|
|
27
73
|
* Rafael Navaza
|
28
74
|
|
29
|
-
* Hassan Abdul Rehman
|
30
|
-
|
31
75
|
## 3.2.1 - 2021-10-23
|
32
76
|
|
33
77
|
### Improvements
|
@@ -148,7 +148,7 @@ This example defines and uses a custom write converter to strip whitespace from
|
|
148
148
|
|
149
149
|
==== Recipe: Specify Multiple Write Converters
|
150
150
|
|
151
|
-
Use option <tt>:write_converters</tt> and multiple custom
|
151
|
+
Use option <tt>:write_converters</tt> and multiple custom converters
|
152
152
|
to convert field values when generating \CSV.
|
153
153
|
|
154
154
|
This example defines and uses two custom write converters to strip and upcase generated fields:
|
@@ -83,7 +83,7 @@ Use instance method CSV#each with option +headers+ to read a source \String one
|
|
83
83
|
CSV.new(string, headers: true).each do |row|
|
84
84
|
p row
|
85
85
|
end
|
86
|
-
|
86
|
+
Output:
|
87
87
|
#<CSV::Row "Name":"foo" "Value":"0">
|
88
88
|
#<CSV::Row "Name":"bar" "Value":"1">
|
89
89
|
#<CSV::Row "Name":"baz" "Value":"2">
|
@@ -4,20 +4,7 @@ require "stringio"
|
|
4
4
|
class CSV
|
5
5
|
module InputRecordSeparator
|
6
6
|
class << self
|
7
|
-
|
8
|
-
verbose, $VERBOSE = $VERBOSE, true
|
9
|
-
stderr, $stderr = $stderr, StringIO.new
|
10
|
-
input_record_separator = $INPUT_RECORD_SEPARATOR
|
11
|
-
begin
|
12
|
-
$INPUT_RECORD_SEPARATOR = "\r\n"
|
13
|
-
is_input_record_separator_deprecated = (not $stderr.string.empty?)
|
14
|
-
ensure
|
15
|
-
$INPUT_RECORD_SEPARATOR = input_record_separator
|
16
|
-
$stderr = stderr
|
17
|
-
$VERBOSE = verbose
|
18
|
-
end
|
19
|
-
|
20
|
-
if is_input_record_separator_deprecated
|
7
|
+
if RUBY_VERSION >= "3.0.0"
|
21
8
|
def value
|
22
9
|
"\n"
|
23
10
|
end
|
data/lib/csv/parser.rb
CHANGED
@@ -27,6 +27,10 @@ class CSV
|
|
27
27
|
class InvalidEncoding < StandardError
|
28
28
|
end
|
29
29
|
|
30
|
+
# Raised when unexpected case is happen.
|
31
|
+
class UnexpectedError < StandardError
|
32
|
+
end
|
33
|
+
|
30
34
|
#
|
31
35
|
# CSV::Scanner receives a CSV output, scans it and return the content.
|
32
36
|
# It also controls the life cycle of the object with its methods +keep_start+,
|
@@ -78,10 +82,10 @@ class CSV
|
|
78
82
|
# +keep_end+, +keep_back+, +keep_drop+.
|
79
83
|
#
|
80
84
|
# CSV::InputsScanner.scan() tries to match with pattern at the current position.
|
81
|
-
# If there's a match, the scanner advances the
|
85
|
+
# If there's a match, the scanner advances the "scan pointer" and returns the matched string.
|
82
86
|
# Otherwise, the scanner returns nil.
|
83
87
|
#
|
84
|
-
# CSV::InputsScanner.rest() returns the
|
88
|
+
# CSV::InputsScanner.rest() returns the "rest" of the string (i.e. everything after the scan pointer).
|
85
89
|
# If there is no more data (eos? = true), it returns "".
|
86
90
|
#
|
87
91
|
class InputsScanner
|
@@ -96,11 +100,13 @@ class CSV
|
|
96
100
|
end
|
97
101
|
|
98
102
|
def each_line(row_separator)
|
103
|
+
return enum_for(__method__, row_separator) unless block_given?
|
99
104
|
buffer = nil
|
100
105
|
input = @scanner.rest
|
101
106
|
position = @scanner.pos
|
102
107
|
offset = 0
|
103
108
|
n_row_separator_chars = row_separator.size
|
109
|
+
# trace(__method__, :start, line, input)
|
104
110
|
while true
|
105
111
|
input.each_line(row_separator) do |line|
|
106
112
|
@scanner.pos += line.bytesize
|
@@ -140,25 +146,28 @@ class CSV
|
|
140
146
|
end
|
141
147
|
|
142
148
|
def scan(pattern)
|
149
|
+
# trace(__method__, pattern, :start)
|
143
150
|
value = @scanner.scan(pattern)
|
151
|
+
# trace(__method__, pattern, :done, :last, value) if @last_scanner
|
144
152
|
return value if @last_scanner
|
145
153
|
|
146
|
-
if value
|
147
|
-
|
148
|
-
|
149
|
-
else
|
150
|
-
nil
|
151
|
-
end
|
154
|
+
read_chunk if value and @scanner.eos?
|
155
|
+
# trace(__method__, pattern, :done, value)
|
156
|
+
value
|
152
157
|
end
|
153
158
|
|
154
159
|
def scan_all(pattern)
|
160
|
+
# trace(__method__, pattern, :start)
|
155
161
|
value = @scanner.scan(pattern)
|
162
|
+
# trace(__method__, pattern, :done, :last, value) if @last_scanner
|
156
163
|
return value if @last_scanner
|
157
164
|
|
158
165
|
return nil if value.nil?
|
159
166
|
while @scanner.eos? and read_chunk and (sub_value = @scanner.scan(pattern))
|
167
|
+
# trace(__method__, pattern, :sub, sub_value)
|
160
168
|
value << sub_value
|
161
169
|
end
|
170
|
+
# trace(__method__, pattern, :done, value)
|
162
171
|
value
|
163
172
|
end
|
164
173
|
|
@@ -167,68 +176,126 @@ class CSV
|
|
167
176
|
end
|
168
177
|
|
169
178
|
def keep_start
|
170
|
-
|
179
|
+
# trace(__method__, :start)
|
180
|
+
adjust_last_keep
|
181
|
+
@keeps.push([@scanner, @scanner.pos, nil])
|
182
|
+
# trace(__method__, :done)
|
171
183
|
end
|
172
184
|
|
173
185
|
def keep_end
|
174
|
-
|
175
|
-
|
186
|
+
# trace(__method__, :start)
|
187
|
+
scanner, start, buffer = @keeps.pop
|
188
|
+
if scanner == @scanner
|
189
|
+
keep = @scanner.string.byteslice(start, @scanner.pos - start)
|
190
|
+
else
|
191
|
+
keep = @scanner.string.byteslice(0, @scanner.pos)
|
192
|
+
end
|
176
193
|
if buffer
|
177
194
|
buffer << keep
|
178
195
|
keep = buffer
|
179
196
|
end
|
197
|
+
# trace(__method__, :done, keep)
|
180
198
|
keep
|
181
199
|
end
|
182
200
|
|
183
201
|
def keep_back
|
184
|
-
|
202
|
+
# trace(__method__, :start)
|
203
|
+
scanner, start, buffer = @keeps.pop
|
185
204
|
if buffer
|
205
|
+
# trace(__method__, :rescan, start, buffer)
|
186
206
|
string = @scanner.string
|
187
|
-
|
207
|
+
if scanner == @scanner
|
208
|
+
keep = string.byteslice(start, string.bytesize - start)
|
209
|
+
else
|
210
|
+
keep = string
|
211
|
+
end
|
188
212
|
if keep and not keep.empty?
|
189
213
|
@inputs.unshift(StringIO.new(keep))
|
190
214
|
@last_scanner = false
|
191
215
|
end
|
192
216
|
@scanner = StringScanner.new(buffer)
|
193
217
|
else
|
218
|
+
if @scanner != scanner
|
219
|
+
message = "scanners are different but no buffer: "
|
220
|
+
message += "#{@scanner.inspect}(#{@scanner.object_id}): "
|
221
|
+
message += "#{scanner.inspect}(#{scanner.object_id})"
|
222
|
+
raise UnexpectedError, message
|
223
|
+
end
|
224
|
+
# trace(__method__, :repos, start, buffer)
|
194
225
|
@scanner.pos = start
|
195
226
|
end
|
196
227
|
read_chunk if @scanner.eos?
|
197
228
|
end
|
198
229
|
|
199
230
|
def keep_drop
|
200
|
-
@keeps.pop
|
231
|
+
_, _, buffer = @keeps.pop
|
232
|
+
# trace(__method__, :done, :empty) unless buffer
|
233
|
+
return unless buffer
|
234
|
+
|
235
|
+
last_keep = @keeps.last
|
236
|
+
# trace(__method__, :done, :no_last_keep) unless last_keep
|
237
|
+
return unless last_keep
|
238
|
+
|
239
|
+
if last_keep[2]
|
240
|
+
last_keep[2] << buffer
|
241
|
+
else
|
242
|
+
last_keep[2] = buffer
|
243
|
+
end
|
244
|
+
# trace(__method__, :done)
|
201
245
|
end
|
202
246
|
|
203
247
|
def rest
|
204
248
|
@scanner.rest
|
205
249
|
end
|
206
250
|
|
251
|
+
def check(pattern)
|
252
|
+
@scanner.check(pattern)
|
253
|
+
end
|
254
|
+
|
207
255
|
private
|
208
|
-
def
|
209
|
-
|
256
|
+
def trace(*args)
|
257
|
+
pp([*args, @scanner, @scanner&.string, @scanner&.pos, @keeps])
|
258
|
+
end
|
210
259
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
260
|
+
def adjust_last_keep
|
261
|
+
# trace(__method__, :start)
|
262
|
+
|
263
|
+
keep = @keeps.last
|
264
|
+
# trace(__method__, :done, :empty) if keep.nil?
|
265
|
+
return if keep.nil?
|
266
|
+
|
267
|
+
scanner, start, buffer = keep
|
268
|
+
string = @scanner.string
|
269
|
+
if @scanner != scanner
|
270
|
+
start = 0
|
271
|
+
end
|
272
|
+
if start == 0 and @scanner.eos?
|
273
|
+
keep_data = string
|
274
|
+
else
|
275
|
+
keep_data = string.byteslice(start, @scanner.pos - start)
|
276
|
+
end
|
277
|
+
if keep_data
|
278
|
+
if buffer
|
279
|
+
buffer << keep_data
|
280
|
+
else
|
281
|
+
keep[2] = keep_data.dup
|
223
282
|
end
|
224
|
-
keep[0] = 0
|
225
283
|
end
|
226
284
|
|
285
|
+
# trace(__method__, :done)
|
286
|
+
end
|
287
|
+
|
288
|
+
def read_chunk
|
289
|
+
return false if @last_scanner
|
290
|
+
|
291
|
+
adjust_last_keep
|
292
|
+
|
227
293
|
input = @inputs.first
|
228
294
|
case input
|
229
295
|
when StringIO
|
230
296
|
string = input.read
|
231
297
|
raise InvalidEncoding unless string.valid_encoding?
|
298
|
+
# trace(__method__, :stringio, string)
|
232
299
|
@scanner = StringScanner.new(string)
|
233
300
|
@inputs.shift
|
234
301
|
@last_scanner = @inputs.empty?
|
@@ -237,6 +304,7 @@ class CSV
|
|
237
304
|
chunk = input.gets(@row_separator, @chunk_size)
|
238
305
|
if chunk
|
239
306
|
raise InvalidEncoding unless chunk.valid_encoding?
|
307
|
+
# trace(__method__, :chunk, chunk)
|
240
308
|
@scanner = StringScanner.new(chunk)
|
241
309
|
if input.respond_to?(:eof?) and input.eof?
|
242
310
|
@inputs.shift
|
@@ -244,6 +312,7 @@ class CSV
|
|
244
312
|
end
|
245
313
|
true
|
246
314
|
else
|
315
|
+
# trace(__method__, :no_chunk)
|
247
316
|
@scanner = StringScanner.new("".encode(@encoding))
|
248
317
|
@inputs.shift
|
249
318
|
@last_scanner = @inputs.empty?
|
@@ -278,7 +347,11 @@ class CSV
|
|
278
347
|
end
|
279
348
|
|
280
349
|
def field_size_limit
|
281
|
-
@
|
350
|
+
@max_field_size&.succ
|
351
|
+
end
|
352
|
+
|
353
|
+
def max_field_size
|
354
|
+
@max_field_size
|
282
355
|
end
|
283
356
|
|
284
357
|
def skip_lines
|
@@ -346,6 +419,16 @@ class CSV
|
|
346
419
|
end
|
347
420
|
message = "Invalid byte sequence in #{@encoding}"
|
348
421
|
raise MalformedCSVError.new(message, lineno)
|
422
|
+
rescue UnexpectedError => error
|
423
|
+
if @scanner
|
424
|
+
ignore_broken_line
|
425
|
+
lineno = @lineno
|
426
|
+
else
|
427
|
+
lineno = @lineno + 1
|
428
|
+
end
|
429
|
+
message = "This should not be happen: #{error.message}: "
|
430
|
+
message += "Please report this to https://github.com/ruby/csv/issues"
|
431
|
+
raise MalformedCSVError.new(message, lineno)
|
349
432
|
end
|
350
433
|
end
|
351
434
|
|
@@ -390,7 +473,7 @@ class CSV
|
|
390
473
|
@backslash_quote = false
|
391
474
|
end
|
392
475
|
@unconverted_fields = @options[:unconverted_fields]
|
393
|
-
@
|
476
|
+
@max_field_size = @options[:max_field_size]
|
394
477
|
@skip_blanks = @options[:skip_blanks]
|
395
478
|
@fields_converter = @options[:fields_converter]
|
396
479
|
@header_fields_converter = @options[:header_fields_converter]
|
@@ -729,28 +812,28 @@ class CSV
|
|
729
812
|
sample[0, 128].index(@quote_character)
|
730
813
|
end
|
731
814
|
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
@io = StringIO.new(string, "rb:#{string.encoding}")
|
737
|
-
end
|
815
|
+
class UnoptimizedStringIO # :nodoc:
|
816
|
+
def initialize(string)
|
817
|
+
@io = StringIO.new(string, "rb:#{string.encoding}")
|
818
|
+
end
|
738
819
|
|
739
|
-
|
740
|
-
|
741
|
-
|
820
|
+
def gets(*args)
|
821
|
+
@io.gets(*args)
|
822
|
+
end
|
742
823
|
|
743
|
-
|
744
|
-
|
745
|
-
|
824
|
+
def each_line(*args, &block)
|
825
|
+
@io.each_line(*args, &block)
|
826
|
+
end
|
746
827
|
|
747
|
-
|
748
|
-
|
749
|
-
end
|
828
|
+
def eof?
|
829
|
+
@io.eof?
|
750
830
|
end
|
831
|
+
end
|
751
832
|
|
752
|
-
|
753
|
-
|
833
|
+
SCANNER_TEST = (ENV["CSV_PARSER_SCANNER_TEST"] == "yes")
|
834
|
+
if SCANNER_TEST
|
835
|
+
SCANNER_TEST_CHUNK_SIZE_NAME = "CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"
|
836
|
+
SCANNER_TEST_CHUNK_SIZE_VALUE = ENV[SCANNER_TEST_CHUNK_SIZE_NAME]
|
754
837
|
def build_scanner
|
755
838
|
inputs = @samples.collect do |sample|
|
756
839
|
UnoptimizedStringIO.new(sample)
|
@@ -760,10 +843,17 @@ class CSV
|
|
760
843
|
else
|
761
844
|
inputs << @input
|
762
845
|
end
|
846
|
+
begin
|
847
|
+
chunk_size_value = ENV[SCANNER_TEST_CHUNK_SIZE_NAME]
|
848
|
+
rescue # Ractor::IsolationError
|
849
|
+
# Ractor on Ruby 3.0 can't read ENV value.
|
850
|
+
chunk_size_value = SCANNER_TEST_CHUNK_SIZE_VALUE
|
851
|
+
end
|
852
|
+
chunk_size = Integer((chunk_size_value || "1"), 10)
|
763
853
|
InputsScanner.new(inputs,
|
764
854
|
@encoding,
|
765
855
|
@row_separator,
|
766
|
-
chunk_size:
|
856
|
+
chunk_size: chunk_size)
|
767
857
|
end
|
768
858
|
else
|
769
859
|
def build_scanner
|
@@ -826,6 +916,14 @@ class CSV
|
|
826
916
|
end
|
827
917
|
end
|
828
918
|
|
919
|
+
def validate_field_size(field)
|
920
|
+
return unless @max_field_size
|
921
|
+
return if field.size <= @max_field_size
|
922
|
+
ignore_broken_line
|
923
|
+
message = "Field size exceeded: #{field.size} > #{@max_field_size}"
|
924
|
+
raise MalformedCSVError.new(message, @lineno)
|
925
|
+
end
|
926
|
+
|
829
927
|
def parse_no_quote(&block)
|
830
928
|
@scanner.each_line(@row_separator) do |line|
|
831
929
|
next if @skip_lines and skip_line?(line)
|
@@ -838,6 +936,11 @@ class CSV
|
|
838
936
|
else
|
839
937
|
line = strip_value(line)
|
840
938
|
row = line.split(@split_column_separator, -1)
|
939
|
+
if @max_field_size
|
940
|
+
row.each do |column|
|
941
|
+
validate_field_size(column)
|
942
|
+
end
|
943
|
+
end
|
841
944
|
n_columns = row.size
|
842
945
|
i = 0
|
843
946
|
while i < n_columns
|
@@ -893,6 +996,7 @@ class CSV
|
|
893
996
|
@need_robust_parsing = true
|
894
997
|
return parse_quotable_robust(&block)
|
895
998
|
end
|
999
|
+
validate_field_size(row[i])
|
896
1000
|
end
|
897
1001
|
i += 1
|
898
1002
|
end
|
@@ -916,10 +1020,7 @@ class CSV
|
|
916
1020
|
value = parse_column_value
|
917
1021
|
if value
|
918
1022
|
@scanner.scan_all(@strip_value) if @strip_value
|
919
|
-
|
920
|
-
ignore_broken_line
|
921
|
-
raise MalformedCSVError.new("Field size exceeded", @lineno)
|
922
|
-
end
|
1023
|
+
validate_field_size(value)
|
923
1024
|
end
|
924
1025
|
if parse_column_end
|
925
1026
|
row << value
|
@@ -940,8 +1041,14 @@ class CSV
|
|
940
1041
|
break
|
941
1042
|
else
|
942
1043
|
if @quoted_column_value
|
1044
|
+
if liberal_parsing? and (new_line = @scanner.check(@line_end))
|
1045
|
+
message =
|
1046
|
+
"Illegal end-of-line sequence outside of a quoted field " +
|
1047
|
+
"<#{new_line.inspect}>"
|
1048
|
+
else
|
1049
|
+
message = "Any value after quoted field isn't allowed"
|
1050
|
+
end
|
943
1051
|
ignore_broken_line
|
944
|
-
message = "Any value after quoted field isn't allowed"
|
945
1052
|
raise MalformedCSVError.new(message, @lineno)
|
946
1053
|
elsif @unquoted_column_value and
|
947
1054
|
(new_line = @scanner.scan(@line_end))
|
data/lib/csv/table.rb
CHANGED
@@ -999,9 +999,15 @@ class CSV
|
|
999
999
|
# Omits the headers if option +write_headers+ is given as +false+
|
1000
1000
|
# (see {Option +write_headers+}[../CSV.html#class-CSV-label-Option+write_headers]):
|
1001
1001
|
# table.to_csv(write_headers: false) # => "foo,0\nbar,1\nbaz,2\n"
|
1002
|
-
|
1002
|
+
#
|
1003
|
+
# Limit rows if option +limit+ is given like +2+:
|
1004
|
+
# table.to_csv(limit: 2) # => "Name,Value\nfoo,0\nbar,1\n"
|
1005
|
+
def to_csv(write_headers: true, limit: nil, **options)
|
1003
1006
|
array = write_headers ? [headers.to_csv(**options)] : []
|
1004
|
-
@table.
|
1007
|
+
limit ||= @table.size
|
1008
|
+
limit = @table.size + 1 + limit if limit < 0
|
1009
|
+
limit = 0 if limit < 0
|
1010
|
+
@table.first(limit).each do |row|
|
1005
1011
|
array.push(row.fields.to_csv(**options)) unless row.header_row?
|
1006
1012
|
end
|
1007
1013
|
|
@@ -1038,9 +1044,13 @@ class CSV
|
|
1038
1044
|
# Example:
|
1039
1045
|
# source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
|
1040
1046
|
# table = CSV.parse(source, headers: true)
|
1041
|
-
# table.inspect # => "#<CSV::Table mode:col_or_row row_count:4
|
1047
|
+
# table.inspect # => "#<CSV::Table mode:col_or_row row_count:4>\nName,Value\nfoo,0\nbar,1\nbaz,2\n"
|
1048
|
+
#
|
1042
1049
|
def inspect
|
1043
|
-
"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>"
|
1050
|
+
inspected = +"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>"
|
1051
|
+
summary = to_csv(limit: 5)
|
1052
|
+
inspected << "\n" << summary if summary.encoding.ascii_compatible?
|
1053
|
+
inspected
|
1044
1054
|
end
|
1045
1055
|
end
|
1046
1056
|
end
|
data/lib/csv/version.rb
CHANGED
data/lib/csv.rb
CHANGED
@@ -357,7 +357,9 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
|
|
357
357
|
# - +row_sep+: Specifies the row separator; used to delimit rows.
|
358
358
|
# - +col_sep+: Specifies the column separator; used to delimit fields.
|
359
359
|
# - +quote_char+: Specifies the quote character; used to quote fields.
|
360
|
-
# - +field_size_limit+: Specifies the maximum field size allowed.
|
360
|
+
# - +field_size_limit+: Specifies the maximum field size + 1 allowed.
|
361
|
+
# Deprecated since 3.2.3. Use +max_field_size+ instead.
|
362
|
+
# - +max_field_size+: Specifies the maximum field size allowed.
|
361
363
|
# - +converters+: Specifies the field converters to be used.
|
362
364
|
# - +unconverted_fields+: Specifies whether unconverted fields are to be available.
|
363
365
|
# - +headers+: Specifies whether data contains headers,
|
@@ -926,7 +928,8 @@ class CSV
|
|
926
928
|
symbol: lambda { |h|
|
927
929
|
h.encode(ConverterEncoding).downcase.gsub(/[^\s\w]+/, "").strip.
|
928
930
|
gsub(/\s+/, "_").to_sym
|
929
|
-
}
|
931
|
+
},
|
932
|
+
symbol_raw: lambda { |h| h.encode(ConverterEncoding).to_sym }
|
930
933
|
}
|
931
934
|
|
932
935
|
# Default values for method options.
|
@@ -937,6 +940,7 @@ class CSV
|
|
937
940
|
quote_char: '"',
|
938
941
|
# For parsing.
|
939
942
|
field_size_limit: nil,
|
943
|
+
max_field_size: nil,
|
940
944
|
converters: nil,
|
941
945
|
unconverted_fields: nil,
|
942
946
|
headers: false,
|
@@ -1865,6 +1869,7 @@ class CSV
|
|
1865
1869
|
row_sep: :auto,
|
1866
1870
|
quote_char: '"',
|
1867
1871
|
field_size_limit: nil,
|
1872
|
+
max_field_size: nil,
|
1868
1873
|
converters: nil,
|
1869
1874
|
unconverted_fields: nil,
|
1870
1875
|
headers: false,
|
@@ -1907,11 +1912,14 @@ class CSV
|
|
1907
1912
|
@initial_header_converters = header_converters
|
1908
1913
|
@initial_write_converters = write_converters
|
1909
1914
|
|
1915
|
+
if max_field_size.nil? and field_size_limit
|
1916
|
+
max_field_size = field_size_limit - 1
|
1917
|
+
end
|
1910
1918
|
@parser_options = {
|
1911
1919
|
column_separator: col_sep,
|
1912
1920
|
row_separator: row_sep,
|
1913
1921
|
quote_character: quote_char,
|
1914
|
-
|
1922
|
+
max_field_size: max_field_size,
|
1915
1923
|
unconverted_fields: unconverted_fields,
|
1916
1924
|
headers: headers,
|
1917
1925
|
return_headers: return_headers,
|
@@ -1979,10 +1987,24 @@ class CSV
|
|
1979
1987
|
# Returns the limit for field size; used for parsing;
|
1980
1988
|
# see {Option +field_size_limit+}[#class-CSV-label-Option+field_size_limit]:
|
1981
1989
|
# CSV.new('').field_size_limit # => nil
|
1990
|
+
#
|
1991
|
+
# Deprecated since 3.2.3. Use +max_field_size+ instead.
|
1982
1992
|
def field_size_limit
|
1983
1993
|
parser.field_size_limit
|
1984
1994
|
end
|
1985
1995
|
|
1996
|
+
# :call-seq:
|
1997
|
+
# csv.max_field_size -> integer or nil
|
1998
|
+
#
|
1999
|
+
# Returns the limit for field size; used for parsing;
|
2000
|
+
# see {Option +max_field_size+}[#class-CSV-label-Option+max_field_size]:
|
2001
|
+
# CSV.new('').max_field_size # => nil
|
2002
|
+
#
|
2003
|
+
# Since 3.2.3.
|
2004
|
+
def max_field_size
|
2005
|
+
parser.max_field_size
|
2006
|
+
end
|
2007
|
+
|
1986
2008
|
# :call-seq:
|
1987
2009
|
# csv.skip_lines -> regexp or nil
|
1988
2010
|
#
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: csv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.2.
|
4
|
+
version: 3.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Edward Gray II
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-04-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -147,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
147
|
- !ruby/object:Gem::Version
|
148
148
|
version: '0'
|
149
149
|
requirements: []
|
150
|
-
rubygems_version: 3.
|
150
|
+
rubygems_version: 3.4.0.dev
|
151
151
|
signing_key:
|
152
152
|
specification_version: 4
|
153
153
|
summary: CSV Reading and Writing
|