csv 3.1.5 → 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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +110 -0
  3. data/README.md +5 -3
  4. data/doc/csv/arguments/io.rdoc +5 -0
  5. data/doc/csv/options/common/col_sep.rdoc +57 -0
  6. data/doc/csv/options/common/quote_char.rdoc +42 -0
  7. data/doc/{row_sep.rdoc → csv/options/common/row_sep.rdoc} +14 -14
  8. data/doc/{force_quotes.rdoc → csv/options/generating/force_quotes.rdoc} +0 -0
  9. data/doc/{quote_empty.rdoc → csv/options/generating/quote_empty.rdoc} +0 -0
  10. data/doc/{write_converters.rdoc → csv/options/generating/write_converters.rdoc} +6 -12
  11. data/doc/{write_empty_value.rdoc → csv/options/generating/write_empty_value.rdoc} +0 -0
  12. data/doc/{write_headers.rdoc → csv/options/generating/write_headers.rdoc} +0 -0
  13. data/doc/{write_nil_value.rdoc → csv/options/generating/write_nil_value.rdoc} +1 -1
  14. data/doc/csv/options/parsing/converters.rdoc +46 -0
  15. data/doc/{empty_value.rdoc → csv/options/parsing/empty_value.rdoc} +0 -0
  16. data/doc/{field_size_limit.rdoc → csv/options/parsing/field_size_limit.rdoc} +0 -0
  17. data/doc/csv/options/parsing/header_converters.rdoc +43 -0
  18. data/doc/{headers.rdoc → csv/options/parsing/headers.rdoc} +0 -0
  19. data/doc/{liberal_parsing.rdoc → csv/options/parsing/liberal_parsing.rdoc} +0 -0
  20. data/doc/{nil_value.rdoc → csv/options/parsing/nil_value.rdoc} +0 -0
  21. data/doc/{return_headers.rdoc → csv/options/parsing/return_headers.rdoc} +0 -0
  22. data/doc/{skip_blanks.rdoc → csv/options/parsing/skip_blanks.rdoc} +0 -0
  23. data/doc/{skip_lines.rdoc → csv/options/parsing/skip_lines.rdoc} +0 -0
  24. data/doc/{strip.rdoc → csv/options/parsing/strip.rdoc} +0 -0
  25. data/doc/{unconverted_fields.rdoc → csv/options/parsing/unconverted_fields.rdoc} +0 -0
  26. data/doc/csv/recipes/filtering.rdoc +158 -0
  27. data/doc/csv/recipes/generating.rdoc +298 -0
  28. data/doc/csv/recipes/parsing.rdoc +545 -0
  29. data/doc/csv/recipes/recipes.rdoc +6 -0
  30. data/lib/csv.rb +1604 -515
  31. data/lib/csv/parser.rb +1 -0
  32. data/lib/csv/row.rb +499 -132
  33. data/lib/csv/table.rb +753 -109
  34. data/lib/csv/version.rb +1 -1
  35. data/lib/csv/writer.rb +45 -4
  36. metadata +38 -28
  37. data/doc/col_sep.rdoc +0 -45
  38. data/doc/converters.rdoc +0 -45
  39. data/doc/header_converters.rdoc +0 -31
  40. data/doc/quote_char.rdoc +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eec352a246dc1189213c78e35541e9903f172889792b92a23d6b8d7215d9ae37
4
- data.tar.gz: 63754025784b9d6d26686b48efe8627b18c1aaa26e14f1261d14a09a4921612e
3
+ metadata.gz: c48c0d15454e002ff10270a9c56cf4311ce635a8a9dfb527f7a7541f29f801b2
4
+ data.tar.gz: 505d1d0dbb4cff0a544b2e00925cb1101ed71642a584d534f443405fba8bd820
5
5
  SHA512:
