csv 3.0.0 → 3.0.1

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: 878c0fd11ecaed5d4fb2ac420068c42b707a721144062ba7fe623002a13478be
4
- data.tar.gz: 007c48a609bcef16dba46b8f7aca667dbdcd3cb24f05a6b7f2470726dcfeea17
3
+ metadata.gz: f6ad673a6db13541d439c4798f677ae19e118fb031411b8434ec4534bafc47a9
4
+ data.tar.gz: 427070352e63b901d410a70eba6f7073103bc6cbe8e57f7e161e01003ae1598c
5
5
  SHA512:
6
- metadata.gz: 4daffd50dd28082465a243e17f77eeb8ccd1c6727c9094eeb3f0fcaaa05426949f7e3103fa84fdad87e27e1e493365f13d4491000d8ac61a006094ff2be2602a
7
- data.tar.gz: adc33341e6dd75c8d8ab343343fd8d455b7e048e081a2e22992f54397c42b3a70a4c78561fc871324b4424ed562a1fc9e59da4a12a56ff7ade2d3161195c3deb
6
+ metadata.gz: 3d2c69c8b784d79149dfd5999d2c937e037f83ccc806bd4fe5a674b913518ab4be4a13911e2ec3f5590c45dc6ebdd346eed6f85be253fad6dae40cd9eb0cb704
7
+ data.tar.gz: 25a0fee13c07fb870831c0b59ea88c9df6844ccf63810925d7ed437000c220b83a212781d5d615529ef487bd3ffdbd90a13b293475e8a4074a2feb249258b156
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # CSV
2
2
 
