csv 3.1.9 → 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/csv.rb CHANGED
@@ -48,7 +48,7 @@
48
48
  #
49
49
  # === Interface
50
50
  #
51
- # * CSV now uses Hash-style parameters to set options.
51
+ # * CSV now uses keyword parameters to set options.
52
52
  # * CSV no longer has generate_row() or parse_row().
53
53
  # * The old CSV's Reader and Writer classes have been dropped.
54
54
  # * CSV::open() is now more like Ruby's open().
@@ -90,11 +90,11 @@
90
90
  # with any questions.
91
91
 
92
92
  require "forwardable"
93
- require "English"
94
93
  require "date"
95
94
  require "stringio"
96
95
 
97
96
  require_relative "csv/fields_converter"
97
+ require_relative "csv/input_record_separator"
98
98
  require_relative "csv/match_p"
99
99
  require_relative "csv/parser"
100
100
  require_relative "csv/row"
@@ -341,6 +341,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
341
341
  # liberal_parsing: false,
342
342
  # nil_value: nil,
343
343
  # empty_value: "",
344
+ # strip: false,
344
345
  # # For generating.
345
346
  # write_headers: nil,
346
347
  # quote_empty: true,
@@ -348,7 +349,6 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
348
349
  # write_converters: nil,
349
350
  # write_nil_value: nil,
350
351
  # write_empty_value: "",
351
- # strip: false,
352
352
  # }
353
353
  #
354
354
  # ==== Options for Parsing
@@ -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,
@@ -366,8 +368,9 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
366
368
  # - +header_converters+: Specifies the header converters to be used.
367
369
  # - +skip_blanks+: Specifies whether blanks lines are to be ignored.
368
370
  # - +skip_lines+: Specifies how comments lines are to be recognized.
369
- # - +strip+: Specifies whether leading and trailing whitespace are
370
- # to be stripped from fields..
371
+ # - +strip+: Specifies whether leading and trailing whitespace are to be
372
+ # stripped from fields. This must be compatible with +col_sep+; if it is not,
373
+ # then an +ArgumentError+ exception will be raised.
371
374
  # - +liberal_parsing+: Specifies whether \CSV should attempt to parse
372
375
  # non-compliant data.
373
376
  # - +nil_value+: Specifies the object that is to be substituted for each null (no-text) field.
@@ -513,7 +516,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
513
516
  # [" 1 ", #<struct CSV::FieldInfo index=1, line=2, header=nil>]
514
517
  # [" baz ", #<struct CSV::FieldInfo index=0, line=3, header=nil>]
515
518
  # [" 2 ", #<struct CSV::FieldInfo index=1, line=3, header=nil>]
516
- # Each CSV::Info object shows:
519
+ # Each CSV::FieldInfo object shows:
517
520
  # - The 0-based field index.
518
521
  # - The 1-based line index.
519
522
  # - The field header, if any.
@@ -547,6 +550,14 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
547
550
  #
548
551
  # There is no such storage structure for write headers.
549
552
  #
553
+ # In order for the parsing methods to access stored converters in non-main-Ractors, the
554
+ # storage structure must be made shareable first.
555
+ # Therefore, <tt>Ractor.make_shareable(CSV::Converters)</tt> and
556
+ # <tt>Ractor.make_shareable(CSV::HeaderConverters)</tt> must be called before the creation
557
+ # of Ractors that use the converters stored in these structures. (Since making the storage
558
+ # structures shareable involves freezing them, any custom converters that are to be used
559
+ # must be added first.)
560
+ #
550
561
  # ===== Converter Lists
551
562
  #
552
563
  # A _converter_ _list_ is an \Array that may include any assortment of:
@@ -705,7 +716,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
705
716
  # Header converters operate only on headers (and not on other rows).
706
717
  #
707
718
  # There are three ways to use header \converters;
708
- # these examples use built-in header converter +:dowhcase+,
719
+ # these examples use built-in header converter +:downcase+,
709
720
  # which downcases each parsed header.
710
721
  #
711
722
  # - Option +header_converters+ with a singleton parsing method:
