csv 3.1.9 → 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.
data/lib/csv/parser.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "strscan"
4
4
 
5
5
  require_relative "delete_suffix"
6
+ require_relative "input_record_separator"
6
7
  require_relative "match_p"
7
8
  require_relative "row"
8
9
  require_relative "table"
@@ -26,6 +27,10 @@ class CSV
26
27
  class InvalidEncoding < StandardError
27
28
  end
28
29
 
30
+ # Raised when unexpected case is happen.
31
+ class UnexpectedError < StandardError
32
+ end
33
+
29
34
  #
30
35
  # CSV::Scanner receives a CSV output, scans it and return the content.
31
36
  # It also controls the life cycle of the object with its methods +keep_start+,
@@ -77,16 +82,17 @@ class CSV
77
82
  # +keep_end+, +keep_back+, +keep_drop+.
78
83
  #
79
84
  # CSV::InputsScanner.scan() tries to match with pattern at the current position.
80
- # 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.
81
86
  # Otherwise, the scanner returns nil.
82
87
  #
83
- # 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).
84
89
  # If there is no more data (eos? = true), it returns "".
85
90
  #
86
91
  class InputsScanner
87
- def initialize(inputs, encoding, chunk_size: 8192)
92
+ def initialize(inputs, encoding, row_separator, chunk_size: 8192)
88
93
  @inputs = inputs.dup
89
94
  @encoding = encoding
95
+ @row_separator = row_separator
90
96
  @chunk_size = chunk_size
91
97
  @last_scanner = @inputs.empty?
92
98
  @keeps = []
@@ -94,11 +100,13 @@ class CSV
94
100
  end
95
101
 
96
102
  def each_line(row_separator)
103
+ return enum_for(__method__, row_separator) unless block_given?
97
104
  buffer = nil
98
105
  input = @scanner.rest
99
106
  position = @scanner.pos
100
107
  offset = 0
101
108
  n_row_separator_chars = row_separator.size
109
+ # trace(__method__, :start, line, input)
102
110
  while true
103
111
  input.each_line(row_separator) do |line|
104
112
  @scanner.pos += line.bytesize
@@ -138,25 +146,28 @@ class CSV
138
146
  end
139
147
 
140
148
  def scan(pattern)
149
+ # trace(__method__, pattern, :start)
141
150
  value = @scanner.scan(pattern)
151
+ # trace(__method__, pattern, :done, :last, value) if @last_scanner
142
152
  return value if @last_scanner
143
153
 
144
- if value
145
- read_chunk if @scanner.eos?
146
- return value
147
- else
148
- nil
149
- end
154
+ read_chunk if value and @scanner.eos?
155
+ # trace(__method__, pattern, :done, value)
156
+ value
150
157
  end
151
158
 
152
159
  def scan_all(pattern)
160
+ # trace(__method__, pattern, :start)
153
161
  value = @scanner.scan(pattern)
162
+ # trace(__method__, pattern, :done, :last, value) if @last_scanner
154
163
  return value if @last_scanner
155
164
 
156
165
  return nil if value.nil?
157
166
  while @scanner.eos? and read_chunk and (sub_value = @scanner.scan(pattern))
167
+ # trace(__method__, pattern, :sub, sub_value)
158
168
  value << sub_value
159
169
  end
170
+ # trace(__method__, pattern, :done, value)
160
171
  value
161
172
  end
162
173
 
@@ -165,76 +176,135 @@ class CSV
165
176
  end
166
177
 
167
178
  def keep_start
168
- @keeps.push([@scanner.pos, nil])
179
+ # trace(__method__, :start)
180
+ adjust_last_keep
181
+ @keeps.push([@scanner, @scanner.pos, nil])
182
+ # trace(__method__, :done)
169
183
  end
170
184
 
171
185
  def keep_end
172
- start, buffer = @keeps.pop
173
- 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
174
193
  if buffer
175
194
  buffer << keep
176
195
  keep = buffer
177
196
  end
197
+ # trace(__method__, :done, keep)
178
198
  keep
179
199
  end
180
200
 
181
201
  def keep_back
182
- start, buffer = @keeps.pop
202
+ # trace(__method__, :start)
203
+ scanner, start, buffer = @keeps.pop
183
204
  if buffer
205
+ # trace(__method__, :rescan, start, buffer)
184
206
  string = @scanner.string
185
- 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
186
212
  if keep and not keep.empty?
187
213
  @inputs.unshift(StringIO.new(keep))
188
214
  @last_scanner = false
189
215
  end
190
216
  @scanner = StringScanner.new(buffer)
191
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)
192
225
  @scanner.pos = start
193
226
  end
194
227
  read_chunk if @scanner.eos?
195
228
  end
196
229
 
197
230
  def keep_drop
198
- @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)
199
245
  end
200
246
 
201
247
  def rest
202
248
  @scanner.rest
203
249
  end
204
250
 
251
+ def check(pattern)
252
+ @scanner.check(pattern)
253
+ end
254
+
205
255
  private
206
- def read_chunk
207
- return false if @last_scanner
256
+ def trace(*args)
257
+ pp([*args, @scanner, @scanner&.string, @scanner&.pos, @keeps])
258
+ end
208
259
 
209
- unless @keeps.empty?
210
- keep = @keeps.last
211
- keep_start = keep[0]
212
- string = @scanner.string
213
- keep_data = string.byteslice(keep_start, @scanner.pos - keep_start)
214
- if keep_data
215
- keep_buffer = keep[1]
216
- if keep_buffer
217
- keep_buffer << keep_data
218
- else
219
- keep[1] = keep_data.dup
220
- 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
221
282
  end
222
- keep[0] = 0
223
283
  end
224
284
 
285
+ # trace(__method__, :done)
286
+ end
287
+
288
+ def read_chunk
289
+ return false if @last_scanner
290
+
291
+ adjust_last_keep
292
+
225
293
  input = @inputs.first
226
294
  case input
227
295
  when StringIO
228
296
  string = input.read
229
297
  raise InvalidEncoding unless string.valid_encoding?
298
+ # trace(__method__, :stringio, string)
230
299
  @scanner = StringScanner.new(string)
231
300
  @inputs.shift
232
301
  @last_scanner = @inputs.empty?
233
302
  true
234
303
  else
235
- chunk = input.gets(nil, @chunk_size)
304
+ chunk = input.gets(@row_separator, @chunk_size)
236
305
  if chunk
237
306
  raise InvalidEncoding unless chunk.valid_encoding?
307
+ # trace(__method__, :chunk, chunk)
238
308
  @scanner = StringScanner.new(chunk)
239
309
  if input.respond_to?(:eof?) and input.eof?
240
310
  @inputs.shift
@@ -242,6 +312,7 @@ class CSV
242
312
  end
243
313
  true
244
314
  else
315
+ # trace(__method__, :no_chunk)
245
316
  @scanner = StringScanner.new("".encode(@encoding))
246
317
  @inputs.shift
247
318
  @last_scanner = @inputs.empty?
@@ -276,7 +347,11 @@ class CSV
276
347
  end
277
348
 
278
349
  def field_size_limit
279
- @field_size_limit
350
+ @max_field_size&.succ
351
+ end
352
+
353
+ def max_field_size
354
+ @max_field_size
280
355
  end
281
356
 
282
357
  def skip_lines
@@ -344,6 +419,16 @@ class CSV
344
419
  end
345
420
  message = "Invalid byte sequence in #{@encoding}"
346
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)
347
432
  end
348
433
  end
349
434
 
@@ -360,6 +445,7 @@ class CSV
360
445
  prepare_skip_lines
361
446
  prepare_strip
362
447
  prepare_separators
448
+ validate_strip_and_col_sep_options
363
449
  prepare_quoted
364
450
  prepare_unquoted
365
451
  prepare_line
@@ -387,7 +473,7 @@ class CSV
387
473
  @backslash_quote = false
388
474
  end
389
475
  @unconverted_fields = @options[:unconverted_fields]
390
- @field_size_limit = @options[:field_size_limit]
476
+ @max_field_size = @options[:max_field_size]
391
477
  @skip_blanks = @options[:skip_blanks]
392
478
  @fields_converter = @options[:fields_converter]
393
479
  @header_fields_converter = @options[:header_fields_converter]
@@ -479,9 +565,9 @@ class CSV
479
565
  begin
480
566
  StringScanner.new("x").scan("x")
481
567
  rescue TypeError
482
- @@string_scanner_scan_accept_string = false
568
+ STRING_SCANNER_SCAN_ACCEPT_STRING = false
483
569
  else
484
- @@string_scanner_scan_accept_string = true
570
+ STRING_SCANNER_SCAN_ACCEPT_STRING = true
485
571
  end
486
572
 
487
573
  def prepare_separators
@@ -505,7 +591,7 @@ class CSV
505
591
  @first_column_separators = Regexp.new(@escaped_first_column_separator +
506
592
  "+".encode(@encoding))
507
593
  else
508
- if @@string_scanner_scan_accept_string
594
+ if STRING_SCANNER_SCAN_ACCEPT_STRING
509
595
  @column_end = @column_separator
510
596
  else
511
597
  @column_end = Regexp.new(@escaped_column_separator)
@@ -526,10 +612,32 @@ class CSV
526
612
 
527
613
  @cr = "\r".encode(@encoding)
528
614
  @lf = "\n".encode(@encoding)
529
- @cr_or_lf = Regexp.new("[\r\n]".encode(@encoding))
615
+ @line_end = Regexp.new("\r\n|\n|\r".encode(@encoding))
530
616
  @not_line_end = Regexp.new("[^\r\n]+".encode(@encoding))
531
617
  end
532
618
 
619
+ # This method verifies that there are no (obvious) ambiguities with the
620
+ # provided +col_sep+ and +strip+ parsing options. For example, if +col_sep+
621
+ # and +strip+ were both equal to +\t+, then there would be no clear way to
622
+ # parse the input.
623
+ def validate_strip_and_col_sep_options
624
+ return unless @strip
625
+
626
+ if @strip.is_a?(String)
627
+ if @column_separator.start_with?(@strip) || @column_separator.end_with?(@strip)
628
+ raise ArgumentError,
629
+ "The provided strip (#{@escaped_strip}) and " \
630
+ "col_sep (#{@escaped_column_separator}) options are incompatible."
631
+ end
632
+ else
633
+ if Regexp.new("\\A[#{@escaped_strip}]|[#{@escaped_strip}]\\z").match?(@column_separator)
634
+ raise ArgumentError,
635
+ "The provided strip (true) and " \
636
+ "col_sep (#{@escaped_column_separator}) options are incompatible."
637
+ end
638
+ end
639
+ end
640
+
533
641
  def prepare_quoted
534
642
  if @quote_character
535
643
  @quotes = Regexp.new(@escaped_quote_character +
@@ -605,7 +713,7 @@ class CSV
605
713
  # do nothing: ensure will set default
606
714
  end
607
715
  end
608
- separator = $INPUT_RECORD_SEPARATOR if separator == :auto
716
+ separator = InputRecordSeparator.value if separator == :auto
609
717
  end
610
718
  separator.to_s.encode(@encoding)
611
719
  end
@@ -704,26 +812,28 @@ class CSV
704
812
  sample[0, 128].index(@quote_character)
705
813
  end
706
814
 
707
- SCANNER_TEST = (ENV["CSV_PARSER_SCANNER_TEST"] == "yes")
708
- if SCANNER_TEST
709
- class UnoptimizedStringIO
710
- def initialize(string)
711
- @io = StringIO.new(string, "rb:#{string.encoding}")
712
- end
815
+ class UnoptimizedStringIO # :nodoc:
816
+ def initialize(string)
817
+ @io = StringIO.new(string, "rb:#{string.encoding}")
818
+ end
713
819
 
714
- def gets(*args)
715
- @io.gets(*args)
716
- end
820
+ def gets(*args)
821
+ @io.gets(*args)
822
+ end
717
823
 
718
- def each_line(*args, &block)
719
- @io.each_line(*args, &block)
720
- end
824
+ def each_line(*args, &block)
825
+ @io.each_line(*args, &block)
826
+ end
721
827
 
722
- def eof?
723
- @io.eof?
724
- end
828
+ def eof?
829
+ @io.eof?
725
830
  end
831
+ end
726
832
 
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]
727
837
  def build_scanner