3
3
  [![Build Status](https://travis-ci.org/ruby/csv.svg?branch=master)](https://travis-ci.org/ruby/csv)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/321fa39e510a0abd0369/test_coverage)](https://codeclimate.com/github/ruby/csv/test_coverage)
4
5
 
5
6
  This library provides a complete interface to CSV files and data. It offers tools to enable you to read and write to and from Strings or IO objects, as needed.
6
7
 
data/lib/csv.rb CHANGED
@@ -141,7 +141,7 @@ end
141
141
  # There are several specialized class methods for one-statement reading or writing,
142
142
  # described in the Specialized Methods section.
143
143
  #
144
- # If a String passed into ::new, it is internally wrapped into a StringIO object.
144
+ # If a String is passed into ::new, it is internally wrapped into a StringIO object.
145
145
  #
146
146
  # +options+ can be used for specifying the particular CSV flavor (column
147
147
  # separators, row separators, value quoting and so on), and for data conversion,
@@ -890,8 +890,12 @@ class CSV
890
890
  # attempt to parse input not conformant
891
891
  # with RFC 4180, such as double quotes
892
892
  # in unquoted fields.
893
- # <b><tt>:nil_value</tt></b>:: TODO: WRITE ME.
894
- # <b><tt>:empty_value</tt></b>:: TODO: WRITE ME.
893
+ # <b><tt>:nil_value</tt></b>:: When set an object, any values of an
894
+ # empty field are replaced by the set
895
+ # object, not nil.
896
+ # <b><tt>:empty_value</tt></b>:: When set an object, any values of a
897
+ # blank string field is replaced by
898
+ # the set object.
895
899
  #
896
900
  # See CSV::DEFAULT_OPTIONS for the default settings.
897
901
  #
@@ -908,6 +912,7 @@ class CSV
908
912
 
909
913
  # create the IO object we will read from
910
914
  @io = data.is_a?(String) ? StringIO.new(data) : data
915
+ @prefix_io = nil # cache for input data possibly read by init_separators
911
916
  @encoding = determine_encoding(encoding, internal_encoding)
912
917
  #
913
918
  # prepare for building safe regular expressions in the target encoding,
@@ -1156,7 +1161,7 @@ class CSV
1156
1161
  def read
1157
1162
  rows = to_a
1158
1163
  if @use_headers
1159
- Table.new(rows)
1164
+ Table.new(rows, headers: @headers)
1160
1165
  else
1161
1166
  rows
1162
1167
  end
@@ -1200,8 +1205,14 @@ class CSV
1200
1205
 
1201
1206
  loop do
1202
1207
  # add another read to the line
1203
- unless parse = @io.gets(@row_sep)
1204
- return nil
1208
+ if @prefix_io
1209
+ parse = @prefix_io.gets(@row_sep)
1210
+ if @prefix_io.eof?
1211
+ parse << (@io.gets(@row_sep) || "") unless parse.end_with?(@row_sep)
1212
+ @prefix_io = nil # avoid having to test @prefix_io.eof? in main code path
1213
+ end
1214
+ else
1215
+ return nil unless parse = @io.gets(@row_sep)
1205
1216
  end
1206
1217
 
1207
1218
  if in_extended_col
@@ -1311,7 +1322,7 @@ class CSV
1311
1322
 
1312
1323
  if in_extended_col
1313
1324
  # if we're at eof?(), a quoted field wasn't closed...
1314
- if @io.eof?
1325
+ if @io.eof? and !@prefix_io
1315
1326
  raise MalformedCSVError.new("Unclosed quoted field",
1316
1327
  lineno + 1)
1317
1328
  elsif @field_size_limit and csv.last.size >= @field_size_limit
@@ -1433,68 +1444,62 @@ class CSV
1433
1444
  # (not fully encoding safe)
1434
1445
  #
1435
1446
  if @row_sep == :auto
1436
- if [ARGF, STDIN, STDOUT, STDERR].include?(@io) or
1437
- (defined?(Zlib) and @io.class == Zlib::GzipWriter)
1438
- @row_sep = $INPUT_RECORD_SEPARATOR
1439
- else
1440
- begin
1447
+ saved_prefix = [] # sample chunks to be reprocessed later
1448
+ begin
1449
+ while @row_sep == :auto && @io.respond_to?(:gets)
1441
1450
  #
1442
- # remember where we were (pos() will raise an exception if @io is pipe
1443
- # or not opened for reading)
1451
+ # if we run out of data, it's probably a single line
1452
+ # (ensure will set default value)
1444
1453
  #
1445
- saved_pos = @io.pos
1446
- while @row_sep == :auto
1447
- #
1448
- # if we run out of data, it's probably a single line
1449
- # (ensure will set default value)
1450
- #
1451
- break unless sample = @io.gets(nil, 1024)
1452
-
1453
- cr = encode_str("\r")
1454
- lf = encode_str("\n")
1455
- # extend sample if we're unsure of the line ending
1456
- if sample.end_with?(cr)
1457
- sample << (@io.gets(nil, 1) || "")
1458
- end
1454
+ break unless sample = @io.gets(nil, 1024)
1455
+
1456
+ cr = encode_str("\r")
1457
+ lf = encode_str("\n")
1458
+ # extend sample if we're unsure of the line ending
1459
+ if sample.end_with?(cr)
1460
+ sample << (@io.gets(nil, 1) || "")
1461
+ end
1459
1462
 
1460
- # try to find a standard separator
1461
- sample.each_char.each_cons(2) do |char, next_char|
1462
- case char
1463
- when cr
1464
- if next_char == lf
1465
- @row_sep = encode_str("\r\n")
1466
- else
1467
- @row_sep = cr
1468
- end
1469
- break
1470
- when lf
1471
- @row_sep = lf
1472
- break
1463
+ saved_prefix << sample
1464
+
1465
+ # try to find a standard separator
1466
+ last_char = nil
1467
+ sample.each_char.each_cons(2) do |char, next_char|
1468
+ last_char = next_char
1469
+ case char
1470
+ when cr
1471
+ if next_char == lf
1472
+ @row_sep = encode_str("\r\n")
1473
+ else
1474
+ @row_sep = cr
1473
1475
  end
1476
+ break
1477
+ when lf
1478
+ @row_sep = lf
1479
+ break
1474
1480
  end
1475
1481
  end
1476
-
1477
- # tricky seek() clone to work around GzipReader's lack of seek()
1478
- @io.rewind
1479
- # reset back to the remembered position
1480
- while saved_pos > 1024 # avoid loading a lot of data into memory
1481
- @io.read(1024)
1482
- saved_pos -= 1024
1482
+ if @row_sep == :auto
1483
+ case last_char
1484
+ when cr
1485
+ @row_sep = cr
1486
+ when lf
1487
+ @row_sep = lf
1488
+ end
1483
1489
  end
1484
- @io.read(saved_pos) if saved_pos.nonzero?
1485
- rescue IOError # not opened for reading
1486
- # do nothing: ensure will set default
1487
- rescue NoMethodError # Zlib::GzipWriter doesn't have some IO methods
1488
- # do nothing: ensure will set default
1489
- rescue SystemCallError # pipe
1490
- # do nothing: ensure will set default
1491
- ensure
1492
- #
1493
- # set default if we failed to detect
1494
- # (stream not opened for reading, a pipe, or a single line of data)
1495
- #
1496
- @row_sep = $INPUT_RECORD_SEPARATOR if @row_sep == :auto
1497
1490
  end
1491
+ rescue IOError
1492
+ # do nothing: ensure will set default
1493
+ ensure
1494
+ #
1495
+ # set default if we failed to detect
1496
+ # (stream not opened for reading or a single line of data)
1497
+ #
1498
+ @row_sep = $INPUT_RECORD_SEPARATOR if @row_sep == :auto
1499
+
1500
+ # save sampled input for later parsing (but only if there is some!)
1501
+ saved_prefix = saved_prefix.join('')
1502
+ @prefix_io = StringIO.new(saved_prefix) unless saved_prefix.empty?
1498
1503
  end
1499
1504
  end
1500
1505
  @row_sep = @row_sep.to_s.encode(@encoding)
@@ -1743,8 +1748,6 @@ class CSV
1743
1748
  def raw_encoding(default = Encoding::ASCII_8BIT)
1744
1749
  if @io.respond_to? :internal_encoding
1745
1750
  @io.internal_encoding || @io.external_encoding
1746
- elsif @io.is_a? StringIO
1747
- @io.string.encoding
1748
1751
  elsif @io.respond_to? :encoding
1749
1752
  @io.encoding
1750
1753
  else
@@ -48,6 +48,11 @@ class CSV
48
48
  extend Forwardable
49
49
  def_delegators :@row, :empty?, :length, :size
50
50
 
51
+ def initialize_copy(other)
52
+ super
53
+ @row = @row.dup
54
+ end
55
+
51
56
  # Returns +true+ if this is a header row.
52
57
  def header_row?
53
58
  @header_row
@@ -16,6 +16,11 @@ class CSV
16
16
  # Construct a new CSV::Table from +array_of_rows+, which are expected
17
17
  # to be CSV::Row objects. All rows are assumed to have the same headers.
18
18
  #
19
+ # The optional +headers+ parameter can be set to Array of headers.
20
+ # If headers aren't set, headers are fetched from CSV::Row objects.
21
+ # Otherwise, headers() method will return headers being set in
22
+ # headers arugument.
23
+ #
19
24
  # A CSV::Table object supports the following Array methods through
20
25
  # delegation:
21
26
  #
@@ -23,8 +28,17 @@ class CSV
23
28
  # * length()
24
29
  # * size()
25
30
  #
26
- def initialize(array_of_rows)
31
+ def initialize(array_of_rows, headers: nil)
27
32
  @table = array_of_rows
33
+ @headers = headers
34
+ unless @headers
35
+ if @table.empty?
36
+ @headers = []
37
+ else
38
+ @headers = @table.first.headers
39
+ end
40
+ end
41
+
28
42
  @mode = :col_or_row
29
43
  end
30
44
 
@@ -122,11 +136,7 @@ class CSV
122
136
  # other rows). An empty Array is returned for empty tables.
123
137
  #
124
138
  def headers
125
- if @table.empty?
126
- Array.new
127
- else
128
- @table.first.headers
129
- end
139
+ @headers.dup
130
140
  end
131
141
 
132
142
  #
@@ -171,6 +181,10 @@ class CSV
171
181
  @table[index_or_header] = value
172
182
  end
173
183
  else # set column
184
+ unless index_or_header.is_a? Integer
185
+ index = @headers.index(index_or_header) || @headers.size
186
+ @headers[index] = index_or_header
187
+ end
174
188
  if value.is_a? Array # multiple values
175
189
  @table.each_with_index do |row, i|
176
190
  if row.header_row?
@@ -258,6 +272,11 @@ class CSV
258
272
  (@mode == :col_or_row and index_or_header.is_a? Integer)
259
273
  @table.delete_at(index_or_header)
260
274
  else # by header
275
+ if index_or_header.is_a? Integer
276
+ @headers.delete_at(index_or_header)
277
+ else
278
+ @headers.delete(index_or_header)
279
+ end
261
280
  @table.map { |row| row.delete(index_or_header).last }
262
281
  end
263
282
  end
@@ -375,4 +394,4 @@ class CSV
375
394
  "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII")
376
395
  end
377
396
  end
378
- end
397
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  class CSV
4
4
  # The version of the installed library.
5
- VERSION = "3.0.0"
5
+ VERSION = "3.0.1"
6
6
  end
data/news.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # News
2
2
 
3
+ ## 3.0.1 - 2018-12-07
4
+
5
+ ### Improvements
6
+
7
+ * Added a test.
8
+ [GitHub#38][Patch by 284km]
9
+
10
+ * `CSV::Row#dup`: Changed to duplicate internal data.
11
+ [GitHub#39][Reported by André Guimarães Sakata]
12
+
13
+ * Documented `:nil_value` and `:empty_value` options.
14
+ [GitHub#41][Patch by OwlWorks]
15
+
16
+ * Added support for separator detection for non-seekable inputs.
17
+ [GitHub#45][Patch by Ilmari Karonen]
18
+
19
+ * Removed needless code.
20
+ [GitHub#48][Patch by Espartaco Palma]
21
+
22
+ * Added support for parsing header only CSV with `headers: true`.
23
+ [GitHub#47][Patch by Kazuma Shibasaka]
24
+
25
+ * Added support for coverage report in CI.
26
+ [GitHub#48][Patch by Espartaco Palma]
27
+
28
+ * Improved auto CR row separator detection.
29
+ [GitHub#51][Reported by Yuki Kurihara]
30
+
31
+ ### Fixes
32
+
33
+ * Fixed a typo in document.
34
+ [GitHub#40][Patch by Marcus Stollsteimer]
35
+
36
+ ### Thanks
37
+
38
+ * 284km
39
+
40
+ * André Guimarães Sakata
41
+
42
+ * Marcus Stollsteimer
43
+
44
+ * OwlWorks
45
+
46
+ * Ilmari Karonen
47
+
48
+ * Espartaco Palma
49
+
50
+ * Kazuma Shibasaka
51
+
52
+ * Yuki Kurihara
53
+
3
54
  ## 3.0.0 - 2018-06-06
4
55
 
5
56
  ### Fixes
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.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Edward Gray II
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-06-06 00:00:00.000000000 Z
12
+ date: 2018-12-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: simplecov
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  description: The CSV library provides a complete interface to CSV files and data.
57
71
  It offers tools to enable you to read and write to and from Strings or IO objects,
58
72
  as needed.
@@ -92,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
106
  version: '0'
93
107
  requirements: []
94
108
  rubyforge_project:
95
- rubygems_version: 3.0.0.beta1
109
+ rubygems_version: 3.0.0.beta3
96
110
  signing_key:
97
111
  specification_version: 4
98
112
  summary: CSV Reading and Writing