csv 3.1.9 → 3.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44985560f2528c6909f94ce869ec8ddb014ee66d89fe940fe6f4c872f8773e97
4
- data.tar.gz: 60297accbe6a82852d251f5e4990dfacc241d6ec1084a39eb7ce39ac04c059d6
3
+ metadata.gz: c48c0d15454e002ff10270a9c56cf4311ce635a8a9dfb527f7a7541f29f801b2
4
+ data.tar.gz: 505d1d0dbb4cff0a544b2e00925cb1101ed71642a584d534f443405fba8bd820
5
5
  SHA512:
6
- metadata.gz: 7d51eae6f0e4eaccf382ffc52306ff9b9235259353c8514aafc3fd0116449ae342d9b5972e6ab236ddad542a9a286580f060080503fb18a2f81dfc97299cb8e8
7
- data.tar.gz: ef7d5e134f0ed59e4e0e466c5a6c4a6e45d51ff90a8e3bc22be1014041d29cb3a70c6ae7b482865426d5957962007875b734364b43742b2b7074548d3263bc76
6
+ metadata.gz: 1c9ecd18d5b9a4f663c0676694ffc133a4657e2f7a07cafe2f0a5d9ddd2d7846f505bc62c21698fcf1117126efc6978b7aa1b497d2fef8d532a8a4246c58bff2
7
+ data.tar.gz: e4fe05b49f92c68c011060d1dcd39ead1785d886eabbd3689a12884df9eb30124694417314e39bcf656cd05d6d0dea6a80a701dd5fe6cac42efc33c67be54926
data/NEWS.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # News
2
2
 
