csv 3.1.5 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +36 -0
  3. data/doc/arguments/io.rdoc +5 -0
  4. data/doc/options/common/col_sep.rdoc +63 -0
  5. data/doc/options/common/quote_char.rdoc +42 -0
  6. data/doc/{row_sep.rdoc → options/common/row_sep.rdoc} +15 -6
  7. data/doc/{force_quotes.rdoc → options/generating/force_quotes.rdoc} +0 -0
  8. data/doc/{quote_empty.rdoc → options/generating/quote_empty.rdoc} +0 -0
  9. data/doc/{write_converters.rdoc → options/generating/write_converters.rdoc} +8 -6
  10. data/doc/{write_empty_value.rdoc → options/generating/write_empty_value.rdoc} +0 -0
  11. data/doc/{write_headers.rdoc → options/generating/write_headers.rdoc} +0 -0
  12. data/doc/{write_nil_value.rdoc → options/generating/write_nil_value.rdoc} +1 -1
  13. data/doc/options/parsing/converters.rdoc +46 -0
  14. data/doc/{empty_value.rdoc → options/parsing/empty_value.rdoc} +0 -0
  15. data/doc/{field_size_limit.rdoc → options/parsing/field_size_limit.rdoc} +0 -0
  16. data/doc/options/parsing/header_converters.rdoc +43 -0
  17. data/doc/{headers.rdoc → options/parsing/headers.rdoc} +0 -0
  18. data/doc/{liberal_parsing.rdoc → options/parsing/liberal_parsing.rdoc} +0 -0
  19. data/doc/{nil_value.rdoc → options/parsing/nil_value.rdoc} +0 -0
  20. data/doc/{return_headers.rdoc → options/parsing/return_headers.rdoc} +0 -0
  21. data/doc/{skip_blanks.rdoc → options/parsing/skip_blanks.rdoc} +0 -0
  22. data/doc/{skip_lines.rdoc → options/parsing/skip_lines.rdoc} +0 -0
  23. data/doc/{strip.rdoc → options/parsing/strip.rdoc} +0 -0
  24. data/doc/{unconverted_fields.rdoc → options/parsing/unconverted_fields.rdoc} +0 -0
  25. data/lib/csv.rb +1334 -495
  26. data/lib/csv/version.rb +1 -1
  27. data/lib/csv/writer.rb +45 -4
  28. metadata +38 -23
  29. data/doc/col_sep.rdoc +0 -45
  30. data/doc/converters.rdoc +0 -45
  31. data/doc/header_converters.rdoc +0 -31
  32. data/doc/quote_char.rdoc +0 -32
@@ -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.1.6"
6
6
  end
@@ -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.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Edward Gray II
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-05-17 00:00:00.000000000 Z
12
+ date: 2020-07-19 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: stringio
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.1.3
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 0.1.3
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: bundler
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -83,27 +97,28 @@ files:
83
97
  - LICENSE.txt
84
98
  - NEWS.md
85
99
  - 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
100
+ - doc/arguments/io.rdoc
101
+ - doc/options/common/col_sep.rdoc
102
+ - doc/options/common/quote_char.rdoc
103
+ - doc/options/common/row_sep.rdoc
104
+ - doc/options/generating/force_quotes.rdoc
105
+ - doc/options/generating/quote_empty.rdoc
106
+ - doc/options/generating/write_converters.rdoc
107
+ - doc/options/generating/write_empty_value.rdoc
108
+ - doc/options/generating/write_headers.rdoc
109
+ - doc/options/generating/write_nil_value.rdoc
110
+ - doc/options/parsing/converters.rdoc
111
+ - doc/options/parsing/empty_value.rdoc
112
+ - doc/options/parsing/field_size_limit.rdoc
113
+ - doc/options/parsing/header_converters.rdoc
114
+ - doc/options/parsing/headers.rdoc
115
+ - doc/options/parsing/liberal_parsing.rdoc
116
+ - doc/options/parsing/nil_value.rdoc
117
+ - doc/options/parsing/return_headers.rdoc
118
+ - doc/options/parsing/skip_blanks.rdoc
119
+ - doc/options/parsing/skip_lines.rdoc
120
+ - doc/options/parsing/strip.rdoc
121
+ - doc/options/parsing/unconverted_fields.rdoc
107
122
  - lib/csv.rb
108
123
  - lib/csv/core_ext/array.rb
109
124
  - lib/csv/core_ext/string.rb
@@ -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)
@@ -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]
@@ -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')