csv 3.3.6 → 3.3.7

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: d2a1d74b0246a143f03c2e786ea5484cb3e5aebe370453141a9d69539eae900b
4
- data.tar.gz: d8bb00a2e6842e28ca6b443e416f8f03186b098dcc81fde6ba5429eed138a755
3
+ metadata.gz: 405d44be299fb5493de237683e24cdbf6e29982e4378c1d01a82e2ad4bbb21a3
4
+ data.tar.gz: 28a53de42e2c35091f441a3d9a7a886afc30bad1c514ea26715d633ee8a00080
5
5
  SHA512:
6
- metadata.gz: 3d26ab99eb2708d787022832ca171def6ef4647183667be03831d9a91c5320545146adac9e5471e92637e6e905c27c06499739fc5e510541c026c15388b9e316
7
- data.tar.gz: f8e7b076653e2e3f04503db617e94f14c66064aa1a8ff790a342c018eb6c598eb84127b0818e0ed8f2688ec6404b163c80e6c58cbf7d3c9411583ad8cd2cd0bd
6
+ metadata.gz: 7cb81c8caf90a7673fd22fc24644b33fb171c9f7b60ea9e679612008912d7ae10150351001d2daea5afdc6360cbe486f454cc3b29387b9c514d232b0c68b5ad3
7
+ data.tar.gz: 72d77e22c654b26dc8fed349670d107169605119554f6b18cd79f7477bd7284f099560edc68bfba1561775fe8acb081fd1427aa6235c8540bc7cca93a7e7a397
data/NEWS.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # News
2
2
 
3
+ ## 3.3.7 - 2026-10-28
4
+
5
+ ### Improvements
6
+
7
+ * Improved documents.
8
+ * GH-376
9
+ * Patch by Oskar Eichler
10
+
11
+ * Improved error handling.
12
+ * GH-378
13
+ * GH-379
14
+ * Reported by sswander
15
+
16
+ ### Thanks
17
+
18
+ * Oskar Eichler
19
+
20
+ * sswander
21
+
3
22
  ## 3.3.6 - 2026-07-27
4
23
 
5
24
  ### Improvements
@@ -1,6 +1,6 @@
1
1
  ====== Option +quote_char+
2
2
 
3
- Specifies the character (\String of length 1) used used to quote fields
3
+ Specifies the character (\String of length 1) used to quote fields
4
4
  in both parsing and generating.
5
5
  This String will be transcoded into the data's \Encoding before use.
6
6
 
@@ -17,7 +17,7 @@ Without +write_headers+:
17
17
  csv.shift
18
18
  end # => ["foo", "0"]
19
19
 
