Skip to content

Commit

Permalink
Import CSV 3.0.8
Browse files Browse the repository at this point in the history
This includes performance improvements and backward incompatibility
fixes.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67560 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
  • Loading branch information
kou committed Apr 14, 2019
1 parent fb96811 commit e3b6c7c
Show file tree
Hide file tree
Showing 23 changed files with 1,534 additions and 650 deletions.
2 changes: 1 addition & 1 deletion NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Regexp/String::

CSV::

* Upgrade to 3.0.4.
* Upgrade to 3.0.8.
See https://github.com/ruby/csv/blob/master/NEWS.md.

Date::
Expand Down
129 changes: 101 additions & 28 deletions lib/csv.rb
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,9 @@ def self.filter(input=nil, output=nil, **options)
# <tt>encoding: "UTF-32BE:UTF-8"</tt> would read UTF-32BE data from the file
# but transcode it to UTF-8 before CSV parses it.
#
def self.foreach(path, **options, &block)
return to_enum(__method__, path, options) unless block_given?
open(path, options) do |csv|
def self.foreach(path, mode="r", **options, &block)
return to_enum(__method__, path, mode, options) unless block_given?
open(path, mode, options) do |csv|
csv.each(&block)
end
end
Expand Down Expand Up @@ -885,6 +885,10 @@ def self.table(path, **options)
# blank string field is replaced by
# the set object.
# <b><tt>:quote_empty</tt></b>:: TODO
# <b><tt>:write_converters</tt></b>:: TODO
# <b><tt>:write_nil_value</tt></b>:: TODO
# <b><tt>:write_empty_value</tt></b>:: TODO
# <b><tt>:strip</tt></b>:: TODO
#
# See CSV::DEFAULT_OPTIONS for the default settings.
#
Expand All @@ -911,7 +915,11 @@ def initialize(data,
encoding: nil,
nil_value: nil,
empty_value: "",
quote_empty: true)
quote_empty: true,
write_converters: nil,
write_nil_value: nil,
write_empty_value: "",
strip: false)
raise ArgumentError.new("Cannot parse nil as CSV") if data.nil?

# create the IO object we will read from
Expand All @@ -922,8 +930,13 @@ def initialize(data,
nil_value: nil_value,
empty_value: empty_value,
}
@write_fields_converter_options = {
nil_value: write_nil_value,
empty_value: write_empty_value,
}
@initial_converters = converters
@initial_header_converters = header_converters
@initial_write_converters = write_converters