728
838
  inputs = @samples.collect do |sample|
729
839
  UnoptimizedStringIO.new(sample)
@@ -733,17 +843,27 @@ class CSV
733
843
  else
734
844
  inputs << @input
735
845
  end
736
- chunk_size = ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"
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)
737
853
  InputsScanner.new(inputs,
738
854
  @encoding,
739
- chunk_size: Integer(chunk_size, 10))
855
+ @row_separator,
856
+ chunk_size: chunk_size)
740
857
  end
741
858
  else
742
859
  def build_scanner
743
860
  string = nil
744
861
  if @samples.empty? and @input.is_a?(StringIO)
745
862
  string = @input.read
746
- elsif @samples.size == 1 and @input.respond_to?(:eof?) and @input.eof?
863
+ elsif @samples.size == 1 and
864
+ @input != ARGF and
865
+ @input.respond_to?(:eof?) and
866
+ @input.eof?
747
867
  string = @samples[0]
748
868
  end
749
869
  if string
@@ -762,7 +882,7 @@ class CSV
762
882
  StringIO.new(sample)
763
883
  end
764
884
  inputs << @input
765
- InputsScanner.new(inputs, @encoding)
885
+ InputsScanner.new(inputs, @encoding, @row_separator)
766
886
  end
767
887
  end
768
888
  end
@@ -796,6 +916,14 @@ class CSV
796
916
  end
797
917
  end
798
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
+
799
927
  def parse_no_quote(&block)
800
928
  @scanner.each_line(@row_separator) do |line|
801
929
  next if @skip_lines and skip_line?(line)
@@ -808,6 +936,11 @@ class CSV
808
936
  else
809
937
  line = strip_value(line)
810
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
811
944
  n_columns = row.size
812
945
  i = 0
813
946
  while i < n_columns
@@ -863,6 +996,7 @@ class CSV
863
996
  @need_robust_parsing = true
864
997
  return parse_quotable_robust(&block)
865
998
  end
999
+ validate_field_size(row[i])
866
1000
  end
867
1001
  i += 1
868
1002
  end
@@ -886,10 +1020,7 @@ class CSV
886
1020
  value = parse_column_value
887
1021
  if value
888
1022
  @scanner.scan_all(@strip_value) if @strip_value
889
- if @field_size_limit and value.size >= @field_size_limit
890
- ignore_broken_line
891
- raise MalformedCSVError.new("Field size exceeded", @lineno)
892
- end
1023
+ validate_field_size(value)
893
1024
  end
894
1025
  if parse_column_end
895
1026
  row << value
@@ -910,11 +1041,17 @@ class CSV
910
1041
  break
911
1042
  else
912
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
913
1051
  ignore_broken_line
914
- message = "Any value after quoted field isn't allowed"
915
1052
  raise MalformedCSVError.new(message, @lineno)
916
1053
  elsif @unquoted_column_value and
917
- (new_line = @scanner.scan(@cr_or_lf))
1054
+ (new_line = @scanner.scan(@line_end))
918
1055
  ignore_broken_line
919
1056
  message = "Unquoted fields do not allow new line " +
920
1057
  "<#{new_line.inspect}>"
@@ -923,7 +1060,7 @@ class CSV
923
1060
  ignore_broken_line
