csv 3.2.2 → 3.2.3
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/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
|