csv 3.3.5 → 3.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f77fe04292e17f436f9c5838bb3cbd6aa4297b807478c127321323fc4ad27c28
4
- data.tar.gz: 63ecf6ce80dcfa3b7eb24d0f3947455eb223d1c44a32b58b4d084b29efad8961
3
+ metadata.gz: d2a1d74b0246a143f03c2e786ea5484cb3e5aebe370453141a9d69539eae900b
4
+ data.tar.gz: d8bb00a2e6842e28ca6b443e416f8f03186b098dcc81fde6ba5429eed138a755
5
5
  SHA512:
6
- metadata.gz: f9ffbf24700b4ab0eb35a2cb8086e82e36825558c14b646ae00374cb3e80f7195ebb3c9ac08d11b011ffa0cc10ae607a79e24d68b793b54c50d2dd1e8bd5ec16
7
- data.tar.gz: 628a0b0c0963d69686845172d84fbbdf1cebb9fc6839e052054f9e7c112bbcb96743a63e8cf5d1a6df8a9299f03ab3b8d81a9b814fb6d0f222e444b9f0282a5d
6
+ metadata.gz: 3d26ab99eb2708d787022832ca171def6ef4647183667be03831d9a91c5320545146adac9e5471e92637e6e905c27c06499739fc5e510541c026c15388b9e316
7
+ data.tar.gz: f8e7b076653e2e3f04503db617e94f14c66064aa1a8ff790a342c018eb6c598eb84127b0818e0ed8f2688ec6404b163c80e6c58cbf7d3c9411583ad8cd2cd0bd
data/NEWS.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # News
2
2
 
3
+ ## 3.3.6 - 2026-07-27
4
+
5
+ ### Improvements
6
+
7
+ * `CSV::Row#to_h`: Added support for block.
8
+ * GH-356
9
+ * Patch by Vlad
10
+
11
+ * Ensured using `path_or_io` for parameter name that accepts path or
12
+ IO.
13
+ * GH-358
14
+ * Patch by Yuto Urushima
15
+
16
+ * Changed to not using enumerator in `CSV`.
17
+ * GH-361
18
+ * GH-363
19
+ * Reported by Cas Donoghue
20
+
21
+ ### Fixes
22
+
23
+ * Fixed a bug that `\r` in unquoted fields are rejected when row
24
+ separator doesn't contain `\r`
25
+ * GH-60
26
+ * GH-346
27
+ * Patch by Jas
28
+
29
+ * Fixed a typo in documentation.
30
+ * GH-350
31
+ * Patch by tmr111116
32
+
33
+ ### Thanks
34
+
35
+ * Jas
36
+ * tmr111116
37
+ * Vlad
38
+ * Yuto Urushima
39
+ * Cas Donoghue
40
+
3
41
  ## 3.3.5 - 2025-06-01
4
42
 
5
43
  ### Improvements
@@ -10,7 +48,7 @@
10
48
 
11
49
  ### Thanks
12
50
 
13
- * Petrik de Heus
51
+ * Petrik de Heus
14
52
 
15
53
  ## 3.3.4 - 2025-04-13
16
54
 
data/README.md CHANGED
@@ -52,4 +52,4 @@ Please do not submit issues and PRs that aim to introduce RuboCop in this reposi
52
52
 
53
53
  The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause).
54
54
 
55
- See LICENSE.txt for details.
55
+ See [LICENSE.txt](LICENSE.txt) for details.
data/lib/csv/parser.rb CHANGED
@@ -181,7 +181,7 @@ class CSV
181
181
  end
182
182
 
183
183
  def eos?
184
- @scanner.eos?
184
+ @last_scanner and @scanner.eos?
185
185
  end
186
186
 
187
187
  def keep_start
@@ -444,6 +444,11 @@ class CSV
444
444
  end
445
445
  end
446
446
 
447
+ def eof?
448
+ return false if @scanner.nil?
449
+ @scanner.eos?
450
+ end
451
+
447
452
  def use_headers?
448
453
  @use_headers
449
454
  end
@@ -675,7 +680,7 @@ class CSV
675
680
  def prepare_unquoted
676
681
  return if @quote_character.nil?
677
682
 
678
- no_unquoted_values = "\r\n".encode(@encoding)
683
+ no_unquoted_values = Regexp.escape(@row_separator).encode(@encoding)
679
684
  no_unquoted_values << @escaped_first_column_separator
680
685
  unless @liberal_parsing
