mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Merge csv-3.0.0 from ruby/csv repository.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64638 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
21ce539f20
commit
60ebd4e26a
11 changed files with 148 additions and 106 deletions
12
lib/csv.rb
12
lib/csv.rb
|
@ -141,7 +141,7 @@ end
|
|||
# There are several specialized class methods for one-statement reading or writing,
|
||||
# described in the Specialized Methods section.
|
||||
#
|
||||
# If a String passed into ::new, it is internally wrapped into a StringIO object.
|
||||
# If a String is passed into ::new, it is internally wrapped into a StringIO object.
|
||||
#
|
||||
# +options+ can be used for specifying the particular CSV flavor (column
|
||||
# separators, row separators, value quoting and so on), and for data conversion,
|
||||
|
@ -890,8 +890,12 @@ class CSV
|
|||
# attempt to parse input not conformant
|
||||
# with RFC 4180, such as double quotes
|
||||
# in unquoted fields.
|
||||
# <b><tt>:nil_value</tt></b>:: TODO: WRITE ME.
|
||||
# <b><tt>:empty_value</tt></b>:: TODO: WRITE ME.
|
||||
# <b><tt>:nil_value</tt></b>:: When set an object, any values of an
|
||||
# empty field are replaced by the set
|
||||
# object, not nil.
|
||||
# <b><tt>:empty_value</tt></b>:: When set an object, any values of a
|
||||
# blank string field is replaced by
|
||||
# the set object.
|
||||
#
|
||||
# See CSV::DEFAULT_OPTIONS for the default settings.
|
||||
#
|
||||
|
@ -1232,7 +1236,7 @@ class CSV
|
|||
elsif @unconverted_fields
|
||||
return add_unconverted_fields(Array.new, Array.new)
|
||||
elsif @use_headers
|
||||
return self.class::Row.new(Array.new, Array.new)
|
||||
return self.class::Row.new(@headers, Array.new)
|
||||
else
|
||||
return Array.new
|
||||
end
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "version"
|
||||
begin
|
||||
require_relative "lib/csv/version"
|
||||
rescue LoadError
|
||||
require_relative "version"
|
||||
end
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = "csv"
|
||||
|
@ -13,7 +17,8 @@ Gem::Specification.new do |spec|
|
|||
spec.homepage = "https://github.com/ruby/csv"
|
||||
spec.license = "BSD-2-Clause"
|
||||
|
||||
spec.files = ["lib/csv.rb", "lib/csv/table.rb", "lib/csv/core_ext/string.rb", "lib/csv/core_ext/array.rb", "lib/csv/row.rb", "lib/csv/version.rb", "README.md", "LICENSE.txt", "news.md"]
|
||||
spec.files = Dir.glob("lib/**/*.rb")
|
||||
spec.files += ["README.md", "LICENSE.txt", "news.md"]
|
||||
spec.require_paths = ["lib"]
|
||||
spec.required_ruby_version = ">= 2.3.0"
|
||||
|
||||
|
|
|
@ -48,6 +48,11 @@ class CSV
|
|||
extend Forwardable
|
||||
def_delegators :@row, :empty?, :length, :size
|
||||
|
||||
def initialize_copy(other)
|
||||
super
|
||||
@row = @row.dup
|
||||
end
|
||||
|
||||
# Returns +true+ if this is a header row.
|
||||
def header_row?
|
||||
@header_row
|
||||
|
|
|
@ -375,4 +375,4 @@ class CSV
|
|||
"#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
class CSV
|
||||
# The version of the installed library.
|
||||
VERSION = "1.0.2"
|
||||
VERSION = "3.0.1"
|
||||
end
|
||||
|
|
|
@ -148,13 +148,13 @@ class TestCSV::Parsing < TestCSV
|
|||
CSV.parse_line("1,2\r,3", row_sep: "\n")
|
||||
end
|
||||
|
||||
bad_data = <<-END_DATA.gsub(/^ +/, "")
|
||||
line,1,abc
|
||||
line,2,"def\nghi"
|
||||
bad_data = <<-CSV
|
||||
line,1,abc
|
||||
line,2,"def\nghi"
|
||||
|
||||
line,4,some\rjunk
|
||||
line,5,jkl
|
||||
END_DATA
|
||||
line,4,some\rjunk
|
||||
line,5,jkl
|
||||
CSV
|
||||
lines = bad_data.lines.to_a
|
||||
assert_equal(6, lines.size)
|
||||
assert_match(/\Aline,4/, lines.find { |l| l =~ /some\rjunk/ })
|
||||
|
@ -172,13 +172,13 @@ class TestCSV::Parsing < TestCSV
|
|||
|
||||
assert_raise(CSV::MalformedCSVError) { CSV.parse_line('1,2,"3...') }
|
||||
|
||||
bad_data = <<-END_DATA.gsub(/^ +/, "")
|
||||
line,1,abc
|
||||
line,2,"def\nghi"
|
||||
bad_data = <<-CSV
|
||||
line,1,abc
|
||||
line,2,"def\nghi"
|
||||
|
||||
line,4,8'10"
|
||||
line,5,jkl
|
||||
END_DATA
|
||||
line,4,8'10"
|
||||
line,5,jkl
|
||||
CSV
|
||||
lines = bad_data.lines.to_a
|
||||
assert_equal(6, lines.size)
|
||||
assert_match(/\Aline,4/, lines.find { |l| l =~ /8'10"/ })
|
||||
|
|
|
@ -260,10 +260,10 @@ class TestCSV::DataConverters < TestCSV
|
|||
assert_equal(unconverted, row.unconverted_fields)
|
||||
end
|
||||
|
||||
data = <<-END_CSV.gsub(/^\s+/, "")
|
||||
first,second,third
|
||||
1,2,3
|
||||
END_CSV
|
||||
data = <<-CSV
|
||||
first,second,third
|
||||
1,2,3
|
||||
CSV
|
||||
row = nil
|
||||
assert_nothing_raised(Exception) do
|
||||
row = CSV.parse_line( data,
|
||||
|
|
|
@ -37,12 +37,12 @@ class TestCSV::Features < TestCSV
|
|||
|
||||
def setup
|
||||
super
|
||||
@sample_data = <<-END_DATA.gsub(/^ +/, "")
|
||||
line,1,abc
|
||||
line,2,"def\nghi"
|
||||
@sample_data = <<-CSV
|
||||
line,1,abc
|
||||
line,2,"def\nghi"
|
||||
|
||||
line,4,jkl
|
||||
END_DATA
|
||||
line,4,jkl
|
||||
CSV
|
||||
@csv = CSV.new(@sample_data)
|
||||
end
|
||||
|
||||
|
@ -225,12 +225,12 @@ class TestCSV::Features < TestCSV
|
|||
end
|
||||
|
||||
# reported by Kev Jackson
|
||||
def test_failing_to_escape_col_sep_bug_fix
|
||||
def test_failing_to_escape_col_sep
|
||||
assert_nothing_raised(Exception) { CSV.new(String.new, col_sep: "|") }
|
||||
end
|
||||
|
||||
# reported by Chris Roos
|
||||
def test_failing_to_reset_headers_in_rewind_bug_fix
|
||||
def test_failing_to_reset_headers_in_rewind
|
||||
csv = CSV.new("forename,surname", headers: true, return_headers: true)
|
||||
csv.each {|row| assert_predicate row, :header_row?}
|
||||
csv.rewind
|
||||
|
@ -238,16 +238,16 @@ class TestCSV::Features < TestCSV
|
|||
end
|
||||
|
||||
# reported by Dave Burt
|
||||
def test_leading_empty_fields_with_multibyte_col_sep_bug_fix
|
||||
data = <<-END_DATA.gsub(/^\s+/, "")
|
||||
<=><=>A<=>B<=>C
|
||||
1<=>2<=>3
|
||||
END_DATA
|
||||
def test_leading_empty_fields_with_multibyte_col_sep
|
||||
data = <<-CSV
|
||||
<=><=>A<=>B<=>C
|
||||
1<=>2<=>3
|
||||
CSV
|
||||
parsed = CSV.parse(data, col_sep: "<=>")
|
||||
assert_equal([[nil, nil, "A", "B", "C"], ["1", "2", "3"]], parsed)
|
||||
end
|
||||
|
||||
def test_gzip_reader_bug_fix
|
||||
def test_gzip_reader
|
||||
zipped = nil
|
||||
assert_nothing_raised(NoMethodError) do
|
||||
zipped = CSV.new(
|
||||
|
@ -261,7 +261,7 @@ class TestCSV::Features < TestCSV
|
|||
zipped.close
|
||||
end if defined?(Zlib::GzipReader)
|
||||
|
||||
def test_gzip_writer_bug_fix
|
||||
def test_gzip_writer
|
||||
Tempfile.create(%w"temp .gz") {|tempfile|
|
||||
tempfile.close
|
||||
file = tempfile.path
|
||||
|
|
|
@ -13,11 +13,11 @@ class TestCSV::Headers < TestCSV
|
|||
|
||||
def setup
|
||||
super
|
||||
@data = <<-END_CSV.gsub(/^\s+/, "")
|
||||
first,second,third
|
||||
A,B,C
|
||||
1,2,3
|
||||
END_CSV
|
||||
@data = <<-CSV
|
||||
first,second,third
|
||||
A,B,C
|
||||
1,2,3
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_first_row
|
||||
|
@ -183,10 +183,10 @@ class TestCSV::Headers < TestCSV
|
|||
|
||||
def test_converters
|
||||
# create test data where headers and fields look alike
|
||||
data = <<-END_MATCHING_CSV.gsub(/^\s+/, "")
|
||||
1,2,3
|
||||
1,2,3
|
||||
END_MATCHING_CSV
|
||||
data = <<-CSV
|
||||
1,2,3
|
||||
1,2,3
|
||||
CSV
|
||||
|
||||
# normal converters do not affect headers
|
||||
csv = CSV.parse( data, headers: true,
|
||||
|
@ -256,16 +256,16 @@ class TestCSV::Headers < TestCSV
|
|||
end
|
||||
|
||||
def test_skip_blanks
|
||||
@data = <<-END_CSV.gsub(/^ +/, "")
|
||||
@data = <<-CSV
|
||||
|
||||
|
||||
A,B,C
|
||||
A,B,C
|
||||
|
||||
1,2,3
|
||||
1,2,3
|
||||
|
||||
|
||||
|
||||
END_CSV
|
||||
CSV
|
||||
|
||||
expected = [%w[1 2 3]]
|
||||
CSV.parse(@data, headers: true, skip_blanks: true) do |row|
|
||||
|
@ -292,7 +292,7 @@ class TestCSV::Headers < TestCSV
|
|||
assert_equal(%w[first second third], csv.headers) # after headers are read
|
||||
end
|
||||
|
||||
def test_blank_row_bug_fix
|
||||
def test_blank_row
|
||||
@data += "\n#{@data}" # add a blank row
|
||||
|
||||
# ensure that everything returned is a Row object
|
||||
|
@ -300,4 +300,19 @@ class TestCSV::Headers < TestCSV
|
|||
assert_instance_of(CSV::Row, row)
|
||||
end
|
||||
end
|
||||
|
||||
def test_nil_row_header
|
||||
@data = <<-CSV
|
||||
A
|
||||
|
||||
1
|
||||
CSV
|
||||
|
||||
csv = CSV.parse(@data, headers: true)
|
||||
|
||||
# ensure nil row creates Row object with headers
|
||||
row = csv[0]
|
||||
assert_equal([["A"], [nil]],
|
||||
[row.headers, row.fields])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -419,4 +419,12 @@ class TestCSV::Row < TestCSV
|
|||
row.dig("A", 0)
|
||||
end
|
||||
end
|
||||
|
||||
def test_dup
|
||||
row = CSV::Row.new(["A"], ["foo"])
|
||||
dupped_row = row.dup
|
||||
dupped_row.delete("A")
|
||||
assert_equal(["foo", nil],
|
||||
[row["A"], dupped_row["A"]])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,6 +42,11 @@ class TestCSV::Table < TestCSV
|
|||
assert_equal(:row, rows.mode)
|
||||
assert_equal(@table, rows)
|
||||
|
||||
col_or_row = rows.by_col_or_row
|
||||
assert_equal(:row, rows.mode)
|
||||
assert_equal(:col_or_row, col_or_row.mode)
|
||||
assert_equal(@table, col_or_row)
|
||||
|
||||
# destructive mode changing calls
|
||||
assert_equal(@table, @table.by_row!)
|
||||
assert_equal(:row, @table.mode)
|
||||
|
@ -148,13 +153,13 @@ class TestCSV::Table < TestCSV
|
|||
@table.to_a )
|
||||
|
||||
# verify resulting table
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
A,B,C,Type,Index
|
||||
1,100,3,data,1
|
||||
4,200,6,data,2
|
||||
10,,12,data,3
|
||||
13,,15,data,
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
A,B,C,Type,Index
|
||||
1,100,3,data,1
|
||||
4,200,6,data,2
|
||||
10,,12,data,3
|
||||
13,,15,data,
|
||||
CSV
|
||||
|
||||
# with headers
|
||||
@header_table["Type"] = "data"
|
||||
|
@ -286,12 +291,12 @@ class TestCSV::Table < TestCSV
|
|||
end
|
||||
|
||||
def test_to_csv
|
||||
csv = <<-END_CSV.gsub(/^\s+/, "")
|
||||
A,B,C
|
||||
1,2,3
|
||||
4,5,6
|
||||
7,8,9
|
||||
END_CSV
|
||||
csv = <<-CSV
|
||||
A,B,C
|
||||
1,2,3
|
||||
4,5,6
|
||||
7,8,9
|
||||
CSV
|
||||
|
||||
# normal conversion
|
||||
assert_equal(csv, @table.to_csv)
|
||||
|
@ -330,11 +335,11 @@ class TestCSV::Table < TestCSV
|
|||
assert_equal(@rows.map { |row| row["A"] }, @table.delete("A"))
|
||||
|
||||
# verify resulting table
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
B,C
|
||||
2,3
|
||||
8,9
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
B,C
|
||||
2,3
|
||||
8,9
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_delete_mixed_multiple
|
||||
|
@ -352,11 +357,11 @@ class TestCSV::Table < TestCSV
|
|||
@table.delete(1, "A"))
|
||||
|
||||
# verify resulting table
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
B,C
|
||||
2,3
|
||||
8,9
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
B,C
|
||||
2,3
|
||||
8,9
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_delete_column
|
||||
|
@ -369,12 +374,12 @@ class TestCSV::Table < TestCSV
|
|||
assert_equal(@rows.map { |row| row["C"] }, @table.delete("C"))
|
||||
|
||||
# verify resulting table
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
B
|
||||
2
|
||||
5
|
||||
8
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
B
|
||||
2
|
||||
5
|
||||
8
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_delete_row
|
||||
|
@ -387,11 +392,11 @@ class TestCSV::Table < TestCSV
|
|||
assert_raise(TypeError) { @table.delete("C") }
|
||||
|
||||
# verify resulting table
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
A,B,C
|
||||
1,2,3
|
||||
7,8,9
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
A,B,C
|
||||
1,2,3
|
||||
7,8,9
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_delete_with_blank_rows
|
||||
|
@ -408,10 +413,10 @@ class TestCSV::Table < TestCSV
|
|||
assert_equal(@table, @table.delete_if { |row| (row["B"] % 2).zero? })
|
||||
|
||||
# verify resulting table
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
A,B,C
|
||||
4,5,6
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
A,B,C
|
||||
4,5,6
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_delete_if_row_without_block
|
||||
|
@ -426,10 +431,10 @@ class TestCSV::Table < TestCSV
|
|||
assert_equal(@table, enum.each { |row| (row["B"] % 2).zero? })
|
||||
|
||||
# verify resulting table
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
A,B,C
|
||||
4,5,6
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
A,B,C
|
||||
4,5,6
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_delete_if_column
|
||||
|
@ -439,12 +444,12 @@ class TestCSV::Table < TestCSV
|
|||
@table.by_col!
|
||||
|
||||
assert_equal(@table, @table.delete_if { |h, v| h > "A" })
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
A
|
||||
1
|
||||
4
|
||||
7
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
A
|
||||
1
|
||||
4
|
||||
7
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_delete_if_column_without_block
|
||||
|
@ -458,12 +463,12 @@ class TestCSV::Table < TestCSV
|
|||
assert_equal(@table.headers.size, enum.size)
|
||||
|
||||
assert_equal(@table, enum.each { |h, v| h > "A" })
|
||||
assert_equal(<<-END_RESULT.gsub(/^\s+/, ""), @table.to_csv)
|
||||
A
|
||||
1
|
||||
4
|
||||
7
|
||||
END_RESULT
|
||||
assert_equal(<<-CSV, @table.to_csv)
|
||||
A
|
||||
1
|
||||
4
|
||||
7
|
||||
CSV
|
||||
end
|
||||
|
||||
def test_values_at
|
||||
|
|
Loading…
Reference in a new issue