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
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.5"
5
+ VERSION = "3.2.0"
6
6
  end
data/lib/csv/writer.rb CHANGED
@@ -43,8 +43,10 @@ class CSV
43
43
 
44
44
  row = @fields_converter.convert(row, nil, lineno) if @fields_converter
45
45
 
46
+ i = -1
46
47
  converted_row = row.collect do |field|
47
- quote(field)
48
+ i += 1
49
+ quote(field, i)
48
50
  end
49
51
  line = converted_row.join(@column_separator) + @row_separator
50
52
  if @output_encoding
@@ -100,6 +102,33 @@ class CSV
100
102
  end
101
103
  end
102
104
 
105
+ def prepare_force_quotes_fields(force_quotes)
106
+ @force_quotes_fields = {}
107
+ force_quotes.each do |name_or_index|
108
+ case name_or_index
109
+ when Integer
110
+ index = name_or_index
111
+ @force_quotes_fields[index] = true
112
+ when String, Symbol
113
+ name = name_or_index.to_s
114
+ if @headers.nil?
115
+ message = ":headers is required when you use field name " +
116
+ "in :force_quotes: " +
117
+ "#{name_or_index.inspect}: #{force_quotes.inspect}"
118
+ raise ArgumentError, message
119
+ end
120
+ index = @headers.index(name)
121
+ next if index.nil?
122
+ @force_quotes_fields[index] = true
123
+ else
124
+ message = ":force_quotes element must be " +
125
+ "field index or field name: " +
126
+ "#{name_or_index.inspect}: #{force_quotes.inspect}"
127
+ raise ArgumentError, message
128
+ end
129
+ end
130
+ end
131
+
103
132
  def prepare_format
104
133
  @column_separator = @options[:column_separator].to_s.encode(@encoding)
105
134
  row_separator = @options[:row_separator]
@@ -109,7 +138,17 @@ class CSV
109
138
  @row_separator = row_separator.to_s.encode(@encoding)
110
139
  end
111
140
  @quote_character = @options[:quote_character]
112
- @force_quotes = @options[:force_quotes]
141
+ force_quotes = @options[:force_quotes]
142
+ if force_quotes.is_a?(Array)
143
+ prepare_force_quotes_fields(force_quotes)
144
+ @force_quotes = false
145
+ elsif force_quotes
146
+ @force_quotes_fields = nil
147
+ @force_quotes = true
148
+ else
149
+ @force_quotes_fields = nil
150
+ @force_quotes = false
151
+ end
113
152
  unless @force_quotes
114
153
  @quotable_pattern =