@@ -917,8 +928,10 @@ class CSV
917
928
  symbol: lambda { |h|
918
929
  h.encode(ConverterEncoding).downcase.gsub(/[^\s\w]+/, "").strip.
919
930
  gsub(/\s+/, "_").to_sym
920
- }
931
+ },
932
+ symbol_raw: lambda { |h| h.encode(ConverterEncoding).to_sym }
921
933
  }
934
+
922
935
  # Default values for method options.
923
936
  DEFAULT_OPTIONS = {
924
937
  # For both parsing and generating.
@@ -927,6 +940,7 @@ class CSV
927
940
  quote_char: '"',
928
941
  # For parsing.
929
942
  field_size_limit: nil,
943
+ max_field_size: nil,
930
944
  converters: nil,
931
945
  unconverted_fields: nil,
932
946
  headers: false,
@@ -937,6 +951,7 @@ class CSV
937
951
  liberal_parsing: false,
938
952
  nil_value: nil,
939
953
  empty_value: "",
954
+ strip: false,
940
955
  # For generating.
941
956
  write_headers: nil,
942
957
  quote_empty: true,
@@ -944,7 +959,6 @@ class CSV
944
959
  write_converters: nil,
945
960
  write_nil_value: nil,
946
961
  write_empty_value: "",
947
- strip: false,
948
962
  }.freeze
949
963
 
950
964
  class << self
@@ -957,6 +971,8 @@ class CSV
957
971
  # Creates or retrieves cached \CSV objects.
958
972
  # For arguments and options, see CSV.new.
959
973
  #
974
+ # This API is not Ractor-safe.
975
+ #
960
976
  # ---
961
977
  #
962
978
  # With no block given, returns a \CSV object.
@@ -1006,63 +1022,188 @@ class CSV
1006
1022
  end
1007
1023
 
1008
1024
  # :call-seq:
1009
- # filter(**options) {|row| ... }
1010
- # filter(in_string, **options) {|row| ... }
1011
- # filter(in_io, **options) {|row| ... }
1012
- # filter(in_string, out_string, **options) {|row| ... }
1013
- # filter(in_string, out_io, **options) {|row| ... }
1014
- # filter(in_io, out_string, **options) {|row| ... }
1015
- # filter(in_io, out_io, **options) {|row| ... }
1016
- #
1017
- # Reads \CSV input and writes \CSV output.
1018
- #
1019
- # For each input row:
1020
- # - Forms the data into:
1021
- # - A CSV::Row object, if headers are in use.
1022
- # - An \Array of Arrays, otherwise.
1023
- # - Calls the block with that object.
1024
- # - Appends the block's return value to the output.
1025
+ # filter(in_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
1026
+ # filter(in_string_or_io, out_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
1027
+ # filter(**options) {|row| ... } -> array_of_arrays or csv_table
1025
1028
  #
1026
- # Arguments:
1027
- # * \CSV source:
1028
- # * Argument +in_string+, if given, should be a \String object;
1029
- # it will be put into a new StringIO object positioned at the beginning.
1030
- # * Argument +in_io+, if given, should be an IO object that is
1031
- # open for reading; on return, the IO object will be closed.
1032
- # * If neither +in_string+ nor +in_io+ is given,
1033
- # the input stream defaults to {ARGF}[https://ruby-doc.org/core/ARGF.html].
1034
- # * \CSV output:
1035
- # * Argument +out_string+, if given, should be a \String object;
1036
- # it will be put into a new StringIO object positioned at the beginning.
1037
- # * Argument +out_io+, if given, should be an IO object that is
1038
- # ppen for writing; on return, the IO object will be closed.
1039
- # * If neither +out_string+ nor +out_io+ is given,
1040
- # the output stream defaults to <tt>$stdout</tt>.
1041
- # * Argument +options+ should be keyword arguments.
1042
- # - Each argument name that is prefixed with +in_+ or +input_+
1043
- # is stripped of its prefix and is treated as an option
1044
- # for parsing the input.
1045
- # Option +input_row_sep+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>.
1046
- # - Each argument name that is prefixed with +out_+ or +output_+
1047
- # is stripped of its prefix and is treated as an option
1048
- # for generating the output.
1049
- # Option +output_row_sep+ defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>.
1050
- # - Each argument not prefixed as above is treated as an option
1051
- # both for parsing the input and for generating the output.
1052
- # - See {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
1053
- # and {Options for Generating}[#class-CSV-label-Options+for+Generating].
1029
+ # - Parses \CSV from a source (\String, \IO stream, or ARGF).
1030
+ # - Calls the given block with each parsed row:
1031
+ # - Without headers, each row is an \Array.
1032
+ # - With headers, each row is a CSV::Row.
1033
+ # - Generates \CSV to an output (\String, \IO stream, or STDOUT).
1034
+ # - Returns the parsed source:
1035
+ # - Without headers, an \Array of \Arrays.
1036
+ # - With headers, a CSV::Table.
1054
1037
  #
