* lib/csv/csv.rb: Reworked CSV's parser and generator to be m17n. Data
is now parsed in the Encoding it is in without need for translation.
* lib/csv/csv.rb: Improved inspect() messages for better IRb support.
* lib/csv/csv.rb: Fixed header writing bug reported by Dov Murik.
* lib/csv/csv.rb: Use custom separators in parsing header Strings as
suggested by Shmulik Regev.
* lib/csv/csv.rb: Added a :write_headers option for outputting headers.
* lib/csv/csv.rb: Handle open() calls in binary mode whenever we can to
workaround a Windows issue where line-ending translation can cause an
off-by-one error in seeking back to a non-zero starting position after
auto-discovery for :row_sep as suggested by Robert Battle.
* lib/csv/csv.rb: Improved the parser to fail faster when fed some forms
of invalid CSV that can be detected without reading ahead.
* lib/csv/csv.rb: Added a :field_size_limit option to control CSV's
lookahead and prevent the parser from biting off more data than
it can chew.
* lib/csv/csv.rb: Added readers for CSV attributes: col_sep(), row_sep(),
quote_char(), field_size_limit(), converters(), unconverted_fields?(),
headers(), return_headers?(), write_headers?(), header_converters(),
skip_blanks?(), and force_quotes?().
* lib/csv/csv.rb: Cleaned up code syntax to be more inline with
Ruby 1.9 than 1.8.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19441 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-09-20 20:39:03 -04:00
|
|
|
#!/usr/bin/env ruby -w
|
|
|
|
# encoding: UTF-8
|
2007-12-24 21:46:26 -05:00
|
|
|
|
|
|
|
# tc_data_converters.rb
|
|
|
|
#
|
|
|
|
# Created by James Edward Gray II on 2005-10-31.
|
|
|
|
# Copyright 2005 James Edward Gray II. You can redistribute or modify this code
|
|
|
|
# under the terms of Ruby's license.
|
|
|
|
|
|
|
|
require "test/unit"
|
|
|
|
|
|
|
|
require "csv"
|
|
|
|
|
|
|
|
class TestDataConverters < Test::Unit::TestCase
|
|
|
|
def setup
|
|
|
|
@data = "Numbers,:integer,1,:float,3.015"
|
|
|
|
@parser = CSV.new(@data)
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
@custom = lambda { |field| field =~ /\A:(\S.*?)\s*\Z/ ? $1.to_sym : field }
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
@win_safe_time_str = Time.now.strftime("%a %b %d %H:%M:%S %Y")
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
def test_builtin_integer_converter
|
|
|
|
# does convert
|
|
|
|
[-5, 1, 10000000000].each do |n|
|
|
|
|
assert_equal(n, CSV::Converters[:integer][n.to_s])
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# does not convert
|
|
|
|
(%w{junk 1.0} + [""]).each do |str|
|
|
|
|
assert_equal(str, CSV::Converters[:integer][str])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_builtin_float_converter
|
|
|
|
# does convert
|
|
|
|
[-5.1234, 0, 2.3e-11].each do |n|
|
|
|
|
assert_equal(n, CSV::Converters[:float][n.to_s])
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# does not convert
|
|
|
|
(%w{junk 1..0 .015F} + [""]).each do |str|
|
|
|
|
assert_equal(str, CSV::Converters[:float][str])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_builtin_date_converter
|
|
|
|
# does convert
|
|
|
|
assert_instance_of(
|
|
|
|
Date,
|
|
|
|
CSV::Converters[:date][@win_safe_time_str.sub(/\d+:\d+:\d+ /, "")]
|
|
|
|
)
|
|
|
|
|
|
|
|
# does not convert
|
|
|
|
assert_instance_of(String, CSV::Converters[:date]["junk"])
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_builtin_date_time_converter
|
|
|
|
# does convert
|
|
|
|
assert_instance_of( DateTime,
|
|
|
|
CSV::Converters[:date_time][@win_safe_time_str] )
|
|
|
|
|
|
|
|
# does not convert
|
|
|
|
assert_instance_of(String, CSV::Converters[:date_time]["junk"])
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
def test_convert_with_builtin
|
|
|
|
# setup parser...
|
|
|
|
assert(@parser.respond_to?(:convert))
|
|
|
|
assert_nothing_raised(Exception) { @parser.convert(:integer) }
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# and use
|
|
|
|
assert_equal(["Numbers", ":integer", 1, ":float", "3.015"], @parser.shift)
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
setup # reset
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# setup parser...
|
|
|
|
assert_nothing_raised(Exception) { @parser.convert(:float) }
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# and use
|
|
|
|
assert_equal(["Numbers", ":integer", 1.0, ":float", 3.015], @parser.shift)
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
def test_convert_order
|
|
|
|
# floats first, then integers...
|
2009-03-05 22:56:38 -05:00
|
|
|
assert_nothing_raised(Exception) do
|
2007-12-24 21:46:26 -05:00
|
|
|
@parser.convert(:float)
|
|
|
|
@parser.convert(:integer)
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# gets us nothing but floats
|
|
|
|
assert_equal( [String, String, Float, String, Float],
|
|
|
|
@parser.shift.map { |field| field.class } )
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
setup # reset
|
|
|
|
|
|
|
|
# integers have precendance...
|
2009-03-05 22:56:38 -05:00
|
|
|
assert_nothing_raised(Exception) do
|
2007-12-24 21:46:26 -05:00
|
|
|
@parser.convert(:integer)
|
|
|
|
@parser.convert(:float)
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# gives us proper number conversion
|
|
|
|
assert_equal( [String, String, Fixnum, String, Float],
|
|
|
|
@parser.shift.map { |field| field.class } )
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_builtin_numeric_combo_converter
|
|
|
|
# setup parser...
|
|
|
|
assert_nothing_raised(Exception) { @parser.convert(:numeric) }
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# and use
|
|
|
|
assert_equal( [String, String, Fixnum, String, Float],
|
|
|
|
@parser.shift.map { |field| field.class } )
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_builtin_all_nested_combo_converter
|
|
|
|
# setup parser...
|
|
|
|
@data << ",#{@win_safe_time_str}" # add a DateTime field
|
|
|
|
@parser = CSV.new(@data) # reset parser
|
|
|
|
assert_nothing_raised(Exception) { @parser.convert(:all) }
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# and use
|
|
|
|
assert_equal( [String, String, Fixnum, String, Float, DateTime],
|
|
|
|
@parser.shift.map { |field| field.class } )
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
def test_convert_with_custom_code
|
|
|
|
# define custom converter...
|
|
|
|
assert_nothing_raised(Exception) do
|
|
|
|
@parser.convert { |field| field =~ /\A:(\S.*?)\s*\Z/ ? $1.to_sym : field }
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# and use
|
|
|
|
assert_equal(["Numbers", :integer, "1", :float, "3.015"], @parser.shift)
|
|
|
|
|
|
|
|
setup # reset
|
|
|
|
|
|
|
|
# mix built-in and custom...
|
|
|
|
assert_nothing_raised(Exception) { @parser.convert(:numeric) }
|
|
|
|
assert_nothing_raised(Exception) { @parser.convert(&@custom) }
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# and use
|
|
|
|
assert_equal(["Numbers", :integer, 1, :float, 3.015], @parser.shift)
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
def test_convert_with_custom_code_using_field_info
|
|
|
|
# define custom converter that uses field information...
|
|
|
|
assert_nothing_raised(Exception) do
|
|
|
|
@parser.convert do |field, info|
|
|
|
|
assert_equal(1, info.line)
|
|
|
|
info.index == 4 ? Float(field).floor : field
|
|
|
|
end
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# and use
|
|
|
|
assert_equal(["Numbers", ":integer", "1", ":float", 3], @parser.shift)
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
def test_convert_with_custom_code_using_field_info_header
|
2008-10-10 11:09:34 -04:00
|
|
|
@parser = CSV.new(@data, headers: %w{one two three four five})
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# define custom converter that uses field header information...
|
|
|
|
assert_nothing_raised(Exception) do
|
|
|
|
@parser.convert do |field, info|
|
|
|
|
info.header == "three" ? Integer(field) * 100 : field
|
|
|
|
end
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
# and use
|
|
|
|
assert_equal( ["Numbers", ":integer", 100, ":float", "3.015"],
|
|
|
|
@parser.shift.fields )
|
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
def test_shortcut_interface
|
|
|
|
assert_equal( ["Numbers", ":integer", 1, ":float", 3.015],
|
2008-10-10 11:09:34 -04:00
|
|
|
CSV.parse_line(@data, converters: :numeric) )
|
2007-12-24 21:46:26 -05:00
|
|
|
|
|
|
|
assert_equal( ["Numbers", ":integer", 1, ":float", 3.015],
|
2008-10-10 11:09:34 -04:00
|
|
|
CSV.parse_line(@data, converters: [:integer, :float]) )
|
2007-12-24 21:46:26 -05:00
|
|
|
|
|
|
|
assert_equal( ["Numbers", :integer, 1, :float, 3.015],
|
2008-10-10 11:09:34 -04:00
|
|
|
CSV.parse_line(@data, converters: [:numeric, @custom]) )
|
2007-12-24 21:46:26 -05:00
|
|
|
end
|
2009-03-05 22:56:38 -05:00
|
|
|
|
2007-12-24 21:46:26 -05:00
|
|
|
def test_unconverted_fields
|
|
|
|
[ [ @data,
|
|
|
|
["Numbers", :integer, 1, :float, 3.015],
|
|
|
|
%w{Numbers :integer 1 :float 3.015} ],
|
|
|
|
["\n", Array.new, Array.new] ].each do |test, fields, unconverted|
|
|
|
|
row = nil
|
2009-03-05 22:56:38 -05:00
|
|
|
assert_nothing_raised(Exception) do
|
2007-12-24 21:46:26 -05:00
|
|
|
row = CSV.parse_line( test,
|
2008-10-10 11:09:34 -04:00
|
|
|
converters: [:numeric, @custom],
|
|
|
|
unconverted_fields: true )
|
2007-12-24 21:46:26 -05:00
|
|
|
end
|
|
|
|
assert_not_nil(row)
|
|
|
|
assert_equal(fields, row)
|
|
|
|
assert_respond_to(row, :unconverted_fields)
|
|
|
|
assert_equal(unconverted, row.unconverted_fields)
|
|
|
|
end
|
|
|
|
|
|
|
|
data = <<-END_CSV.gsub(/^\s+/, "")
|
|
|
|
first,second,third
|
|
|
|
1,2,3
|
|
|
|
END_CSV
|
|
|
|
row = nil
|
2009-03-05 22:56:38 -05:00
|
|
|
assert_nothing_raised(Exception) do
|
2007-12-24 21:46:26 -05:00
|
|
|
row = CSV.parse_line( data,
|
2008-10-10 11:09:34 -04:00
|
|
|
converters: :numeric,
|
|
|
|
unconverted_fields: true,
|
|
|
|
headers: :first_row )
|
2007-12-24 21:46:26 -05:00
|
|
|
end
|
|
|
|
assert_not_nil(row)
|
|
|
|
assert_equal([["first", 1], ["second", 2], ["third", 3]], row.to_a)
|
|
|
|
assert_respond_to(row, :unconverted_fields)
|
|
|
|
assert_equal(%w{1 2 3}, row.unconverted_fields)
|
|
|
|
|
2009-03-05 22:56:38 -05:00
|
|
|
assert_nothing_raised(Exception) do
|
2007-12-24 21:46:26 -05:00
|
|
|
row = CSV.parse_line( data,
|
2008-10-10 11:09:34 -04:00
|
|
|
converters: :numeric,
|
|
|
|
unconverted_fields: true,
|
|
|
|
headers: :first_row,
|
|
|
|
return_headers: true )
|
2007-12-24 21:46:26 -05:00
|
|
|
end
|
|
|
|
assert_not_nil(row)
|
|
|
|
assert_equal( [%w{first first}, %w{second second}, %w{third third}],
|
|
|
|
row.to_a )
|
|
|
|
assert_respond_to(row, :unconverted_fields)
|
|
|
|
assert_equal(%w{first second third}, row.unconverted_fields)
|
|
|
|
|
2009-03-05 22:56:38 -05:00
|
|
|
assert_nothing_raised(Exception) do
|
2007-12-24 21:46:26 -05:00
|
|
|
row = CSV.parse_line( data,
|
2008-10-10 11:09:34 -04:00
|
|
|
converters: :numeric,
|
|
|
|
unconverted_fields: true,
|
|
|
|
headers: :first_row,
|
|
|
|
return_headers: true,
|
|
|
|
header_converters: :symbol )
|
2007-12-24 21:46:26 -05:00
|
|
|
end
|
|
|
|
assert_not_nil(row)
|
|
|
|
assert_equal( [[:first, "first"], [:second, "second"], [:third, "third"]],
|
|
|
|
row.to_a )
|
|
|
|
assert_respond_to(row, :unconverted_fields)
|
|
|
|
assert_equal(%w{first second third}, row.unconverted_fields)
|
|
|
|
|
2009-03-05 22:56:38 -05:00
|
|
|
assert_nothing_raised(Exception) do
|
2007-12-24 21:46:26 -05:00
|
|
|
row = CSV.parse_line( data,
|
2008-10-10 11:09:34 -04:00
|
|
|
converters: :numeric,
|
|
|
|
unconverted_fields: true,
|
|
|
|
headers: %w{my new headers},
|
|
|
|
return_headers: true,
|
|
|
|
header_converters: :symbol )
|
2007-12-24 21:46:26 -05:00
|
|
|
end
|
|
|
|
assert_not_nil(row)
|
|
|
|
assert_equal( [[:my, "my"], [:new, "new"], [:headers, "headers"]],
|
|
|
|
row.to_a )
|
|
|
|
assert_respond_to(row, :unconverted_fields)
|
|
|
|
assert_equal(Array.new, row.unconverted_fields)
|
|
|
|
end
|
|
|
|
end
|