@parser_options = {
column_separator: col_sep,
Expand All @@ -939,6 +952,7 @@ def initialize(data,
encoding: @encoding,
nil_value: nil_value,
empty_value: empty_value,
strip: strip,
}
@parser = nil

Expand Down Expand Up @@ -998,7 +1012,7 @@ def skip_lines
# as is.
#
def converters
fields_converter.map do |converter|
parser_fields_converter.map do |converter|
name = Converters.rassoc(converter)
name ? name.first : converter
end
Expand Down Expand Up @@ -1098,12 +1112,58 @@ def line
### IO and StringIO Delegation ###

extend Forwardable
def_delegators :@io, :binmode, :binmode?, :close, :close_read, :close_write,
:closed?, :eof, :eof?, :external_encoding, :fcntl,
:fileno, :flock, :flush, :fsync, :internal_encoding,
:ioctl, :isatty, :path, :pid, :pos, :pos=, :reopen,
:seek, :stat, :string, :sync, :sync=, :tell, :to_i,
:to_io, :truncate, :tty?
def_delegators :@io, :binmode, :close, :close_read, :close_write,
:closed?, :external_encoding, :fcntl,
:fileno, :flush, :fsync, :internal_encoding,
:isatty, :pid, :pos, :pos=, :reopen,
:seek, :string, :sync, :sync=, :tell,
:truncate, :tty?

def binmode?
if @io.respond_to?(:binmode?)
@io.binmode?
else
false
end
end

def flock(*args)
raise NotImplementedError unless @io.respond_to?(:flock)
@io.flock(*args)
end

def ioctl(*args)
raise NotImplementedError unless @io.respond_to?(:ioctl)
@io.ioctl(*args)
end

def path
@io.path if @io.respond_to?(:path)
end

def stat(*args)
raise NotImplementedError unless @io.respond_to?(:stat)
@io.stat(*args)
end

def to_i
raise NotImplementedError unless @io.respond_to?(:to_i)
@io.to_i
end

def to_io
@io.respond_to?(:to_io) ? @io.to_io : @io
end

def eof?
begin
parser_enumerator.peek
false
rescue StopIteration
true
end
end
alias_method :eof, :eof?

# Rewinds the underlying IO object and resets CSV's lineno() counter.
def rewind
Expand Down Expand Up @@ -1145,7 +1205,7 @@ def <<(row)
# converted field or the field itself.
#
def convert(name = nil, &converter)
fields_converter.add_converter(name, &converter)
parser_fields_converter.add_converter(name, &converter)
end

#
Expand Down Expand Up @@ -1173,7 +1233,7 @@ def header_convert(name = nil, &converter)
# The data source must be open for reading.
#
def each(&block)
parser.parse(&block)
parser_enumerator.each(&block)
end

#
Expand Down Expand Up @@ -1204,9 +1264,8 @@ def header_row?
# The data source must be open for reading.
#
def shift
@parser_enumerator ||= parser.parse
begin
@parser_enumerator.next
parser_enumerator.next
rescue StopIteration
nil
end
Expand Down Expand Up @@ -1299,7 +1358,7 @@ def convert_fields(fields, headers = false)
if headers
header_fields_converter.convert(fields, nil, 0)
else
fields_converter.convert(fields, @headers, lineno)
parser_fields_converter.convert(fields, @headers, lineno)
end
end

Expand All @@ -1316,20 +1375,16 @@ def raw_encoding
end
end

def fields_converter
@fields_converter ||= build_fields_converter
def parser_fields_converter
@parser_fields_converter ||= build_parser_fields_converter
end

def build_fields_converter
def build_parser_fields_converter
specific_options = {
builtin_converters: Converters,
}
options = @base_fields_converter_options.merge(specific_options)
fields_converter = FieldsConverter.new(options)
normalize_converters(@initial_converters).each do |name, converter|
fields_converter.add_converter(name, &converter)
end
fields_converter
build_fields_converter(@initial_converters, options)
end

def header_fields_converter
Expand All @@ -1342,8 +1397,21 @@ def build_header_fields_converter
accept_nil: true,
}
options = @base_fields_converter_options.merge(specific_options)
build_fields_converter(@initial_header_converters, options)
end

def writer_fields_converter
@writer_fields_converter ||= build_writer_fields_converter
end

def build_writer_fields_converter
build_fields_converter(@initial_write_converters,
@write_fields_converter_options)
end

def build_fields_converter(initial_converters, options)
fields_converter = FieldsConverter.new(options)
normalize_converters(@initial_header_converters).each do |name, converter|
normalize_converters(initial_converters).each do |name, converter|
fields_converter.add_converter(name, &converter)
end
fields_converter
Expand All @@ -1354,16 +1422,21 @@ def parser
end

def parser_options
@parser_options.merge(fields_converter: fields_converter,
header_fields_converter: header_fields_converter)
@parser_options.merge(header_fields_converter: header_fields_converter,
fields_converter: parser_fields_converter)
end

def parser_enumerator
@parser_enumerator ||= parser.parse
end

def writer
@writer ||= Writer.new(@io, writer_options)
end

def writer_options
@writer_options.merge(header_fields_converter: header_fields_converter)
@writer_options.merge(header_fields_converter: header_fields_converter,
fields_converter: writer_fields_converter)
end
end

Expand Down
1 change: 1 addition & 0 deletions lib/csv/csv.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
"lib/csv.rb",
"lib/csv/core_ext/array.rb",
"lib/csv/core_ext/string.rb",
"lib/csv/delete_suffix.rb",
"lib/csv/fields_converter.rb",
"lib/csv/match_p.rb",
"lib/csv/parser.rb",
Expand Down
18 changes: 18 additions & 0 deletions lib/csv/delete_suffix.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

# This provides String#delete_suffix? for Ruby 2.4.
unless String.method_defined?(:delete_suffix)
class CSV
module DeleteSuffix
refine String do
def delete_suffix(suffix)
if end_with?(suffix)
self[0..(-(suffix.size + 1))]
else
self
end
end
end
end
end
end
Loading

0 comments on commit e3b6c7c

Please sign in to comment.