115
154
  Regexp.new("[\r\n".encode(@encoding) +
@@ -147,16 +186,18 @@ class CSV
147
186
  encoded_quote_character
148
187
  end
149
188
 
150
- def quote(field)
189
+ def quote(field, i)
151
190
  if @force_quotes
152
191
  quote_field(field)
192
+ elsif @force_quotes_fields and @force_quotes_fields[i]
193
+ quote_field(field)
153
194
  else
154
195
  if field.nil? # represent +nil+ fields as empty unquoted fields
155
196
  ""
156
197
  else
157
198
  field = String(field) # Stringify fields
158
199
  # represent empty fields as empty quoted fields
159
- if (@quote_empty and field.empty?) or @quotable_pattern.match?(field)
200
+ if (@quote_empty and field.empty?) or (field.valid_encoding? and @quotable_pattern.match?(field))
160
201
  quote_field(field)
161
202
  else
162
203
  field # unquoted field
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.5
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-05-17 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.
@@ -79,31 +79,40 @@ extra_rdoc_files:
79
79
  - LICENSE.txt
80
80
  - NEWS.md
81
81
  - README.md
82
+ - doc/csv/recipes/filtering.rdoc
83
+ - doc/csv/recipes/generating.rdoc
84
+ - doc/csv/recipes/parsing.rdoc
85
+ - doc/csv/recipes/recipes.rdoc
82
86
  files:
83
87
  - LICENSE.txt
84
88
  - NEWS.md
85
89
  - README.md
86
- - doc/col_sep.rdoc
87
- - doc/converters.rdoc
88
- - doc/empty_value.rdoc
89
- - doc/field_size_limit.rdoc
90
- - doc/force_quotes.rdoc
91
- - doc/header_converters.rdoc
92
- - doc/headers.rdoc
93
- - doc/liberal_parsing.rdoc
94
- - doc/nil_value.rdoc
95
- - doc/quote_char.rdoc
96
- - doc/quote_empty.rdoc
97
- - doc/return_headers.rdoc
98
- - doc/row_sep.rdoc
99
- - doc/skip_blanks.rdoc
100
- - doc/skip_lines.rdoc
101
- - doc/strip.rdoc
102
- - doc/unconverted_fields.rdoc
103
- - doc/write_converters.rdoc
104
- - doc/write_empty_value.rdoc
105
- - doc/write_headers.rdoc
106
- - doc/write_nil_value.rdoc
90
+ - doc/csv/arguments/io.rdoc
91
+ - doc/csv/options/common/col_sep.rdoc
92
+ - doc/csv/options/common/quote_char.rdoc
93
+ - doc/csv/options/common/row_sep.rdoc
94
+ - doc/csv/options/generating/force_quotes.rdoc
95
+ - doc/csv/options/generating/quote_empty.rdoc
96
+ - doc/csv/options/generating/write_converters.rdoc
97
+ - doc/csv/options/generating/write_empty_value.rdoc
98
+ - doc/csv/options/generating/write_headers.rdoc
99
+ - doc/csv/options/generating/write_nil_value.rdoc
100
+ - doc/csv/options/parsing/converters.rdoc
101
+ - doc/csv/options/parsing/empty_value.rdoc
102
+ - doc/csv/options/parsing/field_size_limit.rdoc
103
+ - doc/csv/options/parsing/header_converters.rdoc
104
+ - doc/csv/options/parsing/headers.rdoc
105
+ - doc/csv/options/parsing/liberal_parsing.rdoc
106
+ - doc/csv/options/parsing/nil_value.rdoc
107
+ - doc/csv/options/parsing/return_headers.rdoc
108
+ - doc/csv/options/parsing/skip_blanks.rdoc
109
+ - doc/csv/options/parsing/skip_lines.rdoc
110
+ - doc/csv/options/parsing/strip.rdoc
111
+ - doc/csv/options/parsing/unconverted_fields.rdoc
112
+ - doc/csv/recipes/filtering.rdoc
113
+ - doc/csv/recipes/generating.rdoc
114
+ - doc/csv/recipes/parsing.rdoc
115
+ - doc/csv/recipes/recipes.rdoc
107
116
  - lib/csv.rb
108
117
  - lib/csv/core_ext/array.rb
109
118
  - lib/csv/core_ext/string.rb
@@ -117,6 +126,7 @@ files:
117
126
  - lib/csv/writer.rb
118
127
  homepage: https://github.com/ruby/csv
119
128
  licenses:
129
+ - Ruby
120
130
  - BSD-2-Clause
121
131
  metadata: {}
122
132
  post_install_message:
@@ -129,14 +139,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
129
139
  requirements:
130
140
  - - ">="
131
141
  - !ruby/object:Gem::Version
132
- version: 2.3.0
142
+ version: 2.5.0
133
143
  required_rubygems_version: !ruby/object:Gem::Requirement
134
144
  requirements:
135
145
  - - ">="
136
146
  - !ruby/object:Gem::Version
137
147
  version: '0'
138
148
  requirements: []
139
- rubygems_version: 3.2.0.pre1
149
+ rubygems_version: 3.3.0.dev
140
150
  signing_key:
141
151
  specification_version: 4
142
152
  summary: CSV Reading and Writing
data/doc/col_sep.rdoc DELETED
@@ -1,45 +0,0 @@
1
- ====== Option +col_sep+
2
-
3
- Specifies the \String field 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
- For examples in this section:
11
- ary = ['a', 'b', 'c']
12
-
13
- Using the default:
14
- str = CSV.generate_line(line)
15
- str # => "a,b,c\n"
16
- ary = CSV.parse_line(str)
17
- ary # => ["a", "b", "c"]
18
-
19
- Using +:+ (colon):
20
- col_sep = ':'
21
- str = CSV.generate_line(ary, col_sep: col_sep)
22
- str # => "a:b:c\n"
23
- ary = CSV.parse_line(str, col_sep: col_sep)
24
- ary # => [["a", "b", "c"]]
25
-
26
- Using +::+ (two colons):
27
- col_sep = '::'
28
- str = CSV.generate_line(ary, col_sep: col_sep)
29
- str # => "a::b::c\n"
30
- ary = CSV.parse_line(str, col_sep: col_sep)
31
- ary # => [["a", "b", "c"]]
32
-
33
- ---
34
-
35
- Raises an exception if given the empty \String:
36
- col_sep = ''
37
- # Raises ArgumentError (:col_sep must be 1 or more characters: "")
38
- CSV.parse_line("a:b:c\n", col_sep: col_sep)
39
-
40
- Raises an exception if the given value is not String-convertible:
41
- col_sep = BasicObject.new
42
- # Raises NoMethodError (undefined method `to_s' for #<BasicObject:>)
43
- CSV.generate_line(line, col_sep: col_sep)
44
- # Raises NoMethodError (undefined method `to_s' for #<BasicObject:>)
45
- CSV.parse(str, col_sep: col_sep)
data/doc/converters.rdoc DELETED
@@ -1,45 +0,0 @@
1
- ====== Option +converters+
2
-
3
- Specifies a single field converter name or \Proc,
4
- or an \Array of field converter names and Procs.
5
-
6
- See {Field Converters}[#class-CSV-label-Field+Converters]
7
-
8
- Default value:
9
- CSV::DEFAULT_OPTIONS.fetch(:converters) # => nil
10
-
11
- The value may be a single field converter name:
12
- str = '1,2,3'
13
- # Without a converter
14
- ary = CSV.parse_line(str)
15
- ary # => ["1", "2", "3"]
16
- # With built-in converter :integer
17
- ary = CSV.parse_line(str, converters: :integer)
18
- ary # => [1, 2, 3]
19
-
20
- The value may be an \Array of field converter names:
21
- str = '1,3.14159'
22
- # Without converters
23
- ary = CSV.parse_line(str)
24
- ary # => ["1", "3.14159"]
25
- # With built-in converters
26
- ary = CSV.parse_line(str, converters: [:integer, :float])
27
- ary # => [1, 3.14159]
28
-
29
- The value may be a \Proc custom converter:
30
- str = ' foo , bar , baz '
31
- # Without a converter
32
- ary = CSV.parse_line(str)
33
- ary # => [" foo ", " bar ", " baz "]
34
- # With a custom converter
35
- ary = CSV.parse_line(str, converters: proc {|field| field.strip })
36
- ary # => ["foo", "bar", "baz"]
37
-
38
- See also {Custom Converters}[#class-CSV-label-Custom+Converters]
39
-
40
- ---
41
-
42
- Raises an exception if the converter is not a converter name or a \Proc:
43
- str = 'foo,0'
44
- # Raises NoMethodError (undefined method `arity' for nil:NilClass)
45
- CSV.parse(str, converters: :foo)
@@ -1,31 +0,0 @@
1
- ====== Option +header_converters+
2
-
3
- Specifies a \String converter name or an \Array of converter names.
4
-
5
- Default value:
6
- CSV::DEFAULT_OPTIONS.fetch(:header_converters) # => nil
7
-
8
- Identical in functionality to option {converters}[#class-CSV-label-Option+converters]
9
- except that:
10
- - The converters apply only to the header row.
11
- - The built-in header converters are +:downcase+ and +:symbol+.
12
-
13
- Examples:
14
- str = <<-EOT
15
- foo,0
16
- bar,1
17
- baz,2
18
- EOT
19
- headers = ['Name', 'Value']
20
- # With no header converter
21
- csv = CSV.parse(str, headers: headers)
22
- csv.headers # => ["Name", "Value"]
23
- # With header converter :downcase
24
- csv = CSV.parse(str, headers: headers, header_converters: :downcase)
25
- csv.headers # => ["name", "value"]
26
- # With header converter :symbol
27
- csv = CSV.parse(str, headers: headers, header_converters: :symbol)
28
- csv.headers # => [:name, :value]
29
- # With both
30
- csv = CSV.parse(str, headers: headers, header_converters: [:downcase, :symbol])
31
- csv.headers # => [:name, :value]
data/doc/quote_char.rdoc DELETED
@@ -1,32 +0,0 @@
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) # => "\"" (backslash)
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:
14
- ary = ['a', 'b', '"c"', 'd']
15
- str = CSV.generate_line(ary)
16
- str # => "a,b,\"\"\"c\"\"\",d\n"
17
- ary = CSV.parse_line(str)
18
- ary # => ["a", "b", "\"c\"", "d"]
19
-
20
- Using <tt>'</tt> (single-quote):
21
- quote_char = "'"
22
- ary = ['a', 'b', '\'c\'', 'd']
23
- str = CSV.generate_line(ary, quote_char: quote_char)
24
- str # => "a,b,'''c''',d\n"
25
- ary = CSV.parse_line(str, quote_char: quote_char)
26
- ary # => [["a", "b", "'c'", "d"]]
27
-
28
- ---
29
-
30
- Raises an exception if the \String length is greater than 1:
31
- # Raises ArgumentError (:quote_char has to be nil or a single character String)
32
- CSV.new('', quote_char: 'xx')