924
1061
  message = "Illegal quoting"
925
1062
  raise MalformedCSVError.new(message, @lineno)
926
- elsif (new_line = @scanner.scan(@cr_or_lf))
1063
+ elsif (new_line = @scanner.scan(@line_end))
927
1064
  ignore_broken_line
928
1065
  message = "New line must be <#{@row_separator.inspect}> " +
929
1066
  "not <#{new_line.inspect}>"
@@ -1089,7 +1226,7 @@ class CSV
1089
1226
 
1090
1227
  def ignore_broken_line
1091
1228
  @scanner.scan_all(@not_line_end)
1092
- @scanner.scan_all(@cr_or_lf)
1229
+ @scanner.scan_all(@line_end)
1093
1230
  @lineno += 1
1094
1231
  end
1095
1232
 
data/lib/csv/row.rb CHANGED
@@ -659,8 +659,30 @@ class CSV
659
659
  end
660
660
  alias_method :to_hash, :to_h
661
661
 
662
+ # :call-seq:
663
+ # row.deconstruct_keys(keys) -> hash
664
+ #
665
+ # Returns the new \Hash suitable for pattern matching containing only the
666
+ # keys specified as an argument.
667
+ def deconstruct_keys(keys)
668
+ if keys.nil?
669
+ to_h
670
+ else
671
+ keys.to_h { |key| [key, self[key]] }
672
+ end
673
+ end
674
+
662
675
  alias_method :to_ary, :to_a
663
676
 
677
+ # :call-seq:
678
+ # row.deconstruct -> array
679
+ #
680
+ # Returns the new \Array suitable for pattern matching containing the values
681
+ # of the row.
682
+ def deconstruct
683
+ fields
684
+ end
685
+
664
686
  # :call-seq:
665
687
  # row.to_csv -> csv_string
666
688
  #
data/lib/csv/table.rb CHANGED
@@ -932,7 +932,9 @@ class CSV
932
932
  return enum_for(__method__) { @mode == :col ? headers.size : size } unless block_given?
933
933
 
934
934
  if @mode == :col
935
- headers.each { |header| yield([header, self[header]]) }
935
+ headers.each.with_index do |header, i|
936
+ yield([header, @table.map {|row| row[header, i]}])
937
+ end
936
938
  else
937
939
  @table.each(&block)
938
940
  end
@@ -997,9 +999,15 @@ class CSV
997
999
  # Omits the headers if option +write_headers+ is given as +false+
998
1000
  # (see {Option +write_headers+}[../CSV.html#class-CSV-label-Option+write_headers]):
999
1001
  # table.to_csv(write_headers: false) # => "foo,0\nbar,1\nbaz,2\n"
1000
- 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)
1001
1006
  array = write_headers ? [headers.to_csv(**options)] : []
1002
- @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|
1003
1011
  array.push(row.fields.to_csv(**options)) unless row.header_row?
1004
1012
  end
1005
1013
 
@@ -1036,9 +1044,13 @@ class CSV
1036
1044
  # Example:
1037
1045
  # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1038
1046
  # table = CSV.parse(source, headers: true)
1039
- # 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
+ #
1040
1049
  def inspect
1041
- "#<#{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
1042
1054
  end
1043
1055
  end
1044
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.1.9"
5
+ VERSION = "3.2.3"
6
6
  end
data/lib/csv/writer.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "input_record_separator"
3
4
  require_relative "match_p"
4
5
  require_relative "row"
5
6
 
@@ -133,7 +134,7 @@ class CSV
133
134
  @column_separator = @options[:column_separator].to_s.encode(@encoding)
134
135
  row_separator = @options[:row_separator]
135
136
  if row_separator == :auto
136
- @row_separator = $INPUT_RECORD_SEPARATOR.encode(@encoding)
137
+ @row_separator = InputRecordSeparator.value.encode(@encoding)
137
138
  else
138
139
  @row_separator = row_separator.to_s.encode(@encoding)
139
140
  end