diff --git a/lib/csv.rb b/lib/csv.rb index 20cfda4b41..170ab04c24 100644 --- a/lib/csv.rb +++ b/lib/csv.rb @@ -1465,6 +1465,46 @@ class CSV (new(str, **options) << row).string end + # :call-seq: + # CSV.generate_lines(rows) + # CSV.generate_lines(rows, **options) + # + # Returns the \String created by generating \CSV from + # using the specified +options+. + # + # Argument +rows+ must be an \Array of row. Row is \Array of \String or \CSV::Row. + # + # Special options: + # * Option :row_sep defaults to "\n" on Ruby 3.0 or later + # and $INPUT_RECORD_SEPARATOR ($/) otherwise.: + # $INPUT_RECORD_SEPARATOR # => "\n" + # * This method accepts an additional option, :encoding, which sets the base + # Encoding for the output. This method will try to guess your Encoding from + # the first non-+nil+ field in +row+, if possible, but you may need to use + # this parameter as a backup plan. + # + # For other +options+, + # see {Options for Generating}[#class-CSV-label-Options+for+Generating]. + # + # --- + # + # Returns the \String generated from an + # CSV.generate_lines(['foo', '0'], ['bar', '1'], ['baz', '2']) # => "foo,0\nbar,1\nbaz.2\n" + # + # --- + # + # Raises an exception + # # Raises NoMethodError (undefined method `find' for :foo:Symbol) + # CSV.generate_lines(:foo) + # + def generate_lines(rows, **options) + self.generate(**options) do |csv| + rows.each do |row| + csv << row + end + end + end + # # :call-seq: # open(file_path, mode = "rb", **options ) -> new_csv diff --git a/lib/csv/version.rb b/lib/csv/version.rb index eaddde9a23..ca16064a89 100644 --- a/lib/csv/version.rb +++ b/lib/csv/version.rb @@ -2,5 +2,5 @@ class CSV # The version of the installed library. - VERSION = "3.2.4" + VERSION = "3.2.5" end diff --git a/test/csv/interface/test_write.rb b/test/csv/interface/test_write.rb index 02c2c5c5ce..0cd39a7663 100644 --- a/test/csv/interface/test_write.rb +++ b/test/csv/interface/test_write.rb @@ -85,6 +85,15 @@ testrow LINE end + def test_generate_lines + lines = CSV.generate_lines([["foo", "bar"], [1, 2], [3, 4]]) + assert_equal(<<-LINES, lines) +foo,bar +1,2 +3,4 + LINES + end + def test_headers_detection headers = ["a", "b", "c"] CSV.open(@output.path, "w", headers: true) do |csv| diff --git a/test/csv/parse/test_convert.rb b/test/csv/parse/test_convert.rb index 2ac255695e..c9195c71d9 100644 --- a/test/csv/parse/test_convert.rb +++ b/test/csv/parse/test_convert.rb @@ -15,6 +15,22 @@ class TestCSVParseConvert < Test::Unit::TestCase @time = Time.utc(2018, 12, 30, 6, 41, 29) @windows_safe_time_data = @time.strftime("%a %b %d %H:%M:%S %Y") + + @preserving_converter = lambda do |field, info| + f = field.encode(CSV::ConverterEncoding) + return f if info.quoted? + begin + Integer(f, 10) + rescue + f + end + end + + @quoted_header_converter = lambda do |field, info| + f = field.encode(CSV::ConverterEncoding) + return f if info.quoted? + f.to_sym + end end def test_integer @@ -108,62 +124,42 @@ class TestCSVParseConvert < Test::Unit::TestCase CSV.parse_line(',"",a', empty_value: "empty")) end - sub_test_case("#quoted?") do - def setup - @preserving_converter = lambda do |field, info| - f = field.encode(CSV::ConverterEncoding) - return f if info.quoted? - begin - Integer(f, 10) - rescue - f - end - end + def test_quoted_parse_line + row = CSV.parse_line('1,"2",3', converters: @preserving_converter) + assert_equal([1, "2", 3], row) + end - @quoted_header_converter = lambda do |field, info| - f = field.encode(CSV::ConverterEncoding) - return f if info.quoted? - f.to_sym - end - end + def test_quoted_parse + expected = [["quoted", "unquoted"], ["109", 1], ["10A", 2]] + rows = CSV.parse(<<~CSV, converters: @preserving_converter) + "quoted",unquoted + "109",1 + "10A",2 + CSV + assert_equal(expected, rows) + end - def test_parse_line - row = CSV.parse_line('1,"2",3', converters: @preserving_converter) - assert_equal([1, "2", 3], row) - end + def test_quoted_alternating_quote + row = CSV.parse_line('"1",2,"3"', converters: @preserving_converter) + assert_equal(['1', 2, '3'], row) + end - def test_parse - expected = [["quoted", "unquoted"], ["109", 1], ["10A", 2]] - rows = CSV.parse(<<~CSV, converters: @preserving_converter) - "quoted",unquoted - "109",1 - "10A",2 - CSV - assert_equal(expected, rows) - end + def test_quoted_parse_headers + expected = [["quoted", :unquoted], ["109", "1"], ["10A", "2"]] + table = CSV.parse(<<~CSV, headers: true, header_converters: @quoted_header_converter) + "quoted",unquoted + "109",1 + "10A",2 + CSV + assert_equal(expected, table.to_a) + end - def test_alternating_quote - row = CSV.parse_line('"1",2,"3"', converters: @preserving_converter) - assert_equal(['1', 2, '3'], row) - end - - def test_parse_headers - expected = [["quoted", :unquoted], ["109", "1"], ["10A", "2"]] - table = CSV.parse(<<~CSV, headers: true, header_converters: @quoted_header_converter) - "quoted",unquoted - "109",1 - "10A",2 - CSV - assert_equal(expected, table.to_a) - end - - def test_parse_with_string_headers - expected = [["quoted", :unquoted], %w[109 1], %w[10A 2]] - table = CSV.parse(<<~CSV, headers: '"quoted",unquoted', header_converters: @quoted_header_converter) - "109",1 - "10A",2 - CSV - assert_equal(expected, table.to_a) - end + def test_quoted_parse_with_string_headers + expected = [["quoted", :unquoted], %w[109 1], %w[10A 2]] + table = CSV.parse(<<~CSV, headers: '"quoted",unquoted', header_converters: @quoted_header_converter) + "109",1 + "10A",2 + CSV + assert_equal(expected, table.to_a) end end