681
686
  no_unquoted_values << @escaped_quote_character
data/lib/csv/row.rb CHANGED
@@ -637,6 +637,7 @@ class CSV
637
637
 
638
638
  # :call-seq:
639
639
  # row.to_h -> hash
640
+ # row.to_h {|key, value| ... } -> hash
640
641
  #
641
642
  # Returns the new \Hash formed by adding each header-value pair in +self+
642
643
  # as a key-value pair in the \Hash.
@@ -650,11 +651,34 @@ class CSV
650
651
  # table = CSV.parse(source, headers: true)
651
652
  # row = table[0]
652
653
  # row.to_h # => {"Name"=>"Foo"}
654
+ #
655
+ # If a block is given, will call it with (key, value) arguments and use result as a hash entry:
656
+ # source = "Name,Value\nfoo,1\nbar,2\nbaz,3\n"
657
+ # table = CSV.parse(source, headers: true)
658
+ # row = table[0]
659
+ # row.to_h { |key, value| [key, "#{key}-#{value}"] } # => {"Name"=>"Name-foo", "Value"=>"Value-1"}
653
660
  def to_h
654
661
  hash = {}
655
- each do |key, _value|
656
- hash[key] = self[key] unless hash.key?(key)
662
+
663
+ if block_given?
664
+ each do |key, _value|
665
+ result = yield(key, self[key])
666
+ result_array = Array.try_convert(result)
667
+ raise TypeError, "wrong element type #{result.class} (expected array)" if result_array.nil?
668
+ raise ArgumentError, "wrong array length (expected 2, was #{result_array.size})" unless result_array.size == 2
669
+
670
+ key, value = result_array
671
+ next if hash.key?(key)
672
+
673
+ key.freeze if key.is_a?(String) && !key.frozen?
674
+ hash[key] = value
675
+ end
676
+ else
677
+ each do |key, _value|
678
+ hash[key] = self[key] unless hash.key?(key)
679
+ end
657
680
  end
681
+
658
682
  hash
659
683
  end
660
684
  alias_method :to_hash, :to_h
data/lib/csv/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  class CSV
4
4
  # The version of the installed library.
5
- VERSION = "3.3.5"
5
+ VERSION = "3.3.6"
6
6
  end
data/lib/csv.rb CHANGED
@@ -1377,7 +1377,7 @@ class CSV
1377
1377
  # * Arguments <tt>**options</tt> must be keyword options.
1378
1378
  # See {Options for Parsing}[#class-CSV-label-Options+for+Parsing].
1379
1379
  # * This method optionally accepts an additional <tt>:encoding</tt> option
1380
- # that you can use to specify the Encoding of the data read from +path+ or +io+.
1380
+ # that you can use to specify the Encoding of the data read from +path_or_io+.
1381
1381
  # You must provide this unless your data is in the encoding
1382
1382
  # given by <tt>Encoding::default_external</tt>.
1383
1383
  # Parsing will use this to determine how to parse the data.
@@ -1386,9 +1386,9 @@ class CSV
1386
1386
  # encoding: 'UTF-32BE:UTF-8'
1387
1387
  # would read +UTF-32BE+ data from the file
1388
1388
  # but transcode it to +UTF-8+ before parsing.
1389
- def foreach(path, mode="r", **options, &block)
1390
- return to_enum(__method__, path, mode, **options) unless block_given?
1391
- open(path, mode, **options) do |csv|
1389
+ def foreach(path_or_io, mode="r", **options, &block)
1390
+ return to_enum(__method__, path_or_io, mode, **options) unless block_given?
1391
+ open(path_or_io, mode, **options) do |csv|
1392
1392
  csv.each(&block)
1393
1393
  end
1394
1394
  end
@@ -1582,7 +1582,7 @@ class CSV
1582
1582
  # * Arguments <tt>**options</tt> must be keyword options.
1583
1583
  # See {Options for Generating}[#class-CSV-label-Options+for+Generating].
1584
1584
  # * This method optionally accepts an additional <tt>:encoding</tt> option
1585
- # that you can use to specify the Encoding of the data read from +path+ or +io+.
1585
+ # that you can use to specify the Encoding of the data read from +path_or_io+.
1586
1586
  # You must provide this unless your data is in the encoding
1587
1587
  # given by <tt>Encoding::default_external</tt>.
1588
1588
  # Parsing will use this to determine how to parse the data.
