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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8682072d16c079d3d25b3f22ca9f06cae36210998194ae3f3de627c74c062453
4
- data.tar.gz: 755bddbed0b08dd681939a76c5f6a80f2c536a0e72edcf6e8c770be860e5fcae
3
+ metadata.gz: 915b3ed5a51bf4836f08f7bb06efc3b07bdc90e09209a5253092130e2cad2ab6
4
+ data.tar.gz: 6bce2e39329afcf200691b4b2f422b6a48d45da66368f5d5e136e0c761cd6217
5
5
  SHA512:
6
- metadata.gz: 28e191df9cb41c6ca04a9969eace28deac9217de9f2677b7edec8803531c28b4c47e4866d00fd6edbc5ea0b21f10a4f59b376473475a8c96317949b84b53f49a
7
- data.tar.gz: 4c747ddbdb78e4e6e8dc725199b2f35aea7949b7eb5b49929c2e818d15cbebfaa07825f57a5ee092a5da7f5c75fc2ea3c035ff204f1a95a12283cea85720e31c
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
@@ -19,7 +19,7 @@ Without +write_headers+:
19
19
 
20
20
  With +write_headers+":
21
21
  CSV.open(file_path,'w',
22
- :write_headers=> true,
22
+ :write_headers => true,
23
23
  :headers => ['Name','Value']
24
24
  ) do |csv|
25
25
  csv << ['foo', '0']
@@ -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 coverters
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
- Ouput:
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
- is_input_record_separator_deprecated = false
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 scan pointer and returns the matched string.
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 rest of the string (i.e. everything after the scan pointer).
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
- read_chunk if @scanner.eos?
148
- return value
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
- @keeps.push([@scanner.pos, nil])
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
- start, buffer = @keeps.pop
175
- keep = @scanner.string.byteslice(start, @scanner.pos - start)
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
- start, buffer = @keeps.pop
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
- keep = string.byteslice(start, string.bytesize - start)
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 read_chunk
209
- return false if @last_scanner
256
+ def trace(*args)
257
+ pp([*args, @scanner, @scanner&.string, @scanner&.pos, @keeps])
258
+ end
210
259
 
211
- unless @keeps.empty?
212
- keep = @keeps.last
213
- keep_start = keep[0]
214
- string = @scanner.string
215
- keep_data = string.byteslice(keep_start, @scanner.pos - keep_start)
216
- if keep_data
217
- keep_buffer = keep[1]
218
- if keep_buffer
219
- keep_buffer << keep_data
220
- else
221
- keep[1] = keep_data.dup
222
- end
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
- @field_size_limit
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
- @field_size_limit = @options[:field_size_limit]
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
- SCANNER_TEST = (ENV["CSV_PARSER_SCANNER_TEST"] == "yes")
733
- if SCANNER_TEST
734
- class UnoptimizedStringIO
735
- def initialize(string)
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
- def gets(*args)
740
- @io.gets(*args)
741
- end
820
+ def gets(*args)
821
+ @io.gets(*args)
822
+ end
742
823
 
743
- def each_line(*args, &block)
744
- @io.each_line(*args, &block)
745
- end
824
+ def each_line(*args, &block)
825
+ @io.each_line(*args, &block)
826
+ end
746
827
 
747
- def eof?
748
- @io.eof?
749
- end
828
+ def eof?
829
+ @io.eof?
750
830
  end
831
+ end
751
832
 
752
- SCANNER_TEST_CHUNK_SIZE =
753
- Integer((ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"), 10)
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: SCANNER_TEST_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
- if @field_size_limit and value.size >= @field_size_limit
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
- def to_csv(write_headers: true, **options)
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.each do |row|
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}>".encode("US-ASCII")
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  class CSV
4
4
  # The version of the installed library.
5
- VERSION = "3.2.2"
5
+ VERSION = "3.2.3"
6
6
  end
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
- field_size_limit: field_size_limit,
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.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: 2021-12-24 00:00:00.000000000 Z
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.3.0
150
+ rubygems_version: 3.4.0.dev
151
151
  signing_key:
152
152
  specification_version: 4
153
153
  summary: CSV Reading and Writing