3
+ ## 3.2.0 - 2021-06-06
4
+
5
+ ### Improvements
6
+
7
+ * `CSV.open`: Added support for `:newline` option.
8
+ [GitHub#198][Patch by Nobuyoshi Nakada]
9
+
10
+ * `CSV::Table#each`: Added support for column mode with duplicated
11
+ headers.
12
+ [GitHub#206][Reported by Yaroslav Berezovskiy]
13
+
14
+ * `Object#CSV`: Added support for Ruby 3.0.
15
+
16
+ * `CSV::Row`: Added support for pattern matching.
17
+ [GitHub#207][Patch by Kevin Newton]
18
+
19
+ ### Fixes
20
+
21
+ * Fixed typos in documentation.
22
+ [GitHub#196][GitHub#205][Patch by Sampat Badhe]
23
+
24
+ ### Thanks
25
+
26
+ * Sampat Badhe
27
+
28
+ * Nobuyoshi Nakada
29
+
30
+ * Yaroslav Berezovskiy
31
+
32
+ * Kevin Newton
33
+
3
34
  ## 3.1.9 - 2020-11-23
4
35
 
5
36
  ### Fixes
data/README.md CHANGED
@@ -1,8 +1,5 @@
1
1
  # CSV
2
2
 
3
- [![Build Status](https://travis-ci.org/ruby/csv.svg?branch=master)](https://travis-ci.org/ruby/csv)
4
- [![Test Coverage](https://api.codeclimate.com/v1/badges/321fa39e510a0abd0369/test_coverage)](https://codeclimate.com/github/ruby/csv/test_coverage)
5
-
6
3
  This library provides a complete interface to CSV files and data. It offers tools to enable you to read and write to and from Strings or IO objects, as needed.
7
4
 
8
5
  ## Installation
@@ -33,8 +30,8 @@ end
33
30
 
34
31
  ## Documentation
35
32
 
36
- - {API}[CSV.html]: all classes, methods, and constants.
37
- - {Recipes}[doc/csv/recipes/recipes_rdoc.html]: specific code for specific tasks.
33
+ - [API](https://ruby-doc.org/stdlib/libdoc/csv/rdoc/CSV.html): all classes, methods, and constants.
34
+ - [Recipes](https://ruby-doc.org/core/doc/csv/recipes/recipes_rdoc.html): specific code for specific tasks.
38
35
 
39
36
  ## Development
40
37
 
@@ -431,7 +431,7 @@ You can use multiple field converters in either of these ways:
431
431
 
432
432
  ===== Recipe: Specify Multiple Field Converters in Option +:converters+
433
433
 
434
- Apply multiple field converters by specifying them in option +:conveters+:
434
+ Apply multiple field converters by specifying them in option +:converters+:
435
435
  source = "Name,Value\nfoo,0\nbar,1.0\nbaz,2.0\n"
436
436
  parsed = CSV.parse(source, headers: true, converters: [:integer, :float])
437
437
  parsed['Value'] # => [0, 1.0, 2.0]
@@ -500,7 +500,7 @@ You can use multiple header converters in either of these ways:
500
500
 
501
501
  ===== Recipe: Specify Multiple Header Converters in Option :header_converters
502
502
 
503
- Apply multiple header converters by specifying them in option +:header_conveters+:
503
+ Apply multiple header converters by specifying them in option +:header_converters+:
504
504
  source = "Name,Value\nfoo,0\nbar,1.0\nbaz,2.0\n"
505
505
  parsed = CSV.parse(source, headers: true, header_converters: [:downcase, :symbol])
506
506
  parsed.headers # => [:name, :value]
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().
@@ -705,7 +705,7 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
705
705
  # Header converters operate only on headers (and not on other rows).
706
706
  #
707
707
  # There are three ways to use header \converters;
708
- # these examples use built-in header converter +:dowhcase+,
708
+ # these examples use built-in header converter +:downcase+,
709
709
  # which downcases each parsed header.
710
710
  #
711
711
  # - Option +header_converters+ with a singleton parsing method:
@@ -1006,60 +1006,185 @@ class CSV
1006
1006
  end
1007
1007
 
1008
1008
  # :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.
1009
+ # filter(in_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
1010
+ # filter(in_string_or_io, out_string_or_io, **options) {|row| ... } -> array_of_arrays or csv_table
1011
+ # filter(**options) {|row| ... } -> array_of_arrays or csv_table
1025
1012
  #
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].
1013
+ # - Parses \CSV from a source (\String, \IO stream, or ARGF).
1014
+ # - Calls the given block with each parsed row:
1015
+ # - Without headers, each row is an \Array.
1016
+ # - With headers, each row is a CSV::Row.
1017
+ # - Generates \CSV to an output (\String, \IO stream, or STDOUT).
1018
+ # - Returns the parsed source:
1019
+ # - Without headers, an \Array of \Arrays.
1020
+ # - With headers, a CSV::Table.
1054
1021
  #
1055
- # Example:
1056
- # in_string = "foo,0\nbar,1\nbaz,2\n"
1022
+ # When +in_string_or_io+ is given, but not +out_string_or_io+,
1023
+ # parses from the given +in_string_or_io+
1024
+ # and generates to STDOUT.
1025
+ #
1026
+ # \String input without headers:
1027
+ #
1028
+ # in_string = "foo,0\nbar,1\nbaz,2"
1029
+ # CSV.filter(in_string) do |row|
1030
+ # row[0].upcase!
1031
+ # row[1] = - row[1].to_i
1032
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1033
+ #
1034
+ # Output (to STDOUT):
1035
+ #
1036
+ # FOO,0
1037
+ # BAR,-1
1038
+ # BAZ,-2
1039
+ #
1040
+ # \String input with headers:
1041
+ #
1042
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1043
+ # CSV.filter(in_string, headers: true) do |row|
1044
+ # row[0].upcase!
1045
+ # row[1] = - row[1].to_i
1046
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1047
+ #
1048
+ # Output (to STDOUT):
1049
+ #
1050
+ # Name,Value
1051
+ # FOO,0
1052
+ # BAR,-1
1053
+ # BAZ,-2
1054
+ #
1055
+ # \IO stream input without headers:
1056
+ #
1057
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
1058
+ # File.open('t.csv') do |in_io|
1059
+ # CSV.filter(in_io) do |row|
1060
+ # row[0].upcase!
1061
+ # row[1] = - row[1].to_i
1062
+ # end
1063
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1064
+ #
1065
+ # Output (to STDOUT):
1066
+ #
1067
+ # FOO,0
1068
+ # BAR,-1
1069
+ # BAZ,-2
1070
+ #
1071
+ # \IO stream input with headers:
1072
+ #
1073
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
1074
+ # File.open('t.csv') do |in_io|
1075
+ # CSV.filter(in_io, headers: true) do |row|
1076
+ # row[0].upcase!
1077
+ # row[1] = - row[1].to_i
1078
+ # end
1079
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1080
+ #
1081
+ # Output (to STDOUT):
1082
+ #
1083
+ # Name,Value
1084
+ # FOO,0
1085
+ # BAR,-1
1086
+ # BAZ,-2
1087
+ #
1088
+ # When both +in_string_or_io+ and +out_string_or_io+ are given,
1089
+ # parses from +in_string_or_io+ and generates to +out_string_or_io+.
1090
+ #
1091
+ # \String output without headers:
1092
+ #
1093
+ # in_string = "foo,0\nbar,1\nbaz,2"
1057
1094
  # out_string = ''
1058
1095
  # 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"
1096
+ # row[0].upcase!
1097
+ # row[1] = - row[1].to_i
1098
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1099
+ # out_string # => "FOO,0\nBAR,-1\nBAZ,-2\n"
1100
+ #
1101
+ # \String output with headers:
1102
+ #
1103
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1104
+ # out_string = ''
1105
+ # CSV.filter(in_string, out_string, headers: true) do |row|
1106
+ # row[0].upcase!
1107
+ # row[1] = - row[1].to_i
1108
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1109
+ # out_string # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
1110
+ #
1111
+ # \IO stream output without headers:
1112
+ #
1113
+ # in_string = "foo,0\nbar,1\nbaz,2"
1114
+ # File.open('t.csv', 'w') do |out_io|
1115
+ # CSV.filter(in_string, out_io) do |row|
1116
+ # row[0].upcase!
1117
+ # row[1] = - row[1].to_i
1118
+ # end
1119
+ # end # => [["FOO", 0], ["BAR", -1], ["BAZ", -2]]
1120
+ # File.read('t.csv') # => "FOO,0\nBAR,-1\nBAZ,-2\n"
1121
+ #
1122
+ # \IO stream output with headers:
1123
+ #
1124
+ # in_string = "Name,Value\nfoo,0\nbar,1\nbaz,2"
1125
+ # File.open('t.csv', 'w') do |out_io|
1126
+ # CSV.filter(in_string, out_io, headers: true) do |row|
1127
+ # row[0].upcase!
1128
+ # row[1] = - row[1].to_i
1129
+ # end
1130
+ # end # => #<CSV::Table mode:col_or_row row_count:4>
1131
+ # File.read('t.csv') # => "Name,Value\nFOO,0\nBAR,-1\nBAZ,-2\n"
1132
+ #
1133
+ # When neither +in_string_or_io+ nor +out_string_or_io+ given,
1134
+ # parses from {ARGF}[https://docs.ruby-lang.org/en/master/ARGF.html]
1135
+ # and generates to STDOUT.
1136
+ #
1137
+ # Without headers:
1138
+ #
1139
+ # # Put Ruby code into a file.
1140
+ # ruby = <<-EOT
1141
+ # require 'csv'
1142
+ # CSV.filter do |row|
1143
+ # row[0].upcase!
1144
+ # row[1] = - row[1].to_i
1145
+ # end
1146
+ # EOT
1147
+ # File.write('t.rb', ruby)
1148
+ # # Put some CSV into a file.
1149
+ # File.write('t.csv', "foo,0\nbar,1\nbaz,2")
1150
+ # # Run the Ruby code with CSV filename as argument.
1151
+ # system(Gem.ruby, "t.rb", "t.csv")
1152
+ #
1153
+ # Output (to STDOUT):
1154
+ #
1155
+ # FOO,0
1156
+ # BAR,-1
1157
+ # BAZ,-2
1158
+ #
1159
+ # With headers:
1160
+ #
1161
+ # # Put Ruby code into a file.
1162
+ # ruby = <<-EOT
1163
+ # require 'csv'
1164
+ # CSV.filter(headers: true) do |row|
1165
+ # row[0].upcase!
1166
+ # row[1] = - row[1].to_i
1167
+ # end
1168
+ # EOT
1169
+ # File.write('t.rb', ruby)
1170
+ # # Put some CSV into a file.
1171
+ # File.write('t.csv', "Name,Value\nfoo,0\nbar,1\nbaz,2")
1172
+ # # Run the Ruby code with CSV filename as argument.
1173
+ # system(Gem.ruby, "t.rb", "t.csv")
1174
+ #
1175
+ # Output (to STDOUT):
1176
+ #
1177
+ # Name,Value
1178
+ # FOO,0
1179
+ # BAR,-1
1180
+ # BAZ,-2
1181
+ #
1182
+ # Arguments:
1183
+ #
1184
+ # * Argument +in_string_or_io+ must be a \String or an \IO stream.
1185
+ # * Argument +out_string_or_io+ must be a \String or an \IO stream.
1186
+ # * Arguments <tt>**options</tt> must be keyword options.
1187
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
1063
1188
  def filter(input=nil, output=nil, **options)
1064
1189
  # parse options for input, output, or both
1065
1190
  in_options, out_options = Hash.new, {row_sep: $INPUT_RECORD_SEPARATOR}
@@ -1106,111 +1231,90 @@ class CSV
1106
1231
 
1107
1232
  #
1108
1233
  # :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
1234
+ # foreach(path_or_io, mode='r', **options) {|row| ... )
1235
+ # foreach(path_or_io, mode='r', **options) -> new_enumerator
1115
1236
  #
1116
- # Calls the block with each row read from source +path+ or +io+.
1237
+ # Calls the block with each row read from source +path_or_io+.
1117
1238
  #
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.
1134
- #
1135
- # ====== Without Option +headers+
1239
+ # \Path input without headers:
1136
1240
  #
1137
- # Without option +headers+, returns each row as an \Array object.
1138
- #
1139
- # These examples assume prior execution of:
1140
1241
  # string = "foo,0\nbar,1\nbaz,2\n"
1141
- # path = 't.csv'
1142
- # File.write(path, string)
1242
+ # in_path = 't.csv'
1243
+ # File.write(in_path, string)
1244
+ # CSV.foreach(in_path) {|row| p row }
1143
1245
  #
1144
- # Read rows from a file at +path+:
1145
- # CSV.foreach(path) {|row| p row }
1146
1246
  # Output:
1147
- # ["foo", "0"]
1148
- # ["bar", "1"]
1149
- # ["baz", "2"]
1150
1247
  #
1151
- # Read rows from an \IO object:
1152
- # File.open(path) do |file|
1153
- # CSV.foreach(file) {|row| p row }
1154
- # end
1155
- #
1156
- # Output:
1157
1248
  # ["foo", "0"]
1158
1249
  # ["bar", "1"]
1159
1250
  # ["baz", "2"]
1160
1251
  #
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")>
1252
+ # \Path input with headers:
1253
+ #
1254
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1255
+ # in_path = 't.csv'
1256
+ # File.write(in_path, string)
1257
+ # CSV.foreach(in_path, headers: true) {|row| p row }
1164
1258
  #
1165
- # Issues a warning if an encoding is unsupported:
1166
- # CSV.foreach(File.open(path), encoding: 'foo:bar') {|row| }
1167
1259
  # Output:
1168
- # warning: Unsupported encoding foo ignored
1169
- # warning: Unsupported encoding bar ignored
1170
1260
  #
1171
- # ====== With Option +headers+
1261
+ # <CSV::Row "Name":"foo" "Value":"0">
1262
+ # <CSV::Row "Name":"bar" "Value":"1">
1263
+ # <CSV::Row "Name":"baz" "Value":"2">
1172
1264
  #
1173
- # With {option +headers+}[#class-CSV-label-Option+headers],
1174
- # returns each row as a CSV::Row object.
1265
+ # \IO stream input without headers:
1175
1266
  #
1176
- # These examples assume prior execution of:
1177
- # string = "Name,Count\nfoo,0\nbar,1\nbaz,2\n"
1267
+ # string = "foo,0\nbar,1\nbaz,2\n"
1178
1268
  # path = 't.csv'
1179
1269
  # File.write(path, string)
1180
- #
1181
- # Read rows from a file at +path+:
1182
- # CSV.foreach(path, headers: true) {|row| p row }
1270
+ # File.open('t.csv') do |in_io|
1271
+ # CSV.foreach(in_io) {|row| p row }
1272
+ # end
1183
1273
  #
1184
1274
  # Output:
1185
- # #<CSV::Row "Name":"foo" "Count":"0">
1186
- # #<CSV::Row "Name":"bar" "Count":"1">
1187
- # #<CSV::Row "Name":"baz" "Count":"2">
1188
1275
  #
1189
- # Read rows from an \IO object:
1190
- # File.open(path) do |file|
1191
- # CSV.foreach(file, headers: true) {|row| p row }
1276
+ # ["foo", "0"]
1277
+ # ["bar", "1"]
1278
+ # ["baz", "2"]
1279
+ #
1280
+ # \IO stream input with headers:
1281
+ #
1282
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
1283
+ # path = 't.csv'
1284
+ # File.write(path, string)
1285
+ # File.open('t.csv') do |in_io|
1286
+ # CSV.foreach(in_io, headers: true) {|row| p row }
1192
1287
  # end
1193
1288
  #
1194
1289
  # Output:
1195
- # #<CSV::Row "Name":"foo" "Count":"0">
1196
- # #<CSV::Row "Name":"bar" "Count":"1">
1197
- # #<CSV::Row "Name":"baz" "Count":"2">
1198
1290
  #
1199
- # ---
1291
+ # <CSV::Row "Name":"foo" "Value":"0">
1292
+ # <CSV::Row "Name":"bar" "Value":"1">
1293
+ # <CSV::Row "Name":"baz" "Value":"2">
1200
1294
  #
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| }
1295
+ # With no block given, returns an \Enumerator:
1204
1296
  #
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| }
1209
- #
1210
- # Raises an exception if +mode+ is invalid:
1211
- # # Raises ArgumentError (invalid access mode nosuch):
1212
- # CSV.foreach(path, 'nosuch') {|row| }
1297
+ # string = "foo,0\nbar,1\nbaz,2\n"
1298
+ # path = 't.csv'
1299
+ # File.write(path, string)
1300
+ # CSV.foreach(path) # => #<Enumerator: CSV:foreach("t.csv", "r")>
1213
1301
  #
1302
+ # Arguments:
1303
+ # * Argument +path_or_io+ must be a file path or an \IO stream.
1304
+ # * Argument +mode+, if given, must be a \File mode
1305
+ # See {Open Mode}[https://ruby-doc.org/core/IO.html#method-c-new-label-Open+Mode].
1306
+ # * Arguments <tt>**options</tt> must be keyword options.
1307
+ # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
1308
+ # * This method optionally accepts an additional <tt>:encoding</tt> option
1309
+ # that you can use to specify the Encoding of the data read from +path+ or +io+.
1310
+ # You must provide this unless your data is in the encoding
1311
+ # given by <tt>Encoding::default_external</tt>.
1312
+ # Parsing will use this to determine how to parse the data.
1313
+ # You may provide a second Encoding to
1314
+ # have the data transcoded as it is read. For example,
1315
+ # encoding: 'UTF-32BE:UTF-8'
1316
+ # would read +UTF-32BE+ data from the file
1317
+ # but transcode it to +UTF-8+ before parsing.
1214
1318
  def foreach(path, mode="r", **options, &block)
1215
1319
  return to_enum(__method__, path, mode, **options) unless block_given?
1216
1320
  open(path, mode, **options) do |csv|
@@ -1356,7 +1460,7 @@ class CSV
1356
1460
  # open(io, mode = "rb", **options ) { |csv| ... } -> object
1357
1461
  #
1358
1462
  # possible options elements:
1359
- # hash form:
1463
+ # keyword form:
1360
1464
  # :invalid => nil # raise error on invalid byte sequence (default)
1361
1465
  # :invalid => :replace # replace invalid byte sequence
1362
1466
  # :undef => :replace # replace undefined conversion
@@ -1423,10 +1527,14 @@ class CSV
1423
1527
  def open(filename, mode="r", **options)
1424
1528
  # wrap a File opened with the remaining +args+ with no newline
1425
1529
  # decorator
1426
- file_opts = {universal_newline: false}.merge(options)
1530
+ file_opts = options.dup
1531
+ unless file_opts.key?(:newline)
1532
+ file_opts[:universal_newline] ||= false
1533
+ end
1427
1534
  options.delete(:invalid)
1428
1535
  options.delete(:undef)
1429
1536
  options.delete(:replace)
1537
+ options.delete_if {|k, _| /newline\z/.match?(k)}
1430
1538
 
1431
1539
  begin
1432
1540
  f = File.open(filename, mode, **file_opts)
@@ -1681,7 +1789,7 @@ class CSV
1681
1789
  #
1682
1790
  # Calls CSV.read with +source+, +options+, and certain default options:
1683
1791
  # - +headers+: +true+
1684
- # - +converbers+: +:numeric+
1792
+ # - +converters+: +:numeric+
1685
1793
  # - +header_converters+: +:symbol+
1686
1794
  #
1687
1795
  # Returns a CSV::Table object.
@@ -1985,7 +2093,7 @@ class CSV
1985
2093
  end
1986
2094
 
1987
2095
  # :call-seq:
1988
- # csv.encoding -> endcoding
2096
+ # csv.encoding -> encoding
1989
2097
  #
1990
2098
  # Returns the encoding used for parsing and generating;
1991
2099
  # see {Character Encodings (M17n or Multilingualization)}[#class-CSV-label-Character+Encodings+-28M17n+or+Multilingualization-29]:
@@ -2661,8 +2769,13 @@ end
2661
2769
  # c.read.any? { |a| a.include?("zombies") }
2662
2770
  # } #=> false
2663
2771
  #
2664
- def CSV(*args, &block)
2665
- CSV.instance(*args, &block)
2772
+ # CSV options may also be given.
2773
+ #
2774
+ # io = StringIO.new
2775
+ # CSV(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
2776
+ #
2777
+ def CSV(*args, **options, &block)
2778
+ CSV.instance(*args, **options, &block)
2666
2779
  end
2667
2780
 
2668
2781
  require_relative "csv/version"
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
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.0"
6
6
  end
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.1.9
4
+ version: 3.2.0
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: 2020-11-22 00:00:00.000000000 Z
12
+ date: 2021-06-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -54,19 +54,19 @@ dependencies:
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
- name: simplecov
57
+ name: test-unit
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - ">="
61
61
  - !ruby/object:Gem::Version
62
- version: '0'
62
+ version: 3.4.3
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: '0'
69
+ version: 3.4.3
70
70
  description: The CSV library provides a complete interface to CSV files and data.
71
71
  It offers tools to enable you to read and write to and from Strings or IO objects,
72
72
  as needed.
@@ -146,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
146
  - !ruby/object:Gem::Version
147
147
  version: '0'
148
148
  requirements: []
149
- rubygems_version: 3.2.0.rc.2
149
+ rubygems_version: 3.3.0.dev
150
150
  signing_key:
151
151
  specification_version: 4
152
152
  summary: CSV Reading and Writing