Skip to content

Commit 8812c58

Browse files
committed
force_quotes: add support for specifying the target indexes or names
GitHub: fix GH-153 Reported by Aleksandr. Thanks!!!
1 parent 6044976 commit 8812c58

File tree

2 files changed

+122
-3
lines changed

2 files changed

+122
-3
lines changed

lib/csv/writer.rb

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ def <<(row)
4343

4444
row = @fields_converter.convert(row, nil, lineno) if @fields_converter
4545

46+
i = -1
4647
converted_row = row.collect do |field|
47-
quote(field)
48+
i += 1
49+
quote(field, i)
4850
end
4951
line = converted_row.join(@column_separator) + @row_separator
5052
if @output_encoding
@@ -100,6 +102,33 @@ def prepare_header
100102
end
101103
end
102104

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+
103132
def prepare_format
104133
@column_separator = @options[:column_separator].to_s.encode(@encoding)
105134
row_separator = @options[:row_separator]
@@ -109,7 +138,17 @@ def prepare_format
109138
@row_separator = row_separator.to_s.encode(@encoding)
110139
end
111140
@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
113152
unless @force_quotes
114153
@quotable_pattern =
115154
Regexp.new("[\r\n".encode(@encoding) +
@@ -147,9 +186,11 @@ def quote_field(field)
147186
encoded_quote_character
148187
end
149188

150-
def quote(field)
189+
def quote(field, i)
151190
if @force_quotes
152191
quote_field(field)
192+
elsif @force_quotes_fields and @force_quotes_fields[i]
193+
quote_field(field)
153194
else
154195
if field.nil? # represent +nil+ fields as empty unquoted fields
155196
""

test/csv/write/test_force_quotes.rb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# frozen_string_literal: false
2+
3+
require_relative "../helper"
4+
5+
module TestCSVWriteForceQuotes
6+
def test_default
7+
assert_equal(%Q[1,2,3#{$INPUT_RECORD_SEPARATOR}],
8+
generate_line(["1", "2", "3"]))
9+
end
10+
11+
def test_true
12+
assert_equal(%Q["1","2","3"#{$INPUT_RECORD_SEPARATOR}],
13+
generate_line(["1", "2", "3"],
14+
force_quotes: true))
15+
end
16+
17+
def test_false
18+
assert_equal(%Q[1,2,3#{$INPUT_RECORD_SEPARATOR}],
19+
generate_line(["1", "2", "3"],
20+
force_quotes: false))
21+
end
22+
23+
def test_field_name
24+
assert_equal(%Q["1",2,"3"#{$INPUT_RECORD_SEPARATOR}],
25+
generate_line(["1", "2", "3"],
26+
headers: ["a", "b", "c"],
27+
force_quotes: ["a", :c]))
28+
end
29+
30+
def test_field_name_without_headers
31+
force_quotes = ["a", "c"]
32+
error = assert_raise(ArgumentError) do
33+
generate_line(["1", "2", "3"],
34+
force_quotes: force_quotes)
35+
end
36+
assert_equal(":headers is required when you use field name " +
37+
"in :force_quotes: " +
38+
"#{force_quotes.first.inspect}: #{force_quotes.inspect}",
39+
error.message)
40+
end
41+
42+
def test_field_index
43+
assert_equal(%Q["1",2,"3"#{$INPUT_RECORD_SEPARATOR}],
44+
generate_line(["1", "2", "3"],
45+
force_quotes: [0, 2]))
46+
end
47+
48+
def test_field_unknown
49+
force_quotes = [1.1]
50+
error = assert_raise(ArgumentError) do
51+
generate_line(["1", "2", "3"],
52+
force_quotes: force_quotes)
53+
end
54+
assert_equal(":force_quotes element must be field index or field name: " +
55+
"#{force_quotes.first.inspect}: #{force_quotes.inspect}",
56+
error.message)
57+
end
58+
end
59+
60+
class TestCSVWriteForceQuotesGenerateLine < Test::Unit::TestCase
61+
include TestCSVWriteForceQuotes
62+
extend DifferentOFS
63+
64+
def generate_line(row, **kwargs)
65+
CSV.generate_line(row, **kwargs)
66+
end
67+
end
68+
69+
class TestCSVWriteForceQuotesGenerate < Test::Unit::TestCase
70+
include TestCSVWriteForceQuotes
71+
extend DifferentOFS
72+
73+
def generate_line(row, **kwargs)
74+
CSV.generate(**kwargs) do |csv|
75+
csv << row
76+
end
77+
end
78+
end

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy