csv 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
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