@@ -1644,11 +1644,11 @@ class CSV
1644
1644
  # Raises an exception if the argument is not a \String object or \IO object:
1645
1645
  # # Raises TypeError (no implicit conversion of Symbol into String)
1646
1646
  # CSV.open(:foo)
1647
- def open(filename_or_io, mode="r", **options)
1647
+ def open(path_or_io, mode="r", **options)
1648
1648
  # wrap a File opened with the remaining +args+ with no newline
1649
1649
  # decorator
1650
1650
  file_opts = {}
1651
- may_enable_bom_detection_automatically(filename_or_io,
1651
+ may_enable_bom_detection_automatically(path_or_io,
1652
1652
  mode,
1653
1653
  options,
1654
1654
  file_opts)
@@ -1661,11 +1661,11 @@ class CSV
1661
1661
  options.delete(:replace)
1662
1662
  options.delete_if {|k, _| /newline\z/.match?(k)}
1663
1663
 
1664
- if filename_or_io.is_a?(StringIO)
1665
- f = create_stringio(filename_or_io.string, mode, **file_opts)
1664
+ if path_or_io.is_a?(StringIO)
1665
+ f = create_stringio(path_or_io.string, mode, **file_opts)
1666
1666
  else
1667
1667
  begin
1668
- f = File.open(filename_or_io, mode, **file_opts)
1668
+ f = File.open(path_or_io, mode, **file_opts)
1669
1669
  rescue ArgumentError => e
1670
1670
  raise unless /needs binmode/.match?(e.message) and mode == "r"
1671
1671
  mode = "rb"
@@ -1901,10 +1901,10 @@ class CSV
1901
1901
 
1902
1902
  #
1903
1903
  # :call-seq:
1904
- # read(source, **options) -> array_of_arrays
1905
- # read(source, headers: true, **options) -> csv_table
1904
+ # read(path_or_io, **options) -> array_of_arrays
1905
+ # read(path_or_io, headers: true, **options) -> csv_table
1906
1906
  #
1907
- # Opens the given +source+ with the given +options+ (see CSV.open),
1907
+ # Opens the given +path_or_io+ with the given +options+ (see CSV.open),
1908
1908
  # reads the source (see CSV#read), and returns the result,
1909
1909
  # which will be either an \Array of Arrays or a CSV::Table.
1910
1910
  #
@@ -1919,22 +1919,22 @@ class CSV
1919
1919
  # path = 't.csv'
1920
1920
  # File.write(path, string)
1921
1921
  # CSV.read(path, headers: true) # => #<CSV::Table mode:col_or_row row_count:4>
1922
- def read(path, **options)
1923
- open(path, **options) { |csv| csv.read }
1922
+ def read(path_or_io, **options)
1923
+ open(path_or_io, **options) { |csv| csv.read }
1924
1924
  end
1925
1925
 
1926
1926
  # :call-seq:
1927
- # CSV.readlines(source, **options)
1927
+ # CSV.readlines(path_or_io, **options)
1928
1928
  #
1929
1929
  # Alias for CSV.read.
1930
- def readlines(path, **options)
1931
- read(path, **options)
1930
+ def readlines(path_or_io, **options)
1931
+ read(path_or_io, **options)
1932
1932
  end
1933
1933
 
1934
1934
  # :call-seq:
1935
- # CSV.table(source, **options)
1935
+ # CSV.table(path_or_io, **options)
1936
1936
  #
1937
- # Calls CSV.read with +source+, +options+, and certain default options:
1937
+ # Calls CSV.read with +path_or_io+, +options+, and certain default options:
1938
1938
  # - +headers+: +true+
1939
1939
  # - +converters+: +:numeric+
1940
1940
  # - +header_converters+: +:symbol+
@@ -1946,25 +1946,25 @@ class CSV
1946
1946
  # path = 't.csv'
1947
1947
  # File.write(path, string)
1948
1948
  # CSV.table(path) # => #<CSV::Table mode:col_or_row row_count:4>
1949
- def table(path, **options)
1949
+ def table(path_or_io, **options)
1950
1950
  default_options = {
1951
1951
  headers: true,
1952
1952
  converters: :numeric,
1953
1953
  header_converters: :symbol,
1954
1954
  }
1955
1955
  options = default_options.merge(options)
1956
- read(path, **options)
1956
+ read(path_or_io, **options)
1957
1957
  end
1958
1958
 
1959
1959
  ON_WINDOWS = /mingw|mswin/.match?(RUBY_PLATFORM)
1960
1960
  private_constant :ON_WINDOWS
1961
1961
 
1962
1962
  private
1963
- def may_enable_bom_detection_automatically(filename_or_io,
1963
+ def may_enable_bom_detection_automatically(path_or_io,
1964
1964
  mode,
1965
1965
  options,
1966
1966
  file_opts)
1967
- if filename_or_io.is_a?(StringIO)
1967
+ if path_or_io.is_a?(StringIO)
1968
1968
  # Support to StringIO was dropped for Ruby 2.6 and earlier without BOM support:
1969
1969
  # https://github.com/ruby/stringio/pull/47
1970
1970
  return if RUBY_VERSION < "2.7"
@@ -2110,8 +2110,6 @@ class CSV
2110
2110
  strip: strip,
2111
2111
  }
2112
2112
  @parser = nil
2113
- @parser_enumerator = nil
2114
- @eof_error = nil
2115
2113
 
2116
2114
  @writer_options = {
2117
2115
  encoding: @encoding,
@@ -2327,7 +2325,7 @@ class CSV
2327
2325
  attr_reader :encoding
2328
2326
 
2329
2327
  # :call-seq:
2330
- # csv.line_no -> integer
2328
+ # csv.lineno -> integer
2331
2329
  #
2332
2330
  # Returns the count of the rows parsed or generated.
2333
2331
  #
@@ -2430,24 +2428,13 @@ class CSV
2430
2428
  end
2431
2429
 
2432
2430
  def eof?
2433
- return false if @eof_error
2434
- begin
2435
- parser_enumerator.peek
2436
- false
2437
- rescue MalformedCSVError => error
2438
- @eof_error = error
2439
- false
2440
- rescue StopIteration
2441
- true
2442
- end
2431
+ parser.eof?
2443
2432
  end
2444
2433
  alias_method :eof, :eof?
2445
2434
 
2446
2435
  # Rewinds the underlying IO object and resets CSV's lineno() counter.
2447
2436
  def rewind
2448
2437
  @parser = nil
2449
- @parser_enumerator = nil
2450
- @eof_error = nil
2451
2438
  @writer.rewind if @writer
2452
2439
  @io.rewind
2453
2440
  end
@@ -2687,13 +2674,7 @@ class CSV
2687
2674
  # p row
2688
2675
  # end
2689
2676
  def each(&block)
2690
- return to_enum(__method__) unless block_given?
2691
- begin
2692
- while true
2693
- yield(parser_enumerator.next)
2694
- end
2695
- rescue StopIteration
2696
- end
2677
+ parser.parse(&block)
2697
2678
  end
2698
2679
 
2699
2680
  # :call-seq:
@@ -2801,15 +2782,10 @@ class CSV
2801
2782
  # # Raises IOError (not opened for reading)
2802
2783
  # csv.shift
2803
2784
  def shift
2804
- if @eof_error
2805
- eof_error, @eof_error = @eof_error, nil
2806
- raise eof_error
2807
- end
2808
- begin
2809
- parser_enumerator.next
2810
- rescue StopIteration
2811
- nil
2785
+ parser.parse do |row|
2786
+ return row
2812
2787
  end
2788
+ nil
2813
2789
  end
2814
2790
  alias_method :gets, :shift
2815
2791
  alias_method :readline, :shift
@@ -2971,10 +2947,6 @@ class CSV
2971
2947
  fields_converter: parser_fields_converter)
2972
2948
  end
2973
2949
 
2974
- def parser_enumerator
2975
- @parser_enumerator ||= parser.parse
2976
- end
2977
-
2978
2950
  def writer
2979
2951
  @writer ||= Writer.new(@io, writer_options)
2980
2952
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.5
4
+ version: 3.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Edward Gray II
@@ -71,7 +71,7 @@ licenses:
71
71
  - Ruby
72
72
  - BSD-2-Clause
73
73
  metadata:
74
- changelog_uri: https://github.com/ruby/csv/releases/tag/v3.3.5
74
+ changelog_uri: https://github.com/ruby/csv/releases/tag/v3.3.6
75
75
  rdoc_options:
76
76
  - "--main"
77
77
  - README.md
@@ -88,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  requirements: []
91
- rubygems_version: 3.6.7
91
+ rubygems_version: 4.0.16
92
92
  specification_version: 4
93
93
  summary: CSV Reading and Writing
94
94
  test_files: []