1055
- # Example:
1056
- # in_string = "foo,0\nbar,1\nbaz,2\n"
1038
+ # When +in_string_or_io+ is given, but not +out_string_or_io+,
1039
+ # parses from the given +in_string_or_io+
1040
+ # and generates to STDOUT.
1041
+ #
1042
+ # \String input without headers:
1043
+ #
1044
+ # in_string = "foo,0\nbar,1\nbaz,2"
1045
+ # CSV.filter(in_string) do |row|
1046
+ # row[0].upcase!
1047
+ # row[1] = - row[1].to_i
1048
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1049
+ #
1050
+ # Output (to STDOUT):
1051
+ #
1052
+ # FOO,0
1053
+ # BAR,-1
1054
+ # BAZ,-2
1055
+ #
1056
+ # \String input with headers:
1057
+ #
1058
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1059
+ # CSV.filter(in_string, headers: true) do |row|
1060
+ # row[0].upcase!
1061
+ # row[1] = - row[1].to_i
1062
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1063
+ #
1064
+ # Output (to STDOUT):
1065
+ #
1066
+ # Name,Value
1067
+ # FOO,0
1068
+ # BAR,-1
1069
+ # BAZ,-2
1070
+ #
1071
+ # \IO stream input without headers:
1072
+ #
1073
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
1074
+ # File.open('t.csv') do |in_io|
1075
+ # CSV.filter(in_io) do |row|
1076
+ # row[0].upcase!
1077
+ # row[1] = - row[1].to_i
1078
+ # end
1079
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1080
+ #
1081
+ # Output (to STDOUT):
1082
+ #
1083
+ # FOO,0
1084
+ # BAR,-1
1085
+ # BAZ,-2
1086
+ #
1087
+ # \IO stream input with headers:
1088
+ #
1089
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
1090
+ # File.open('t.csv') do |in_io|
1091
+ # CSV.filter(in_io, headers: true) do |row|
1092
+ # row[0].upcase!
1093
+ # row[1] = - row[1].to_i
1094
+ # end
1095
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1096
+ #
1097
+ # Output (to STDOUT):
1098
+ #
1099
+ # Name,Value
1100
+ # FOO,0
1101
+ # BAR,-1
1102
+ # BAZ,-2
1103
+ #
1104
+ # When both +in_string_or_io+ and +out_string_or_io+ are given,
1105
+ # parses from +in_string_or_io+ and generates to +out_string_or_io+.
1106
+ #
1107
+ # \String output without headers:
1108
+ #
1109
+ # in_string = "foo,0\nbar,1\nbaz,2"
1057
1110
  # out_string = ''
1058
1111
  # CSV.filter(in_string, out_string) do |row|
