summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/csv.rb21
-rw-r--r--lib/csv/csv.gemspec2
-rw-r--r--lib/csv/parser.rb11
-rw-r--r--test/csv/interface/test_read.rb32
-rw-r--r--test/csv/interface/test_write.rb34
5 files changed, 94 insertions, 6 deletions
diff --git a/lib/csv.rb b/lib/csv.rb
index 87c3a4be31..42e99435cb 100644
--- a/lib/csv.rb
+++ b/lib/csv.rb
@@ -536,6 +536,14 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
#
# There is no such storage structure for write headers.
#
+# In order for the parsing methods to access stored converters in non-main-Ractors, the
+# storage structure must be made shareable first.
+# Therefore, <tt>Ractor.make_shareable(CSV::Converters)</tt> and
+# <tt>Ractor.make_shareable(CSV::HeaderConverters)</tt> must be called before the creation
+# of Ractors that use the converters stored in these structures. (Since making the storage
+# structures shareable involves freezing them, any custom converters that are to be used
+# must be added first.)
+#
# ===== Converter Lists
#
# A _converter_ _list_ is an \Array that may include any assortment of:
@@ -908,6 +916,7 @@ class CSV
gsub(/\s+/, "_").to_sym
}
}
+
# Default values for method options.
DEFAULT_OPTIONS = {
# For both parsing and generating.
@@ -946,6 +955,8 @@ class CSV
# Creates or retrieves cached \CSV objects.
# For arguments and options, see CSV.new.
#
+ # This API is not Ractor-safe.
+ #
# ---
#
# With no block given, returns a \CSV object.
@@ -1873,6 +1884,10 @@ class CSV
# csv.converters # => [:integer]
# csv.convert(proc {|x| x.to_s })
# csv.converters
+ #
+ # Notes that you need to call
+ # +Ractor.make_shareable(CSV::Converters)+ on the main Ractor to use
+ # this method.
def converters
parser_fields_converter.map do |converter|
name = Converters.rassoc(converter)
@@ -1935,6 +1950,10 @@ class CSV
# Returns an \Array containing header converters; used for parsing;
# see {Header Converters}[#class-CSV-label-Header+Converters]:
# CSV.new('').header_converters # => []
+ #
+ # Notes that you need to call
+ # +Ractor.make_shareable(CSV::HeaderConverters)+ on the main Ractor
+ # to use this method.
def header_converters
header_fields_converter.map do |converter|
name = HeaderConverters.rassoc(converter)
@@ -2655,6 +2674,8 @@ end
# io = StringIO.new
# CSV(io, col_sep: ";") { |csv| csv << ["a", "b", "c"] }
#
+# This API is not Ractor-safe.
+#
def CSV(*args, **options, &block)
CSV.instance(*args, **options, &block)
end
diff --git a/lib/csv/csv.gemspec b/lib/csv/csv.gemspec
index 6948e9a72f..11c5b0f2a6 100644
--- a/lib/csv/csv.gemspec
+++ b/lib/csv/csv.gemspec
@@ -60,5 +60,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "bundler"
spec.add_development_dependency "rake"
spec.add_development_dependency "benchmark_driver"
- spec.add_development_dependency "test-unit", ">= 3.4.3"
+ spec.add_development_dependency "test-unit", ">= 3.4.8"
end
diff --git a/lib/csv/parser.rb b/lib/csv/parser.rb
index 0d8a157fd7..3334acfbdd 100644
--- a/lib/csv/parser.rb
+++ b/lib/csv/parser.rb
@@ -480,9 +480,9 @@ class CSV
begin
StringScanner.new("x").scan("x")
rescue TypeError
- @@string_scanner_scan_accept_string = false
+ STRING_SCANNER_SCAN_ACCEPT_STRING = false
else
- @@string_scanner_scan_accept_string = true
+ STRING_SCANNER_SCAN_ACCEPT_STRING = true
end
def prepare_separators
@@ -506,7 +506,7 @@ class CSV
@first_column_separators = Regexp.new(@escaped_first_column_separator +
"+".encode(@encoding))
else
- if @@string_scanner_scan_accept_string
+ if STRING_SCANNER_SCAN_ACCEPT_STRING
@column_end = @column_separator
else
@column_end = Regexp.new(@escaped_column_separator)
@@ -725,6 +725,8 @@ class CSV
end
end
+ SCANNER_TEST_CHUNK_SIZE =
+ Integer((ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"), 10)
def build_scanner
inputs = @samples.collect do |sample|
UnoptimizedStringIO.new(sample)
@@ -734,10 +736,9 @@ class CSV
else
inputs << @input
end
- chunk_size = ENV["CSV_PARSER_SCANNER_TEST_CHUNK_SIZE"] || "1"
InputsScanner.new(inputs,
@encoding,
- chunk_size: Integer(chunk_size, 10))
+ chunk_size: SCANNER_TEST_CHUNK_SIZE)
end
else
def build_scanner
diff --git a/test/csv/interface/test_read.rb b/test/csv/interface/test_read.rb
index b86c54fc9f..d73622d554 100644
--- a/test/csv/interface/test_read.rb
+++ b/test/csv/interface/test_read.rb
@@ -32,6 +32,24 @@ class TestCSVInterfaceRead < Test::Unit::TestCase
assert_equal(@rows, rows)
end
+ if respond_to?(:ractor)
+ ractor
+ def test_foreach_in_ractor
+ ractor = Ractor.new(@input.path) do |path|
+ rows = []
+ CSV.foreach(path, col_sep: "\t", row_sep: "\r\n").each do |row|
+ rows << row
+ end
+ rows
+ end
+ rows = [
+ ["1", "2", "3"],
+ ["4", "5"],
+ ]
+ assert_equal(rows, ractor.take)
+ end
+ end
+
def test_foreach_mode
rows = []
CSV.foreach(@input.path, "r", col_sep: "\t", row_sep: "\r\n").each do |row|
@@ -240,6 +258,20 @@ class TestCSVInterfaceRead < Test::Unit::TestCase
CSV.read(@input.path, col_sep: "\t", row_sep: "\r\n"))
end
+ if respond_to?(:ractor)
+ ractor
+ def test_read_in_ractor
+ ractor = Ractor.new(@input.path) do |path|
+ CSV.read(path, col_sep: "\t", row_sep: "\r\n")
+ end
+ rows = [
+ ["1", "2", "3"],
+ ["4", "5"],
+ ]
+ assert_equal(rows, ractor.take)
+ end
+ end
+
def test_readlines
assert_equal(@rows,
CSV.readlines(@input.path, col_sep: "\t", row_sep: "\r\n"))
diff --git a/test/csv/interface/test_write.rb b/test/csv/interface/test_write.rb
index 8650ecd624..02c2c5c5ce 100644
--- a/test/csv/interface/test_write.rb
+++ b/test/csv/interface/test_write.rb
@@ -25,6 +25,21 @@ class TestCSVInterfaceWrite < Test::Unit::TestCase
CSV
end
+ if respond_to?(:ractor)
+ ractor
+ def test_generate_default_in_ractor
+ ractor = Ractor.new do
+ CSV.generate do |csv|
+ csv << [1, 2, 3] << [4, nil, 5]
+ end
+ end
+ assert_equal(<<-CSV, ractor.take)
+1,2,3
+4,,5
+ CSV
+ end
+ end
+
def test_generate_append
csv_text = <<-CSV
1,2,3
@@ -101,6 +116,25 @@ a,b,c
CSV
end
+
+ if respond_to?(:ractor)
+ ractor
+ def test_append_row_in_ractor
+ ractor = Ractor.new(@output.path) do |path|
+ CSV.open(path, "wb") do |csv|
+ csv <<
+ CSV::Row.new([], ["1", "2", "3"]) <<
+ CSV::Row.new([], ["a", "b", "c"])
+ end
+ end
+ ractor.take
+ assert_equal(<<-CSV, File.read(@output.path, mode: "rb"))
+1,2,3
+a,b,c
+ CSV
+ end
+ end
+
def test_append_hash
CSV.open(@output.path, "wb", headers: true) do |csv|
csv << [:a, :b, :c]