20
- With +write_headers+":
20
+ With +write_headers+:
21
21
  CSV.open(file_path,'w',
22
22
  :write_headers => true,
23
23
  :headers => ['Name','Value']
@@ -31,9 +31,10 @@ the first row of the data is treated as a row of headers:
31
31
  bax,2
32
32
  EOT
33
33
  csv = CSV.new(str, headers: true)
34
- csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:2 col_sep:"," row_sep:"\n" quote_char:"\"" headers:["Name", "Count"]>
34
+ csv # => #<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:"," row_sep:"\n" quote_char:"\"" headers:true>
35
+ csv.headers # => true
36
+ csv.shift # => #<CSV::Row "Name":"foo" "Count":"0">
35
37
  csv.headers # => ["Name", "Count"]
36
- csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
37
38
 
38
39
  ---
39
40
 
@@ -46,7 +47,7 @@ If set to an \Array, the \Array elements are treated as headers:
46
47
  csv = CSV.new(str, headers: ['Name', 'Count'])
47
48
  csv
48
49
  csv.headers # => ["Name", "Count"]
49
- csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
50
+ csv.shift # => #<CSV::Row "Name":"foo" "Count":"0">
50
51
 
51
52
  ---
52
53
 
@@ -60,4 +61,4 @@ with the current +options+, and the returned \Array is treated as headers:
60
61
  csv = CSV.new(str, headers: 'Name,Count')
61
62
  csv
62
63
  csv.headers # => ["Name", "Count"]
63
- csv.shift # => #<CSV::Row "Name":"bar" "Count":"1">
64
+ csv.shift # => #<CSV::Row "Name":"foo" "Count":"0">
@@ -6,7 +6,7 @@ a line that contains a column separator is not considered to be blank.
6
6
  Default value:
7
7
  CSV::DEFAULT_OPTIONS.fetch(:skip_blanks) # => false
8
8
 
9
- See also option {skiplines}[#class-CSV-label-Option+skip_lines].
9
+ See also option {skip_lines}[#class-CSV-label-Option+skip_lines].
10
10
 
11
11
  For examples in this section:
12
12
  str = <<-EOT
@@ -2,8 +2,9 @@
2
2
 
3
3
  Specifies an object to use in identifying comment lines in the input that are to be ignored:
4
4
  * If a \Regexp, ignores lines that match it.
5
- * If a \String, converts it to a \Regexp, ignores lines that match it.
5
+ * If a \String, ignores lines that include it.
6
6
  * If +nil+, no lines are considered to be comments.
7
+ * Otherwise, the object must respond to +match+; ignores lines for which +match+ returns a truthy value.
7
8
 
8
9
  Default value:
9
10
  CSV::DEFAULT_OPTIONS.fetch(:skip_lines) # => nil
@@ -32,6 +33,7 @@ Using a \String:
32
33
 
33
34
  ---
34
35
 
35
- Raises an exception if given an object that is not a \Regexp, a \String, or +nil+:
36
+ Raises an exception if given an object that is not a \Regexp, a \String, +nil+,
37
+ or an object that responds to +match+:
36
38
  # Raises ArgumentError (:skip_lines has to respond to #match: 0)
37
39
  CSV.parse(str, skip_lines: 0)
@@ -16,7 +16,7 @@ All code snippets on this page assume that the following has been executed:
16
16
  - {Generating to a File}[#label-Generating+to+a+File]
17
17
  - {Recipe: Generate to File with Headers}[#label-Recipe-3A+Generate+to+File+with+Headers]
18
18
  - {Recipe: Generate to File Without Headers}[#label-Recipe-3A+Generate+to+File+Without+Headers]
19
- - {Generating to IO an Stream}[#label-Generating+to+an+IO+Stream]
19
+ - {Generating to an IO Stream}[#label-Generating+to+an+IO+Stream]
20
20
  - {Recipe: Generate to IO Stream with Headers}[#label-Recipe-3A+Generate+to+IO+Stream+with+Headers]
21
21
  - {Recipe: Generate to IO Stream Without Headers}[#label-Recipe-3A+Generate+to+IO+Stream+Without+Headers]
22
22
  - {Converting Fields}[#label-Converting+Fields]
@@ -71,11 +71,11 @@ that are to be generated:
71
71
 
72
72
  ==== Generating to a \File
73
73
 
74
- You can generate /CSV data to a \File, with or without headers.
74
+ You can generate \CSV data to a \File, with or without headers.
75
75
 
76
76
  ===== Recipe: Generate to \File with Headers
77
77
 
78
- Use class method CSV.open with option +headers+ generate to a \File.
78
+ Use class method CSV.open with option +headers+ to generate to a \File.
79
79
 
80
80
  This example uses method CSV#<< to append the rows
81
81
  that are to be generated:
@@ -105,7 +105,7 @@ that are to be generated:
105
105
 
106
106
  You can generate \CSV data to an \IO stream, with or without headers.
107
107
 
108
- ==== Recipe: Generate to \IO Stream with Headers
108
+ ===== Recipe: Generate to \IO Stream with Headers
109
109
 
110
110
  Use class method CSV.new with option +headers+ to generate \CSV data to an \IO stream:
111
111
  path = 't.csv'
@@ -187,7 +187,7 @@ For strict compliance, use option +:row_sep+ to specify row separator <tt>"\r\n"
187
187
  ===== Recipe: Generate Non-Compliant Row Separator
188
188
 
189
189
  For data with non-compliant row separators, use option +:row_sep+ with a different value:
190
- This example source uses semicolon (<tt>";'</tt>) as its row separator:
190
+ This example source uses semicolon (<tt>";"</tt>) as its row separator:
191
191
  output_string = CSV.generate('', row_sep: ";") do |csv|
192
192
  csv << ['Foo', 0]
193
193
  csv << ['Bar', 1]
@@ -223,11 +223,11 @@ This example source uses TAB (<tt>"\t"</tt>) as its column separator:
223
223
 
224
224
  ==== Quotes
225
225
 
226
- IFC 4180 allows most fields to be quoted or not.
226
+ RFC 4180 allows most fields to be quoted or not.
227
227
  By default, \CSV does not quote most fields.
228
228
 
229
229
  However, a field containing the current row separator, column separator,
230
- or quote character is automatically quoted, producing IFC 4180 compliance:
230
+ or quote character is automatically quoted, producing RFC 4180 compliance:
231
231
  # Field contains row separator.
232
232
  output_string = CSV.generate('') do |csv|
233
233
  row_sep = csv.row_sep
data/lib/csv/parser.rb CHANGED
@@ -978,63 +978,69 @@ class CSV
978
978
 
979
979
  def parse_quotable_loose(&block)
980
980
  @scanner.keep_start
981
- @scanner.each_line(@row_separator) do |line|
982
- if @skip_lines and skip_line?(line)
983
- @scanner.keep_drop
984
- @scanner.keep_start
985
- next
986
- end
987
- original_line = line
988
- line = line.delete_suffix(@row_separator)
989
-
990
- if line.empty?
991
- if @skip_blanks
981
+ kept = true
982
+ begin
983
+ @scanner.each_line(@row_separator) do |line|
984
+ if @skip_lines and skip_line?(line)
992
985
  @scanner.keep_drop
993
986
  @scanner.keep_start
994
987
  next
995
988
  end
996
- row = []
997
- quoted_fields = FieldsConverter::NO_QUOTED_FIELDS
998
- elsif line.include?(@cr) or line.include?(@lf)
999
- @scanner.keep_back
1000
- @parse_method = :parse_quotable_robust
1001
- return parse_quotable_robust(&block)
1002
- else
1003
- row = line.split(@split_column_separator, -1)
1004
- quoted_fields = []
1005
- n_columns = row.size
1006
- i = 0
1007
- while i < n_columns
1008
- column = row[i]
1009
- if column.empty?
1010
- quoted_fields << false
1011
- row[i] = nil
1012
- else
1013
- n_quotes = column.count(@quote_character)
1014
- if n_quotes.zero?
989
+ original_line = line
990
+ line = line.delete_suffix(@row_separator)
991
+
992
+ if line.empty?
993
+ if @skip_blanks
994
+ @scanner.keep_drop
995
+ @scanner.keep_start
996
+ next
997
+ end
998
+ row = []
999
+ quoted_fields = FieldsConverter::NO_QUOTED_FIELDS
1000
+ elsif line.include?(@cr) or line.include?(@lf)
1001
+ kept = false
1002
+ @scanner.keep_back
1003
+ @parse_method = :parse_quotable_robust
1004
+ return parse_quotable_robust(&block)
1005
+ else
1006
+ row = line.split(@split_column_separator, -1)
1007
+ quoted_fields = []
1008
+ n_columns = row.size
1009
+ i = 0
1010
+ while i < n_columns
1011
+ column = row[i]
1012
+ if column.empty?
1015
1013
  quoted_fields << false
1016
- # no quote
1017
- elsif n_quotes == 2 and
1018
- column.start_with?(@quote_character) and
1019
- column.end_with?(@quote_character)
1020
- quoted_fields << true
1021
- row[i] = column[1..-2]
1014
+ row[i] = nil
1022
1015
  else
1023
- @scanner.keep_back
1024
- @parse_method = :parse_quotable_robust
1025
- return parse_quotable_robust(&block)
1016
+ n_quotes = column.count(@quote_character)
1017
+ if n_quotes.zero?
1018
+ quoted_fields << false
1019
+ # no quote
1020
+ elsif n_quotes == 2 and
1021
+ column.start_with?(@quote_character) and
1022
+ column.end_with?(@quote_character)
1023
+ quoted_fields << true
1024
+ row[i] = column[1..-2]
1025
+ else
1026
+ kept = false
1027
+ @scanner.keep_back
1028
+ @parse_method = :parse_quotable_robust
1029
+ return parse_quotable_robust(&block)
1030
+ end
1031
+ validate_field_size(row[i])
1026
1032
  end
1027
- validate_field_size(row[i])
1033
+ i += 1
1028
1034
  end
1029
- i += 1
1030
1035
  end
1036
+ @scanner.keep_drop
1037
+ @scanner.keep_start
1038
+ @last_line = original_line
1039
+ emit_row(row, quoted_fields, &block)
1031
1040
  end
1032
- @scanner.keep_drop
1033
- @scanner.keep_start
1034
- @last_line = original_line
1035
- emit_row(row, quoted_fields, &block)
1041
+ ensure
1042
+ @scanner.keep_drop if kept
1036
1043
  end
1037
- @scanner.keep_drop
1038
1044
  end
1039
1045
 
1040
1046
  def parse_quotable_robust(&block)
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.6"
5
+ VERSION = "3.3.7"
6
6
  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.6
4
+ version: 3.3.7
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.6
74
+ changelog_uri: https://github.com/ruby/csv/releases/tag/v3.3.7
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: 4.0.16
91
+ rubygems_version: 4.0.20
92
92
  specification_version: 4
93
93
  summary: CSV Reading and Writing
94
94
  test_files: []