1059
- # row[0] = row[0].upcase
1060
- # row[1] *= 4
1061
- # end
1062
- # out_string # => "FOO,0000\nBAR,1111\nBAZ,2222\n"
1112
+ # row[0].upcase!
1113
+ # row[1] = - row[1].to_i
1114
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1115
+ # out_string # => "FOO,0\nBAR,-1\nBAZ,-2\n"
1116
+ #
1117
+ # \String output with headers:
1118
+ #
1119
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1120
+ # out_string = ''
1121
+ # CSV.filter(in_string, out_string, headers: true) do |row|
1122
+ # row[0].upcase!
1123
+ # row[1] = - row[1].to_i
1124
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1125
+ # out_string # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
1126
+ #
1127
+ # \IO stream output without headers:
1128
+ #
1129
+ # in_string = "foo,0\nbar,1\nbaz,2"
1130
+ # File.open('t.csv', 'w') do |out_io|
1131
+ # CSV.filter(in_string, out_io) do |row|
1132
+ # row[0].upcase!
1133
+ # row[1] = - row[1].to_i
1134
+ # end
1135
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1136
+ # File.read('t.csv') # => "FOO,0\nBAR,-1\nBAZ,-2\n"
1137
+ #
1138
+ # \IO stream output with headers:
1139
+ #
1140
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1141
+ # File.open('t.csv', 'w') do |out_io|
1142
+ # CSV.filter(in_string, out_io, headers: true) do |row|
1143
+ # row[0].upcase!
1144
+ # row[1] = - row[1].to_i
1145
+ # end
1146
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1147
+ # File.read('t.csv') # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
1148
+ #
1149
+ # When neither +in_string_or_io+ nor +out_string_or_io+ given,
1150
+ # parses from {ARGF}[https://docs.ruby-lang.org/en/master/ARGF.html]
1151
+ # and generates to STDOUT.
1152
+ #
1153
+ # Without headers:
1154
+ #
1155
+ # # Put Ruby code into a file.
1156
+ # ruby = <<-EOT
1157
+ # require 'csv'
1158
+ # CSV.filter do |row|
1159
+ # row[0].upcase!
1160
+ # row[1] = - row[1].to_i
1161
+ # end
1162
+ # EOT
1163
+ # File.write('t.rb', ruby)
1164
+ # # Put some CSV into a file.
1165
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
1166
+ # # Run the Ruby code with CSV filename as argument.
1167
+ # system(Gem.ruby, "t.rb", "t.csv")
1168
+ #
1169
+ # Output (to STDOUT):
1170
+ #
1171
+ # FOO,0
1172
+ # BAR,-1
1173
+ # BAZ,-2
1174
+ #
1175
+ # With headers:
1176
+ #
1177
+ # # Put Ruby code into a file.
1178
+ # ruby = <<-EOT
1179
+ # require 'csv'
1180
+ # CSV.filter(headers: true) do |row|
1181
+ # row[0].upcase!
1182
+ # row[1] = - row[1].to_i
1183
+ # end
1184
+ # EOT
1185
+ # File.write('t.rb', ruby)
1186
+ # # Put some CSV into a file.
1187
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
1188
+ # # Run the Ruby code with CSV filename as argument.
1189
+ # system(Gem.ruby, "t.rb", "t.csv")
1190
+ #
1191
+ # Output (to STDOUT):
1192
+ #
1193
+ # Name,Value
1194
+ # FOO,0
1195
+ # BAR,-1
1196
+ # BAZ,-2
1197
+ #
1198
+ # Arguments:
1199
+ #
1200
+ # * Argument +in_string_or_io+ must be a \String or an \IO stream.
1201
+ # * Argument +out_string_or_io+ must be a \String or an \IO stream.
1202
+ # * Arguments <tt>**options</tt> must be keyword options.
1203
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
1063
1204
  def filter(input=nil, output=nil, **options)
1064
1205
  # parse options for input, output, or both
1065
- in_options, out_options = Hash.new, {row_sep: $INPUT_RECORD_SEPARATOR}
1206
+ in_options, out_options = Hash.new, {row_sep: InputRecordSeparator.value}
1066
1207
  options.each do |key, value|
1067
1208
  case key.to_s
1068
1209
  when /\Ain(?:put)?_(.+)\Z/
@@ -1106,111 +1247,90 @@ class CSV
1106
1247
 
1107
1248
  #
1108
1249
  # :call-seq:
