csv 3.1.5 → 3.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/NEWS.md +36 -0
- data/doc/arguments/io.rdoc +5 -0
- data/doc/options/common/col_sep.rdoc +63 -0
- data/doc/options/common/quote_char.rdoc +42 -0
- data/doc/{row_sep.rdoc → options/common/row_sep.rdoc} +15 -6
- data/doc/{force_quotes.rdoc → options/generating/force_quotes.rdoc} +0 -0
- data/doc/{quote_empty.rdoc → options/generating/quote_empty.rdoc} +0 -0
- data/doc/{write_converters.rdoc → options/generating/write_converters.rdoc} +8 -6
- data/doc/{write_empty_value.rdoc → options/generating/write_empty_value.rdoc} +0 -0
- data/doc/{write_headers.rdoc → options/generating/write_headers.rdoc} +0 -0
- data/doc/{write_nil_value.rdoc → options/generating/write_nil_value.rdoc} +1 -1
- data/doc/options/parsing/converters.rdoc +46 -0
- data/doc/{empty_value.rdoc → options/parsing/empty_value.rdoc} +0 -0
- data/doc/{field_size_limit.rdoc → options/parsing/field_size_limit.rdoc} +0 -0
- data/doc/options/parsing/header_converters.rdoc +43 -0
- data/doc/{headers.rdoc → options/parsing/headers.rdoc} +0 -0
- data/doc/{liberal_parsing.rdoc → options/parsing/liberal_parsing.rdoc} +0 -0
- data/doc/{nil_value.rdoc → options/parsing/nil_value.rdoc} +0 -0
- data/doc/{return_headers.rdoc → options/parsing/return_headers.rdoc} +0 -0
- data/doc/{skip_blanks.rdoc → options/parsing/skip_blanks.rdoc} +0 -0
- data/doc/{skip_lines.rdoc → options/parsing/skip_lines.rdoc} +0 -0
- data/doc/{strip.rdoc → options/parsing/strip.rdoc} +0 -0
- data/doc/{unconverted_fields.rdoc → options/parsing/unconverted_fields.rdoc} +0 -0
- data/lib/csv.rb +1334 -495
- data/lib/csv/version.rb +1 -1
- data/lib/csv/writer.rb +45 -4
- metadata +38 -23
- data/doc/col_sep.rdoc +0 -45
- data/doc/converters.rdoc +0 -45
- data/doc/header_converters.rdoc +0 -31
- data/doc/quote_char.rdoc +0 -32
data/lib/csv/version.rb
CHANGED
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
|
-
|
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
|
-
|
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.
|
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-
|
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/
|
87
|
-
- doc/
|
88
|
-
- doc/
|
89
|
-
- doc/
|
90
|
-
- doc/force_quotes.rdoc
|
91
|
-
- doc/
|
92
|
-
- doc/
|
93
|
-
- doc/
|
94
|
-
- doc/
|
95
|
-
- doc/
|
96
|
-
- doc/
|
97
|
-
- doc/
|
98
|
-
- doc/
|
99
|
-
- doc/
|
100
|
-
- doc/
|
101
|
-
- doc/
|
102
|
-
- doc/
|
103
|
-
- doc/
|
104
|
-
- doc/
|
105
|
-
- doc/
|
106
|
-
- doc/
|
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
|
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)
|
data/doc/header_converters.rdoc
DELETED
@@ -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')
|