6
- metadata.gz: c1b9895d4735d3f7a1376765d8e71a03388d4b8f52ace5a8eab3126e1f3167f77fcfd312212fdbdbda496d4a2f837a966bf33b871bee96f633c536b566ce0a9f
7
- data.tar.gz: 9347baa988315b544d41ec23ded72a812dec09b7c0832c72bc4ff3be950e25d0f2184f9ed43967113ddb63e56e29c847bffb469b7883ad4207b913368057b58c
6
+ metadata.gz: 1c9ecd18d5b9a4f663c0676694ffc133a4657e2f7a07cafe2f0a5d9ddd2d7846f505bc62c21698fcf1117126efc6978b7aa1b497d2fef8d532a8a4246c58bff2
7
+ data.tar.gz: e4fe05b49f92c68c011060d1dcd39ead1785d886eabbd3689a12884df9eb30124694417314e39bcf656cd05d6d0dea6a80a701dd5fe6cac42efc33c67be54926
data/NEWS.md CHANGED
@@ -1,5 +1,115 @@
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
+
34
+ ## 3.1.9 - 2020-11-23
35
+
36
+ ### Fixes
37
+
38
+ * Fixed a compatibility bug that the line to be processed by
39
+ `skip_lines:` has a row separator.
40
+ [GitHub#194][Reported by Josef Šimánek]
41
+
42
+ ### Thanks
43
+
44
+ * Josef Šimánek
45
+
46
+ ## 3.1.8 - 2020-11-18
47
+
48
+ ### Improvements
49
+
50
+ * Improved documentation.
51
+ [Patch by Burdette Lamar]
52
+
53
+ ### Thanks
54
+
55
+ * Burdette Lamar
56
+
57
+ ## 3.1.7 - 2020-08-04
58
+
59
+ ### Improvements
60
+
61
+ * Improved document.
62
+ [GitHub#158][GitHub#160][GitHub#161]
63
+ [Patch by Burdette Lamar]
64
+
65
+ * Updated required Ruby version to 2.5.0 or later.
66
+ [GitHub#159]
67
+ [Patch by Gabriel Nagy]
68
+
69
+ * Removed stringio 0.1.3 or later dependency.
70
+
71
+ ### Thanks
72
+
73
+ * Burdette Lamar
74
+
75
+ * Gabriel Nagy
76
+
77
+ ## 3.1.6 - 2020-07-20
78
+
79
+ ### Improvements
80
+
81
+ * Improved document.
82
+ [GitHub#127][GitHub#135][GitHub#136][GitHub#137][GitHub#139][GitHub#140]
83
+ [GitHub#141][GitHub#142][GitHub#143][GitHub#145][GitHub#146][GitHub#148]
84
+ [GitHub#148][GitHub#151][GitHub#152][GitHub#154][GitHub#155][GitHub#157]
85
+ [Patch by Burdette Lamar]
86
+
87
+ * `CSV.open`: Added support for `undef: :replace`.
88
+ [GitHub#129][Patch by Koichi ITO]
89
+
90
+ * `CSV.open`: Added support for `invalid: :replace`.
91
+ [GitHub#129][Patch by Koichi ITO]
92
+
93
+ * Don't run quotable check for invalid encoding field values.
94
+ [GitHub#131][Patch by Koichi ITO]
95
+
96
+ * Added support for specifying the target indexes and names to
97
+ `force_quotes:`.
98
+ [GitHub#153][Reported by Aleksandr]
99
+
100
+ * `CSV.generate`: Changed to use the encoding of the first non-ASCII
101
+ field rather than the encoding of ASCII only field.
102
+
103
+ * Changed to require the stringio gem 0.1.3 or later.
104
+
105
+ ### Thanks
106
+
107
+ * Burdette Lamar
108
+
109
+ * Koichi ITO
110
+
111
+ * Aleksandr
112
+
3
113
  ## 3.1.5 - 2020-05-18
4
114
 
5
115
  ### Improvements
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
@@ -31,6 +28,11 @@ CSV.foreach("path/to/file.csv") do |row|
31
28
  end
32
29
  ```
33
30
 
31
+ ## Documentation
32
+
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.
35
+
34
36
  ## Development
35
37
 
36
38
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,5 @@
1
+ * Argument +io+ should be an IO object that is:
2
+ * Open for reading; on return, the IO object will be closed.
3
+ * Positioned at the beginning.
4
+ To position at the end, for appending, use method CSV.generate.
5
+ For any other positioning, pass a preset \StringIO object instead.
@@ -0,0 +1,57 @@
1
+ ====== Option +col_sep+
2
+
3
+ Specifies the \String column separator to be used
4
+ for both parsing and generating.
5
+ The \String will be transcoded into the data's \Encoding before use.
6
+
7
+ Default value:
8
+ CSV::DEFAULT_OPTIONS.fetch(:col_sep) # => "," (comma)
9
+
10
+ Using the default (comma):
11
+ str = CSV.generate do |csv|
12
+ csv << [:foo, 0]
13
+ csv << [:bar, 1]
14
+ csv << [:baz, 2]
15
+ end
16
+ str # => "foo,0\nbar,1\nbaz,2\n"
17
+ ary = CSV.parse(str)
18
+ ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
19
+
20
+ Using +:+ (colon):
21
+ col_sep = ':'
22
+ str = CSV.generate(col_sep: col_sep) do |csv|
23
+ csv << [:foo, 0]
24
+ csv << [:bar, 1]
25
+ csv << [:baz, 2]
26
+ end
27
+ str # => "foo:0\nbar:1\nbaz:2\n"
28
+ ary = CSV.parse(str, col_sep: col_sep)
29
+ ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
30
+
31
+ Using +::+ (two colons):
32
+ col_sep = '::'
33
+ str = CSV.generate(col_sep: col_sep) do |csv|
34
+ csv << [:foo, 0]
35
+ csv << [:bar, 1]
36
+ csv << [:baz, 2]
37
+ end
38
+ str # => "foo::0\nbar::1\nbaz::2\n"
39
+ ary = CSV.parse(str, col_sep: col_sep)
40
+ ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
41
+
42
+ Using <tt>''</tt> (empty string):
43
+ col_sep = ''
44
+ str = CSV.generate(col_sep: col_sep) do |csv|
45
+ csv << [:foo, 0]
46
+ csv << [:bar, 1]
47
+ csv << [:baz, 2]
48
+ end
49
+ str # => "foo0\nbar1\nbaz2\n"
50
+
51
+ ---
52
+
53
+ Raises an exception if parsing with the empty \String:
54
+ col_sep = ''
55
+ # Raises ArgumentError (:col_sep must be 1 or more characters: "")
56
+ CSV.parse("foo0\nbar1\nbaz2\n", col_sep: col_sep)
57
+
@@ -0,0 +1,42 @@
1
+ ====== Option +quote_char+
2
+
3
+ Specifies the character (\String of length 1) used used to quote fields
4
+ in both parsing and generating.
5
+ This String will be transcoded into the data's \Encoding before use.
6
+
7
+ Default value:
8
+ CSV::DEFAULT_OPTIONS.fetch(:quote_char) # => "\"" (double quote)
9
+
10
+ This is useful for an application that incorrectly uses <tt>'</tt> (single-quote)
11
+ to quote fields, instead of the correct <tt>"</tt> (double-quote).
12
+
13
+ Using the default (double quote):
14
+ str = CSV.generate do |csv|
15
+ csv << ['foo', 0]
16
+ csv << ["'bar'", 1]
17
+ csv << ['"baz"', 2]
18
+ end
19
+ str # => "foo,0\n'bar',1\n\"\"\"baz\"\"\",2\n"
20
+ ary = CSV.parse(str)
21
+ ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
22
+
23
+ Using <tt>'</tt> (single-quote):
24
+ quote_char = "'"
25
+ str = CSV.generate(quote_char: quote_char) do |csv|
26
+ csv << ['foo', 0]
27
+ csv << ["'bar'", 1]
28
+ csv << ['"baz"', 2]
29
+ end
30
+ str # => "foo,0\n'''bar''',1\n\"baz\",2\n"
31
+ ary = CSV.parse(str, quote_char: quote_char)
32
+ ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
33
+
34
+ ---
35
+
36
+ Raises an exception if the \String length is greater than 1:
37
+ # Raises ArgumentError (:quote_char has to be nil or a single character String)
38
+ CSV.new('', quote_char: 'xx')
39
+
40
+ Raises an exception if the value is not a \String:
41
+ # Raises ArgumentError (:quote_char has to be nil or a single character String)
42
+ CSV.new('', quote_char: :foo)
@@ -12,7 +12,8 @@ When +row_sep+ is a \String, that \String becomes the row separator.
12
12
  The String will be transcoded into the data's Encoding before use.
13
13
 
14
14
  Using <tt>"\n"</tt>:
15
- str = CSV.generate do |csv|
15
+ row_sep = "\n"
16
+ str = CSV.generate(row_sep: row_sep) do |csv|
16
17
  csv << [:foo, 0]
17
18
  csv << [:bar, 1]
18
19
  csv << [:baz, 2]
@@ -57,20 +58,28 @@ Using <tt>''</tt> (empty string):
57
58
  ---
58
59
 
59
60
  When +row_sep+ is the \Symbol +:auto+ (the default),
60
- invokes auto-discovery of the row separator.
61
+ generating uses <tt>"\n"</tt> as the row separator:
62
+ str = CSV.generate do |csv|
63
+ csv << [:foo, 0]
64
+ csv << [:bar, 1]
65
+ csv << [:baz, 2]
66
+ end
67
+ str # => "foo,0\nbar,1\nbaz,2\n"
68
+
69
+ Parsing, on the other hand, invokes auto-discovery of the row separator.
61
70
 
62
71
  Auto-discovery reads ahead in the data looking for the next <tt>\r\n</tt>, +\n+, or +\r+ sequence.
63
72
  The sequence will be selected even if it occurs in a quoted field,
64
73
  assuming that you would have the same line endings there.
65
74
 
66
- row_sep = :auto
67
- str = CSV.generate(row_sep: row_sep) do |csv|
75
+ Example:
76
+ str = CSV.generate do |csv|
68
77
  csv << [:foo, 0]
69
78
  csv << [:bar, 1]
70
79
  csv << [:baz, 2]
71
80
  end
72
81
  str # => "foo,0\nbar,1\nbaz,2\n"
73
- ary = CSV.parse(str, row_sep: row_sep)
82
+ ary = CSV.parse(str)
74
83
  ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
75
84
 
76
85
  The default <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) is used
@@ -80,12 +89,3 @@ if any of the following is true:
80
89
  * The stream is only available for output.
81
90
 
82
91
  Obviously, discovery takes a little time. Set manually if speed is important. Also note that IO objects should be opened in binary mode on Windows if this feature will be used as the line-ending translation can cause problems with resetting the document position to where it was before the read ahead.
83
-
84
- ---
85
-
86
- Raises an exception if the given value is not String-convertible:
87
- row_sep = BasicObject.new
88
- # Raises NoMethodError (undefined method `to_s' for #<BasicObject:>)
89
- CSV.generate_line(ary, row_sep: row_sep)
90
- # Raises NoMethodError (undefined method `to_s' for #<BasicObject:>)
91
- CSV.parse(str, row_sep: row_sep)
@@ -1,7 +1,7 @@
1
1
  ====== Option +write_converters+
2
2
 
3
- Specifies the \Proc or \Array of Procs that are to be called
4
- for converting each output field.
3
+ Specifies converters to be used in generating fields.
4
+ See {Write Converters}[#class-CSV-label-Write+Converters]
5
5
 
6
6
  Default value:
7
7
  CSV::DEFAULT_OPTIONS.fetch(:write_converters) # => nil
@@ -11,21 +11,15 @@ With no write converter:
11
11
  str # => "\"\na\n\",\tb\t, c \n"
12
12
 
13
13
  With a write converter:
14
- strip_converter = lambda {|field| field.strip }
14
+ strip_converter = proc {|field| field.strip }
15
15
  str = CSV.generate_line(["\na\n", "\tb\t", " c "], write_converters: strip_converter)
16
16
  str # => "a,b,c\n"
17
17
 
18
18
  With two write converters (called in order):
19
- upcase_converter = lambda {|field| field.upcase }
20
- downcase_converter = lambda {|field| field.downcase }
19
+ upcase_converter = proc {|field| field.upcase }
20
+ downcase_converter = proc {|field| field.downcase }
21
21
  write_converters = [upcase_converter, downcase_converter]
22
22
  str = CSV.generate_line(['a', 'b', 'c'], write_converters: write_converters)
23
23
  str # => "a,b,c\n"
24
24
 
25
- ---
26
-
27
- Raises an exception if the converter returns a value that is neither +nil+
28
- nor \String-convertible:
29
- bad_converter = lambda {|field| BasicObject.new }
30
- # Raises NoMethodError (undefined method `is_a?' for #<BasicObject:>)
31
- CSV.generate_line(['a', 'b', 'c'], write_converters: bad_converter)
25
+ See also {Write Converters}[#class-CSV-label-Write+Converters]
@@ -1,6 +1,6 @@
1
1
  ====== Option +write_nil_value+
2
2
 
3
- Specifies the object that is to be substituted for each +nil+ field.
3
+ Specifies the object that is to be substituted for each +nil+-valued field.
4
4
 
5
5
  Default value:
6
6
  CSV::DEFAULT_OPTIONS.fetch(:write_nil_value) # => nil
@@ -0,0 +1,46 @@
1
+ ====== Option +converters+
2
+
3
+ Specifies converters to be used in parsing fields.
4
+ See {Field Converters}[#class-CSV-label-Field+Converters]
5
+
6
+ Default value:
7
+ CSV::DEFAULT_OPTIONS.fetch(:converters) # => nil
8
+
9
+ The value may be a field converter name
10
+ (see {Stored Converters}[#class-CSV-label-Stored+Converters]):
11
+ str = '1,2,3'
12
+ # Without a converter
13
+ array = CSV.parse_line(str)
14
+ array # => ["1", "2", "3"]
15
+ # With built-in converter :integer
16
+ array = CSV.parse_line(str, converters: :integer)
17
+ array # => [1, 2, 3]
18
+
19
+ The value may be a converter list
20
+ (see {Converter Lists}[#class-CSV-label-Converter+Lists]):
21
+ str = '1,3.14159'
22
+ # Without converters
23
+ array = CSV.parse_line(str)
24
+ array # => ["1", "3.14159"]
25
+ # With built-in converters
26
+ array = CSV.parse_line(str, converters: [:integer, :float])
27
+ array # => [1, 3.14159]
28
+
29
+ The value may be a \Proc custom converter:
30
+ (see {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters]):
31
+ str = ' foo , bar , baz '
32
+ # Without a converter
33
+ array = CSV.parse_line(str)
34
+ array # => [" foo ", " bar ", " baz "]
35
+ # With a custom converter
36
+ array = CSV.parse_line(str, converters: proc {|field| field.strip })
37
+ array # => ["foo", "bar", "baz"]
38
+
39
+ See also {Custom Field Converters}[#class-CSV-label-Custom+Field+Converters]
40
+
41
+ ---
42
+
43
+ Raises an exception if the converter is not a converter name or a \Proc:
44
+ str = 'foo,0'
45
+ # Raises NoMethodError (undefined method `arity' for nil:NilClass)
46
+ CSV.parse(str, converters: :foo)
@@ -0,0 +1,43 @@
1
+ ====== Option +header_converters+
2
+
3
+ Specifies converters to be used in parsing headers.
4
+ See {Header Converters}[#class-CSV-label-Header+Converters]
5
+
6
+ Default value:
7
+ CSV::DEFAULT_OPTIONS.fetch(:header_converters) # => nil
8
+
9
+ Identical in functionality to option {converters}[#class-CSV-label-Option+converters]
10
+ except that:
11
+ - The converters apply only to the header row.
12
+ - The built-in header converters are +:downcase+ and +:symbol+.
13
+
14
+ This section assumes prior execution of:
15
+ str = <<-EOT
16
+ Name,Value
17
+ foo,0
18
+ bar,1
19
+ baz,2
20
+ EOT
21
+ # With no header converter
22
+ table = CSV.parse(str, headers: true)
23
+ table.headers # => ["Name", "Value"]
24
+
25
+ The value may be a header converter name
26
+ (see {Stored Converters}[#class-CSV-label-Stored+Converters]):
27
+ table = CSV.parse(str, headers: true, header_converters: :downcase)
28
+ table.headers # => ["name", "value"]
29
+
30
+ The value may be a converter list
31
+ (see {Converter Lists}[#class-CSV-label-Converter+Lists]):
32
+ header_converters = [:downcase, :symbol]
33
+ table = CSV.parse(str, headers: true, header_converters: header_converters)
34
+ table.headers # => [:name, :value]
35
+
36
+ The value may be a \Proc custom converter
37
+ (see {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters]):
38
+ upcase_converter = proc {|field| field.upcase }
39
+ table = CSV.parse(str, headers: true, header_converters: upcase_converter)
40
+ table.headers # => ["NAME", "VALUE"]
41
+
42
+ See also {Custom Header Converters}[#class-CSV-label-Custom+Header+Converters]
43
+