1109
- # foreach(path, mode='r', **options) {|row| ... )
1110
- # foreach(io, mode='r', **options {|row| ... )
1111
- # foreach(path, mode='r', headers: ..., **options) {|row| ... )
1112
- # foreach(io, mode='r', headers: ..., **options {|row| ... )
1113
- # foreach(path, mode='r', **options) -> new_enumerator
1114
- # foreach(io, mode='r', **options -> new_enumerator
1115
- #
1116
- # Calls the block with each row read from source +path+ or +io+.
1117
- #
1118
- # * Argument +path+, if given, must be the path to a file.
1119
- # :include: ../doc/csv/arguments/io.rdoc
1120
- # * Argument +mode+, if given, must be a \File mode
1121
- # See {Open Mode}[IO.html#method-c-new-label-Open+Mode].
1122
- # * Arguments <tt>**options</tt> must be keyword options.
1123
- # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
1124
- # * This method optionally accepts an additional <tt>:encoding</tt> option
1125
- # that you can use to specify the Encoding of the data read from +path+ or +io+.
1126
- # You must provide this unless your data is in the encoding
1127
- # given by <tt>Encoding::default_external</tt>.
1128
- # Parsing will use this to determine how to parse the data.
1129
- # You may provide a second Encoding to
1130
- # have the data transcoded as it is read. For example,
1131
- # encoding: 'UTF-32BE:UTF-8'
1132
- # would read +UTF-32BE+ data from the file
1133
- # but transcode it to +UTF-8+ before parsing.
1250
+ # foreach(path_or_io, mode='r', **options) {|row| ... )
1251
+ # foreach(path_or_io, mode='r', **options) -> new_enumerator
1134
1252
  #
1135
- # ====== Without Option +headers+
1253
+ # Calls the block with each row read from source +path_or_io+.
1136
1254
  #
1137
- # Without option +headers+, returns each row as an \Array object.
1255
+ # \Path input without headers:
1138
1256
  #
1139
- # These examples assume prior execution of:
1140
1257
  # string = "foo,0\nbar,1\nbaz,2\n"
1141
- # path = 't.csv'
1142
- # File.write(path, string)
1258
+ # in_path = 't.csv'
1259
+ # File.write(in_path, string)
1260
+ # CSV.foreach(in_path) {|row| p row }
1143
1261
  #
1144
- # Read rows from a file at +path+:
1145
- # CSV.foreach(path) {|row| p row }
1146
1262
  # Output:
1147
- # ["foo", "0"]
1148
- # ["bar", "1"]
1149
- # ["baz", "2"]
1150
- #
1151
- # Read rows from an \IO object:
1152
- # File.open(path) do |file|
1153
- # CSV.foreach(file) {|row| p row }
1154
- # end
1155
1263
  #
1156
- # Output:
1157
1264
  # ["foo", "0"]
1158
1265
  # ["bar", "1"]
1159
1266
  # ["baz", "2"]
1160
1267
  #
1161
- # Returns a new \Enumerator if no block given:
1162
- # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
1163
- # CSV.foreach(File.open(path)) # => #<Enumerator: CSV:foreach(#<File:t.csv>, "r")>
1268
+ # \Path input with headers:
1269
+ #
1270
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1271
+ # in_path = 't.csv'
1272
+ # File.write(in_path, string)
1273
+ # CSV.foreach(in_path, headers: true) {|row| p row }
1164
1274
  #
1165
- # Issues a warning if an encoding is unsupported:
1166
- # CSV.foreach(File.open(path), encoding: 'foo:bar') {|row| }
1167
1275
  # Output:
1168
- # warning: Unsupported encoding foo ignored
1169
- # warning: Unsupported encoding bar ignored
1170
1276
  #
1171
- # ====== With Option +headers+
1277
+ # <CSV::Row "Name":"foo" "Value":"0">
1278
+ # <CSV::Row "Name":"bar" "Value":"1">
1279
+ # <CSV::Row "Name":"baz" "Value":"2">
1172
1280
  #
1173
- # With {option +headers+}[#class-CSV-label-Option+headers],
1174
- # returns each row as a CSV::Row object.
1281
+ # \IO stream input without headers:
1175
1282
  #
1176
- # These examples assume prior execution of:
1177
- # string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
1283
+ # string = "foo,0\nbar,1\nbaz,2\n"
1178
1284
  # path = 't.csv'
1179
1285
  # File.write(path, string)
1180
- #
1181
- # Read rows from a file at +path+:
1182
- # CSV.foreach(path, headers: true) {|row| p row }
1286
+ # File.open('t.csv') do |in_io|
1287
+ # CSV.foreach(in_io) {|row| p row }
1288
+ # end
1183
1289
  #
1184
1290
  # Output:
1185
- # #<CSV::Row "Name":"foo" "Count":"0">
1186
- # #<CSV::Row "Name":"bar" "Count":"1">
1187
- # #<CSV::Row "Name":"baz" "Count":"2">
1188
1291
  #
1189
- # Read rows from an \IO object:
1190
- # File.open(path) do |file|
1191
- # CSV.foreach(file, headers: true) {|row| p row }
1292
+ # ["foo", "0"]
1293
+ # ["bar", "1"]
1294
+ # ["baz", "2"]
1295
+ #
1296
+ # \IO stream input with headers:
1297
+ #
1298
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1299
+ # path = 't.csv'
1300
+ # File.write(path, string)
1301
+ # File.open('t.csv') do |in_io|
1302
+ # CSV.foreach(in_io, headers: true) {|row| p row }
1192
1303
  # end
1193
1304
  #
1194
1305
  # Output:
1195
- # #<CSV::Row "Name":"foo" "Count":"0">
1196
- # #<CSV::Row "Name":"bar" "Count":"1">
1197
- # #<CSV::Row "Name":"baz" "Count":"2">
1198
- #
1199
- # ---
1200
1306
  #
1201
- # Raises an exception if +path+ is a \String, but not the path to a readable file:
1202
- # # Raises Errno::ENOENT (No such file or directory @ rb_sysopen - nosuch.csv):
1203
- # CSV.foreach('nosuch.csv') {|row| }
1307
+ # <CSV::Row "Name":"foo" "Value":"0">
1308
+ # <CSV::Row "Name":"bar" "Value":"1">
1309
+ # <CSV::Row "Name":"baz" "Value":"2">
1204
1310
  #
1205
- # Raises an exception if +io+ is an \IO object, but not open for reading:
1206
- # io = File.open(path, 'w') {|row| }
1207
- # # Raises TypeError (no implicit conversion of nil into String):
1208
- # CSV.foreach(io) {|row| }
1311
+ # With no block given, returns an \Enumerator:
1209
1312
  #
1210
- # Raises an exception if +mode+ is invalid:
1211
- # # Raises ArgumentError (invalid access mode nosuch):
1212
- # CSV.foreach(path, 'nosuch') {|row| }
1313
+ # string = "foo,0\nbar,1\nbaz,2\n"
1314
+ # path = 't.csv'
1315
+ # File.write(path, string)
1316
+ # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
1213
1317
  #
1318
+ # Arguments:
1319
+ # * Argument +path_or_io+ must be a file path or an \IO stream.
1320
+ # * Argument +mode+, if given, must be a \File mode
1321
+ # See {Open Mode}[https://ruby-doc.org/core/IO.html#method-c-new-label-Open+Mode].
1322
+ # * Arguments <tt>**options</tt> must be keyword options.
1323
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
1324
+ # * This method optionally accepts an additional <tt>:encoding</tt> option
1325
+ # that you can use to specify the Encoding of the data read from +path+ or +io+.
1326
+ # You must provide this unless your data is in the encoding
1327
+ # given by <tt>Encoding::default_external</tt>.
1328
+ # Parsing will use this to determine how to parse the data.
1329
+ # You may provide a second Encoding to
1330
+ # have the data transcoded as it is read. For example,
1331
+ # encoding: 'UTF-32BE:UTF-8'
1332
+ # would read +UTF-32BE+ data from the file
1333
+ # but transcode it to +UTF-8+ before parsing.
1214
1334
  def foreach(path, mode="r", **options, &block)
1215
1335
  return to_enum(__method__, path, mode, **options) unless block_given?
1216
1336
  open(path, mode, **options) do |csv|
@@ -1303,8 +1423,8 @@ class CSV
1303
1423
  # Argument +ary+ must be an \Array.
1304
1424
  #
1305
1425
  # Special options:
1306
- # * Option <tt>:row_sep</tt> defaults to <tt>$INPUT_RECORD_SEPARATOR</tt>
1307
- # (<tt>$/</tt>).:
1426
+ # * Option <tt>:row_sep</tt> defaults to <tt>"\n"> on Ruby 3.0 or later
1427
+ # and <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) otherwise.:
1308
1428
  # $INPUT_RECORD_SEPARATOR # => "\n"
1309
1429
  # * This method accepts an additional option, <tt>:encoding</tt>, which sets the base
1310
1430
  # Encoding for the output. This method will try to guess your Encoding from
@@ -1326,7 +1446,7 @@ class CSV
1326
1446
  # CSV.generate_line(:foo)
1327
1447
  #
1328
1448
  def generate_line(row, **options)
1329
- options = {row_sep: $INPUT_RECORD_SEPARATOR}.merge(options)
1449
+ options = {row_sep: InputRecordSeparator.value}.merge(options)
1330
1450
  str = +""
1331
1451
  if options[:encoding]
1332
1452
  str.force_encoding(options[:encoding])
@@ -1356,7 +1476,7 @@ class CSV
1356
1476
  # open(io, mode = "rb", **options ) { |csv| ... } -> object
1357
1477
  #
1358
1478
  # possible options elements:
1359
- # hash form:
1479
+ # keyword form:
1360
1480
  # :invalid => nil # raise error on invalid byte sequence (default)
1361
1481
  # :invalid => :replace # replace invalid byte sequence
1362
1482
  # :undef => :replace # replace undefined conversion
@@ -1423,10 +1543,14 @@ class CSV
1423
1543
  def open(filename, mode="r", **options)
1424
1544
  # wrap a File opened with the remaining +args+ with no newline
1425
1545
  # decorator
1426
- file_opts = {universal_newline: false}.merge(options)
1546
+ file_opts = options.dup
1547
+ unless file_opts.key?(:newline)
1548
+ file_opts[:universal_newline] ||= false
1549
+ end
1427
1550
  options.delete(:invalid)
1428
1551
  options.delete(:undef)
1429
1552
  options.delete(:replace)
1553
+ options.delete_if {|k, _| /newline\z/.match?(k)}
1430
1554
 
1431
1555
  begin
1432
1556
  f = File.open(filename, mode, **file_opts)
@@ -1681,7 +1805,7 @@ class CSV
1681
1805
  #
1682
1806
  # Calls CSV.read with +source+, +options+, and certain default options:
1683
1807
  # - +headers+: +true+
1684
- # - +converbers+: +:numeric+
1808
+ # - +converters+: +:numeric+
1685
1809
  # - +header_converters+: +:symbol+
1686
1810
  #
1687
1811
  # Returns a CSV::Table object.
@@ -1745,6 +1869,7 @@ class CSV
1745
1869
  row_sep: :auto,
1746
1870
  quote_char: '"',
1747
1871
  field_size_limit: nil,
1872
+ max_field_size: nil,
1748
1873
  converters: nil,
1749
1874
  unconverted_fields: nil,
1750
1875
  headers: false,
@@ -1760,11 +1885,11 @@ class CSV
1760
1885
  encoding: nil,
1761
1886
  nil_value: nil,
1762
1887
  empty_value: "",
1888
+ strip: false,
1763
1889
  quote_empty: true,
1764
1890
  write_converters: nil,
1765
1891
  write_nil_value: nil,
1766
- write_empty_value: "",
1767
- strip: false)
1892
+ write_empty_value: "")
1768
1893
  raise ArgumentError.new("Cannot parse nil as CSV") if data.nil?
1769
1894
 
1770
1895
  if data.is_a?(String)
@@ -1787,11 +1912,14 @@ class CSV
1787
1912
  @initial_header_converters = header_converters
1788
1913
  @initial_write_converters = write_converters
1789
1914
 
1915
+ if max_field_size.nil? and field_size_limit
1916
+ max_field_size = field_size_limit - 1
1917
+ end
1790
1918
  @parser_options = {
1791
1919
  column_separator: col_sep,
1792
1920
  row_separator: row_sep,
1793
1921
  quote_character: quote_char,
1794
- field_size_limit: field_size_limit,
1922
+ max_field_size: max_field_size,
1795
1923
  unconverted_fields: unconverted_fields,
1796
1924
  headers: headers,
1797
1925
  return_headers: return_headers,
@@ -1859,10 +1987,24 @@ class CSV
1859
1987
  # Returns the limit for field size; used for parsing;
1860
1988
  # see {Option +field_size_limit+}[#class-CSV-label-Option+field_size_limit]:
1861
1989
  # CSV.new('').field_size_limit # => nil
1990
+ #
1991
+ # Deprecated since 3.2.3. Use +max_field_size+ instead.
1862
1992
  def field_size_limit
1863
1993
  parser.field_size_limit
1864
1994
  end
1865
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
+
1866
2008
  # :call-seq:
1867
2009
  # csv.skip_lines -> regexp or nil
1868
2010
  #
@@ -1884,6 +2026,10 @@ class CSV
1884
2026
  # csv.converters # => [:integer]
1885
2027
  # csv.convert(proc {|x| x.to_s })
1886
2028
  # csv.converters
2029
+ #
2030
+ # Notes that you need to call
2031
+ # +Ractor.make_shareable(CSV::Converters)+ on the main Ractor to use
2032
+ # this method.
1887
2033
  def converters
1888
2034
  parser_fields_converter.map do |converter|
1889
2035
  name = Converters.rassoc(converter)
@@ -1946,6 +2092,10 @@ class CSV
1946
2092
  # Returns an \Array containing header converters; used for parsing;
1947
2093
  # see {Header Converters}[#class-CSV-label-Header+Converters]:
1948
2094
  # CSV.new('').header_converters # => []
2095
+ #
2096
+ # Notes that you need to call
2097
+ # +Ractor.make_shareable(CSV::HeaderConverters)+ on the main Ractor
2098
+ # to use this method.
1949
2099
  def header_converters
1950
2100
  header_fields_converter.map do |converter|
1951
2101
  name = HeaderConverters.rassoc(converter)
@@ -1985,7 +2135,7 @@ class CSV
1985
2135
  end
1986
2136
 
1987
2137
  # :call-seq:
1988
- # csv.encoding -> endcoding
2138
+ # csv.encoding -> encoding
1989
2139
  #
1990
2140
  # Returns the encoding used for parsing and generating;
1991
2141
  # see {Character Encodings (M17n or Multilingualization)}[#class-CSV-label-Character+Encodings+-28M17n+or+Multilingualization-29]:
@@ -2586,7 +2736,7 @@ class CSV
2586
2736
 
2587
2737
  def build_parser_fields_converter
2588
2738
  specific_options = {
2589
- builtin_converters: Converters,
2739
+ builtin_converters_name: :Converters,
2590
2740
  }
2591
2741
  options = @base_fields_converter_options.merge(specific_options)
2592
2742
  build_fields_converter(@initial_converters, options)
@@ -2598,7 +2748,7 @@ class CSV
2598
2748
 
2599
2749
  def build_header_fields_converter
2600
2750
  specific_options = {
2601
- builtin_converters: HeaderConverters,
2751
+ builtin_converters_name: :HeaderConverters,
2602
2752
  accept_nil: true,
2603
2753
  }
2604
2754
  options = @base_fields_converter_options.merge(specific_options)
@@ -2661,8 +2811,15 @@ end
2661
2811
  # c.read.any? { |a| a.include?("zombies") }
2662
2812
  # } #=> false
2663
2813
  #
2664
- def CSV(*args, &block)
2665
- CSV.instance(*args, &block)
2814
+ # CSV options may also be given.
2815
+ #
2816
+ # io = StringIO.new
2817
+ # CSV(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
2818
+ #
2819
+ # This API is not Ractor-safe.
2820
+ #
2821
+ def CSV(*args, **options, &block)
2822
+ CSV.instance(*args, **options, &block)
2666
2823
  end
2667
2824
 
2668
2825
  require_relative "csv/version"