csv 3.1.6 → 3.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +20 -0
  3. data/doc/{arguments → csv/arguments}/io.rdoc +0 -0
  4. data/doc/{options → csv/options}/common/col_sep.rdoc +0 -0
  5. data/doc/{options → csv/options}/common/quote_char.rdoc +0 -0
  6. data/doc/{options → csv/options}/common/row_sep.rdoc +0 -0
  7. data/doc/{options → csv/options}/generating/force_quotes.rdoc +0 -0
  8. data/doc/{options → csv/options}/generating/quote_empty.rdoc +0 -0
  9. data/doc/{options → csv/options}/generating/write_converters.rdoc +0 -0
  10. data/doc/{options → csv/options}/generating/write_empty_value.rdoc +0 -0
  11. data/doc/{options → csv/options}/generating/write_headers.rdoc +0 -0
  12. data/doc/{options → csv/options}/generating/write_nil_value.rdoc +0 -0
  13. data/doc/{options → csv/options}/parsing/converters.rdoc +0 -0
  14. data/doc/{options → csv/options}/parsing/empty_value.rdoc +0 -0
  15. data/doc/{options → csv/options}/parsing/field_size_limit.rdoc +0 -0
  16. data/doc/{options → csv/options}/parsing/header_converters.rdoc +0 -0
  17. data/doc/{options → csv/options}/parsing/headers.rdoc +0 -0
  18. data/doc/{options → csv/options}/parsing/liberal_parsing.rdoc +0 -0
  19. data/doc/{options → csv/options}/parsing/nil_value.rdoc +0 -0
  20. data/doc/{options → csv/options}/parsing/return_headers.rdoc +0 -0
  21. data/doc/{options → csv/options}/parsing/skip_blanks.rdoc +0 -0
  22. data/doc/{options → csv/options}/parsing/skip_lines.rdoc +0 -0
  23. data/doc/{options → csv/options}/parsing/strip.rdoc +0 -0
  24. data/doc/{options → csv/options}/parsing/unconverted_fields.rdoc +0 -0
  25. data/lib/csv.rb +142 -46
  26. data/lib/csv/table.rb +285 -64
  27. data/lib/csv/version.rb +1 -1
  28. metadata +26 -40
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 684d334d79d666022640de8b3bfa8d2e6fa879cb0c9e59349e9adf8307147fa0
4
- data.tar.gz: 6298def337c705d19acb6d1da52560aa53f0dfeba5660202d8fc0259418d96a1
3
+ metadata.gz: c834580f6d364830ddcd85b07edcb44fc91520fd594492a835cb57f43f145c94
4
+ data.tar.gz: 989310760b22148eeb70be9181a39c2d08c8040f7e79fd6a06dc7586409c4e7f
5
5
  SHA512:
6
- metadata.gz: 42349aa586b53f75c75ea29a5388889c21645b24e1ad8ac8e1752697f94dc9e7564b62f36b2b6219516f34c1a27bbf581566f3f2dafef556f9783b907e932d04
7
- data.tar.gz: 69319007e9a35acb7659d73b5c9a38256a572a5a21ef59365b8f4fd0fee9d06c2c0c89ae346bf701e6c0af430fc99868c625323cc75f620340eb80006ce0d036
6
+ metadata.gz: 75859dd4eff126ab15bf372305da6eeb638ce1927186931f3ea87fe00c0a769c8adb38cb786b8ee3f7fd418de720d26fe01fba9ca28ac565bbd761431057cec8
7
+ data.tar.gz: 56b5de70961f3e9792296e6b68941fa1ea1e1eaa5ad2811ae47954a775c986d8f9455af50e5e6d25deb797794b697cef0e0c4a87695dc3a42d9fa477008d85dc
data/NEWS.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # News
2
2
 
3
+ ## 3.1.7 - 2020-08-04
4
+
5
+ ### Improvements
6
+
7
+ * Improved document.
8
+ [GitHub#158][GitHub#160][GitHub#161]
9
+ [Patch by Burdette Lamar]
10
+
11
+ * Updated required Ruby version to 2.5.0 or later.
12
+ [GitHub#159]
13
+ [Patch by Gabriel Nagy]
14
+
15
+ * Removed stringio 0.1.3 or later dependency.
16
+
17
+ ### Thanks
18
+
19
+ * Burdette Lamar
20
+
21
+ * Gabriel Nagy
22
+
3
23
  ## 3.1.6 - 2020-07-20
4
24
 
5
25
  ### Improvements
File without changes
data/lib/csv.rb CHANGED
@@ -104,7 +104,7 @@ require_relative "csv/writer"
104
104
  using CSV::MatchP if CSV.const_defined?(:MatchP)
105
105
 
106
106
  # == \CSV
107
- # \CSV (comma-separated variables) data is a text representation of a table:
107
+ # \CSV (comma-separated values) data is a text representation of a table:
108
108
  # - A _row_ _separator_ delimits table rows.
109
109
  # A common row separator is the newline character <tt>"\n"</tt>.
110
110
  # - A _column_ _separator_ delimits fields in a row.
@@ -357,35 +357,35 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
357
357
  # - +nil_value+: Specifies the object that is to be substituted for each null (no-text) field.
358
358
  # - +empty_value+: Specifies the object that is to be substituted for each empty field.
359
359
  #
360
- # :include: ../doc/options/common/row_sep.rdoc
360
+ # :include: ../doc/csv/options/common/row_sep.rdoc
361
361
  #
362
- # :include: ../doc/options/common/col_sep.rdoc
362
+ # :include: ../doc/csv/options/common/col_sep.rdoc
363
363
  #
364
- # :include: ../doc/options/common/quote_char.rdoc
364
+ # :include: ../doc/csv/options/common/quote_char.rdoc
365
365
  #
366
- # :include: ../doc/options/parsing/field_size_limit.rdoc
366
+ # :include: ../doc/csv/options/parsing/field_size_limit.rdoc
367
367
  #
368
- # :include: ../doc/options/parsing/converters.rdoc
368
+ # :include: ../doc/csv/options/parsing/converters.rdoc
369
369
  #
370
- # :include: ../doc/options/parsing/unconverted_fields.rdoc
370
+ # :include: ../doc/csv/options/parsing/unconverted_fields.rdoc
371
371
  #
372
- # :include: ../doc/options/parsing/headers.rdoc
372
+ # :include: ../doc/csv/options/parsing/headers.rdoc
373
373
  #
374
- # :include: ../doc/options/parsing/return_headers.rdoc
374
+ # :include: ../doc/csv/options/parsing/return_headers.rdoc
375
375
  #
376
- # :include: ../doc/options/parsing/header_converters.rdoc
376
+ # :include: ../doc/csv/options/parsing/header_converters.rdoc
377
377
  #
378
- # :include: ../doc/options/parsing/skip_blanks.rdoc
378
+ # :include: ../doc/csv/options/parsing/skip_blanks.rdoc
379
379
  #
380
- # :include: ../doc/options/parsing/skip_lines.rdoc
380
+ # :include: ../doc/csv/options/parsing/skip_lines.rdoc
381
381
  #
382
- # :include: ../doc/options/parsing/strip.rdoc
382
+ # :include: ../doc/csv/options/parsing/strip.rdoc
383
383
  #
384
- # :include: ../doc/options/parsing/liberal_parsing.rdoc
384
+ # :include: ../doc/csv/options/parsing/liberal_parsing.rdoc
385
385
  #
386
- # :include: ../doc/options/parsing/nil_value.rdoc
386
+ # :include: ../doc/csv/options/parsing/nil_value.rdoc
387
387
  #
388
- # :include: ../doc/options/parsing/empty_value.rdoc
388
+ # :include: ../doc/csv/options/parsing/empty_value.rdoc
389
389
  #
390
390
  # ==== Options for Generating
391
391
  #
@@ -400,23 +400,23 @@ using CSV::MatchP if CSV.const_defined?(:MatchP)
400
400
  # - +write_nil_value+: Specifies the object that is to be substituted for each +nil+-valued field.
401
401
  # - +write_empty_value+: Specifies the object that is to be substituted for each empty field.
402
402
  #
403
- # :include: ../doc/options/common/row_sep.rdoc
403
+ # :include: ../doc/csv/options/common/row_sep.rdoc
404
404
  #
405
- # :include: ../doc/options/common/col_sep.rdoc
405
+ # :include: ../doc/csv/options/common/col_sep.rdoc
406
406
  #
407
- # :include: ../doc/options/common/quote_char.rdoc
407
+ # :include: ../doc/csv/options/common/quote_char.rdoc
408
408
  #
409
- # :include: ../doc/options/generating/write_headers.rdoc
409
+ # :include: ../doc/csv/options/generating/write_headers.rdoc
410
410
  #
411
- # :include: ../doc/options/generating/force_quotes.rdoc
411
+ # :include: ../doc/csv/options/generating/force_quotes.rdoc
412
412
  #
413
- # :include: ../doc/options/generating/quote_empty.rdoc
413
+ # :include: ../doc/csv/options/generating/quote_empty.rdoc
414
414
  #
415
- # :include: ../doc/options/generating/write_converters.rdoc
415
+ # :include: ../doc/csv/options/generating/write_converters.rdoc
416
416
  #
417
- # :include: ../doc/options/generating/write_nil_value.rdoc
417
+ # :include: ../doc/csv/options/generating/write_nil_value.rdoc
418
418
  #
419
- # :include: ../doc/options/generating/write_empty_value.rdoc
419
+ # :include: ../doc/csv/options/generating/write_empty_value.rdoc
420
420
  #
421
421
  # === \CSV with Headers
422
422
  #
@@ -1075,7 +1075,7 @@ class CSV
1075
1075
  # Calls the block with each row read from source +path+ or +io+.
1076
1076
  #
1077
1077
  # * Argument +path+, if given, must be the path to a file.
1078
- # :include: ../doc/arguments/io.rdoc
1078
+ # :include: ../doc/csv/arguments/io.rdoc
1079
1079
  # * Argument +mode+, if given, must be a \File mode
1080
1080
  # See {Open Mode}[IO.html#method-c-new-label-Open+Mode].
1081
1081
  # * Arguments <tt>**options</tt> must be keyword options.
@@ -1322,7 +1322,7 @@ class CSV
1322
1322
  # :replace => string # replacement string ("?" or "\uFFFD" if not specified)
1323
1323
  #
1324
1324
  # * Argument +path+, if given, must be the path to a file.
1325
- # :include: ../doc/arguments/io.rdoc
1325
+ # :include: ../doc/csv/arguments/io.rdoc
1326
1326
  # * Argument +mode+, if given, must be a \File mode
1327
1327
  # See {Open Mode}[IO.html#method-c-new-label-Open+Mode].
1328
1328
  # * Arguments <tt>**options</tt> must be keyword options.
@@ -1427,7 +1427,7 @@ class CSV
1427
1427
  #
1428
1428
  # - Argument +string+ should be a \String object;
1429
1429
  # it will be put into a new StringIO object positioned at the beginning.
1430
- # :include: ../doc/arguments/io.rdoc
1430
+ # :include: ../doc/csv/arguments/io.rdoc
1431
1431
  # - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
1432
1432
  #
1433
1433
  # ====== Without Option +headers+
@@ -1552,7 +1552,7 @@ class CSV
1552
1552
  #
1553
1553
  # - Argument +string+ should be a \String object;
1554
1554
  # it will be put into a new StringIO object positioned at the beginning.
1555
- # :include: ../doc/arguments/io.rdoc
1555
+ # :include: ../doc/csv/arguments/io.rdoc
1556
1556
  # - Argument +options+: see {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
1557
1557
  #
1558
1558
  # ====== Without Option +headers+
@@ -1672,7 +1672,7 @@ class CSV
1672
1672
  #
1673
1673
  # - Argument +string+ should be a \String object;
1674
1674
  # it will be put into a new StringIO object positioned at the beginning.
1675
- # :include: ../doc/arguments/io.rdoc
1675
+ # :include: ../doc/csv/arguments/io.rdoc
1676
1676
  # - Argument +options+: See:
1677
1677
  # * {Options for Parsing}[#class-CSV-label-Options+for+Parsing]
1678
1678
  # * {Options for Generating}[#class-CSV-label-Options+for+Generating]
@@ -2080,7 +2080,7 @@ class CSV
2080
2080
  ### End Delegation ###
2081
2081
 
2082
2082
  # :call-seq:
2083
- # csv.<< row
2083
+ # csv << row -> self
2084
2084
  #
2085
2085
  # Appends a row to +self+.
2086
2086
  #
@@ -2120,7 +2120,7 @@ class CSV
2120
2120
  # csv << :foo
2121
2121
  # end
2122
2122
  #
2123
- # Raises an exception if the output stream is not open for writing:
2123
+ # Raises an exception if the output stream is not opened for writing:
2124
2124
  # path = 't.csv'
2125
2125
  # File.write(path, '')
2126
2126
  # File.open(path) do |file|
@@ -2272,25 +2272,57 @@ class CSV
2272
2272
 
2273
2273
  include Enumerable
2274
2274
 
2275
+ # :call-seq:
2276
+ # csv.each -> enumerator
2277
+ # csv.each {|row| ...}
2275
2278
  #
2276
- # Yields each row of the data source in turn.
2279
+ # Calls the block with each successive row.
2280
+ # The data source must be opened for reading.
2277
2281
  #
2278
- # Support for Enumerable.
2282
+ # Without headers:
2283
+ # string = "foo,0\nbar,1\nbaz,2\n"
2284
+ # csv = CSV.new(string)
2285
+ # csv.each do |row|
2286
+ # p row
2287
+ # end
2288
+ # Output:
2289
+ # ["foo", "0"]
2290
+ # ["bar", "1"]
2291
+ # ["baz", "2"]
2279
2292
  #
2280
- # The data source must be open for reading.
2293
+ # With headers:
2294
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2295
+ # csv = CSV.new(string, headers: true)
2296
+ # csv.each do |row|
2297
+ # p row
2298
+ # end
2299
+ # Output:
2300
+ # <CSV::Row "Name":"foo" "Value":"0">
2301
+ # <CSV::Row "Name":"bar" "Value":"1">
2302
+ # <CSV::Row "Name":"baz" "Value":"2">
2303
+ #
2304
+ # ---
2281
2305
  #
2306
+ # Raises an exception if the source is not opened for reading:
2307
+ # string = "foo,0\nbar,1\nbaz,2\n"
2308
+ # csv = CSV.new(string)
2309
+ # csv.close
2310
+ # # Raises IOError (not opened for reading)
2311
+ # csv.each do |row|
2312
+ # p row
2313
+ # end
2282
2314
  def each(&block)
2283
2315
  parser_enumerator.each(&block)
2284
2316
  end
2285
2317
 
2286
2318
  # :call-seq:
2287
- # read
2319
+ # csv.read -> array or csv_table
2288
2320
  #
2289
2321
  # Forms the remaining rows from +self+ into:
2290
2322
  # - A CSV::Table object, if headers are in use.
2291
- # - An Array of Arrays, otherwise.
2323
+ # - An \Array of Arrays, otherwise.
2292
2324
  #
2293
- # The data source must be open for reading.
2325
+ # The data source must be opened for reading.
2294
2326
  #
2295
2327
  # Without headers:
2296
2328
  # string = "foo,0\nbar,1\nbaz,2\n"
@@ -2305,6 +2337,15 @@ class CSV
2305
2337
  # File.write(path, string)
2306
2338
  # csv = CSV.open(path, headers: true)
2307
2339
  # csv.read # => #<CSV::Table mode:col_or_row row_count:4>
2340
+ #
2341
+ # ---
2342
+ #
2343
+ # Raises an exception if the source is not opened for reading:
2344
+ # string = "foo,0\nbar,1\nbaz,2\n"
2345
+ # csv = CSV.new(string)
2346
+ # csv.close
2347
+ # # Raises IOError (not opened for reading)
2348
+ # csv.read
2308
2349
  def read
2309
2350
  rows = to_a
2310
2351
  if parser.use_headers?
@@ -2315,18 +2356,69 @@ class CSV
2315
2356
  end
2316
2357
  alias_method :readlines, :read
2317
2358
 
2318
- # Returns +true+ if the next row read will be a header row.
2359
+ # :call-seq:
2360
+ # csv.header_row? -> true or false
2361
+ #
2362
+ # Returns +true+ if the next row to be read is a header row\;
2363
+ # +false+ otherwise.
2364
+ #
2365
+ # Without headers:
2366
+ # string = "foo,0\nbar,1\nbaz,2\n"
2367
+ # csv = CSV.new(string)
2368
+ # csv.header_row? # => false
2369
+ #
2370
+ # With headers:
2371
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2372
+ # csv = CSV.new(string, headers: true)
2373
+ # csv.header_row? # => true
2374
+ # csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
2375
+ # csv.header_row? # => false
2376
+ #
2377
+ # ---
2378
+ #
2379
+ # Raises an exception if the source is not opened for reading:
2380
+ # string = "foo,0\nbar,1\nbaz,2\n"
2381
+ # csv = CSV.new(string)
2382
+ # csv.close
2383
+ # # Raises IOError (not opened for reading)
2384
+ # csv.header_row?
2319
2385
  def header_row?
2320
2386
  parser.header_row?
2321
2387
  end
2322
2388
 
2389
+ # :call-seq:
2390
+ # csv.shift -> array, csv_row, or nil
2323
2391
  #
2324
- # The primary read method for wrapped Strings and IOs, a single row is pulled
2325
- # from the data source, parsed and returned as an Array of fields (if header
2326
- # rows are not used) or a CSV::Row (when header rows are used).
2392
+ # Returns the next row of data as:
2393
+ # - An \Array if no headers are used.
2394
+ # - A CSV::Row object if headers are used.
2327
2395
  #
2328
- # The data source must be open for reading.
2396
+ # The data source must be opened for reading.
2329
2397
  #
2398
+ # Without headers:
2399
+ # string = "foo,0\nbar,1\nbaz,2\n"
2400
+ # csv = CSV.new(string)
2401
+ # csv.shift # => ["foo", "0"]
2402
+ # csv.shift # => ["bar", "1"]
2403
+ # csv.shift # => ["baz", "2"]
2404
+ # csv.shift # => nil
2405
+ #
2406
+ # With headers:
2407
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2408
+ # csv = CSV.new(string, headers: true)
2409
+ # csv.shift # => #<CSV::Row "Name":"foo" "Value":"0">
2410
+ # csv.shift # => #<CSV::Row "Name":"bar" "Value":"1">
2411
+ # csv.shift # => #<CSV::Row "Name":"baz" "Value":"2">
2412
+ # csv.shift # => nil
2413
+ #
2414
+ # ---
2415
+ #
2416
+ # Raises an exception if the source is not opened for reading:
2417
+ # string = "foo,0\nbar,1\nbaz,2\n"
2418
+ # csv = CSV.new(string)
2419
+ # csv.close
2420
+ # # Raises IOError (not opened for reading)
2421
+ # csv.shift
2330
2422
  def shift
2331
2423
  if @eof_error
2332
2424
  eof_error, @eof_error = @eof_error, nil
@@ -2341,10 +2433,14 @@ class CSV
2341
2433
  alias_method :gets, :shift
2342
2434
  alias_method :readline, :shift
2343
2435
 
2436
+ # :call-seq:
2437
+ # csv.inspect -> string
2344
2438
  #
2345
- # Returns a simplified description of the key CSV attributes in an
2346
- # ASCII compatible String.
2347
- #
2439
+ # Returns a \String showing certain properties of +self+:
2440
+ # string = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
2441
+ # csv = CSV.new(string, headers: true)
2442
+ # s = csv.inspect
2443
+ # s # => "#<CSV io_type:StringIO encoding:UTF-8 lineno:0 col_sep:\",\" row_sep:\"\\n\" quote_char:\"\\\"\" headers:true>"
2348
2444
  def inspect
2349
2445
  str = ["#<", self.class.to_s, " io_type:"]
2350
2446
  # show type of wrapped IO
@@ -3,31 +3,199 @@
3
3
  require "forwardable"
4
4
 
5
5
  class CSV
6
+ # = \CSV::Table
7
+ # A \CSV::Table instance is an object representing \CSV data.
8
+ # (see {class CSV}[../CSV.html]).
6
9
  #
7
- # A CSV::Table is a two-dimensional data structure for representing CSV
8
- # documents. Tables allow you to work with the data by row or column,
9
- # manipulate the data, and even convert the results back to CSV, if needed.
10
+ # The instance may have:
11
+ # - Rows: each is a Table::Row object.
12
+ # - Headers: names for the columns.
10
13
  #
11
- # All tables returned by CSV will be constructed from this class, if header
12
- # row processing is activated.
14
+ # === Instance Methods
13
15
  #
16
+ # \CSV::Table has three groups of instance methods:
17
+ # - Its own internally defined instance methods.
18
+ # - Methods included by module Enumerable.
19
+ # - Methods delegated to class Array.:
20
+ # * Array#empty?
21
+ # * Array#length
22
+ # * Array#size
23
+ #
24
+ # == Creating a \CSV::Table Instance
25
+ #
26
+ # Commonly, a new \CSV::Table instance is created by parsing \CSV source
27
+ # using headers:
28
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
29
+ # table = CSV.parse(source, headers: true)
30
+ # table.class # => CSV::Table
31
+ #
32
+ # You can also create an instance directly. See ::new.
33
+ #
34
+ # == Headers
35
+ #
36
+ # If a table has headers, the headers serve as labels for the columns of data.
37
+ # Each header serves as the label for its column.
38
+ #
39
+ # The headers for a \CSV::Table object are stored as an \Array of Strings.
40
+ #
41
+ # Commonly, headers are defined in the first row of \CSV source:
42
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
43
+ # table = CSV.parse(source, headers: true)
44
+ # table.headers # => ["Name", "Value"]
45
+ #
46
+ # If no headers are defined, the \Array is empty:
47
+ # table = CSV::Table.new([])
48
+ # table.headers # => []
49
+ #
50
+ # == Access Modes
51
+ #
52
+ # \CSV::Table provides three modes for accessing table data:
53
+ # - \Row mode.
54
+ # - Column mode.
55
+ # - Mixed mode (the default for a new table).
56
+ #
57
+ # The access mode for a\CSV::Table instance affects the behavior
58
+ # of some of its instance methods:
59
+ # - #[]
60
+ # - #[]=
61
+ # - #delete
62
+ # - #delete_if
63
+ # - #each
64
+ # - #values_at
65
+ #
66
+ # === \Row Mode
67
+ #
68
+ # Set a table to row mode with method #by_row!:
69
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
70
+ # table = CSV.parse(source, headers: true)
71
+ # table.by_row! # => #<CSV::Table mode:row row_count:4>
72
+ #
73
+ # Specify a single row by an \Integer index:
74
+ # # Get a row.
75
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
76
+ # # Set a row, then get it.
77
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
78
+ # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
79
+ #
80
+ # Specify a sequence of rows by a \Range:
81
+ # # Get rows.
82
+ # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
83
+ # # Set rows, then get them.
84
+ # table[1..2] = [
85
+ # CSV::Row.new(['Name', 'Value'], ['bat', 4]),
86
+ # CSV::Row.new(['Name', 'Value'], ['bad', 5]),
87
+ # ]
88
+ # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
89
+ #
90
+ # === Column Mode
91
+ #
92
+ # Set a table to column mode with method #by_col!:
93
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
94
+ # table = CSV.parse(source, headers: true)
95
+ # table.by_col! # => #<CSV::Table mode:col row_count:4>
96
+ #
97
+ # Specify a column by an \Integer index:
98
+ # # Get a column.
99
+ # table[0]
100
+ # # Set a column, then get it.
101
+ # table[0] = ['FOO', 'BAR', 'BAZ']
102
+ # table[0] # => ["FOO", "BAR", "BAZ"]
103
+ #
104
+ # Specify a column by its \String header:
105
+ # # Get a column.
106
+ # table['Name'] # => ["FOO", "BAR", "BAZ"]
107
+ # # Set a column, then get it.
108
+ # table['Name'] = ['Foo', 'Bar', 'Baz']
109
+ # table['Name'] # => ["Foo", "Bar", "Baz"]
110
+ #
111
+ # === Mixed Mode
112
+ #
113
+ # In mixed mode, you can refer to either rows or columns:
114
+ # - An \Integer index refers to a row.
115
+ # - A \Range index refers to multiple rows.
116
+ # - A \String index refers to a column.
117
+ #
118
+ # Set a table to mixed mode with method #by_col_or_row!:
119
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
120
+ # table = CSV.parse(source, headers: true)
121
+ # table.by_col_or_row! # => #<CSV::Table mode:col_or_row row_count:4>
122
+ #
123
+ # Specify a single row by an \Integer index:
124
+ # # Get a row.
125
+ # table[1] # => #<CSV::Row "Name":"bar" "Value":"1">
126
+ # # Set a row, then get it.
127
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bam', 3])
128
+ # table[1] # => #<CSV::Row "Name":"bam" "Value":3>
129
+ #
130
+ # Specify a sequence of rows by a \Range:
131
+ # # Get rows.
132
+ # table[1..2] # => [#<CSV::Row "Name":"bam" "Value":3>, #<CSV::Row "Name":"baz" "Value":"2">]
133
+ # # Set rows, then get them.
134
+ # table[1] = CSV::Row.new(['Name', 'Value'], ['bat', 4])
135
+ # table[2] = CSV::Row.new(['Name', 'Value'], ['bad', 5])
136
+ # table[1..2] # => [["Name", #<CSV::Row "Name":"bat" "Value":4>], ["Value", #<CSV::Row "Name":"bad" "Value":5>]]
137
+ #
138
+ # Specify a column by its \String header:
139
+ # # Get a column.
140
+ # table['Name'] # => ["foo", "bat", "bad"]
141
+ # # Set a column, then get it.
142
+ # table['Name'] = ['Foo', 'Bar', 'Baz']
143
+ # table['Name'] # => ["Foo", "Bar", "Baz"]
14
144
  class Table
15
- #
16
- # Constructs a new CSV::Table from +array_of_rows+, which are expected
17
- # to be CSV::Row objects. All rows are assumed to have the same headers.
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 argument.
23
- #
24
- # A CSV::Table object supports the following Array methods through
25
- # delegation:
26
- #
27
- # * empty?()
28
- # * length()
29
- # * size()
30
- #
145
+ # :call-seq:
146
+ # CSV::Table.new(array_of_rows, headers = nil)
147
+ #
148
+ # Returns a new \CSV::Table object.
149
+ #
150
+ # - Argument +array_of_rows+ must be an \Array of CSV::Row objects.
151
+ # - Argument +headers+, if given, may be an \Array of Strings.
152
+ #
153
+ # ---
154
+ #
155
+ # Create an empty \CSV::Table object:
156
+ # table = CSV::Table.new([])
157
+ # table # => #<CSV::Table mode:col_or_row row_count:1>
158
+ #
159
+ # Create a non-empty \CSV::Table object:
160
+ # rows = [
161
+ # CSV::Row.new([], []),
162
+ # CSV::Row.new([], []),
163
+ # CSV::Row.new([], []),
164
+ # ]
165
+ # table = CSV::Table.new(rows)
166
+ # table # => #<CSV::Table mode:col_or_row row_count:4>
167
+ #
168
+ # ---
169
+ #
170
+ # If argument +headers+ is an \Array of Strings,
171
+ # those Strings become the table's headers:
172
+ # table = CSV::Table.new([], headers: ['Name', 'Age'])
173
+ # table.headers # => ["Name", "Age"]
174
+ #
175
+ # If argument +headers+ is not given and the table has rows,
176
+ # the headers are taken from the first row:
177
+ # rows = [
178
+ # CSV::Row.new(['Foo', 'Bar'], []),
179
+ # CSV::Row.new(['foo', 'bar'], []),
180
+ # CSV::Row.new(['FOO', 'BAR'], []),
181
+ # ]
182
+ # table = CSV::Table.new(rows)
183
+ # table.headers # => ["Foo", "Bar"]
184
+ #
185
+ # If argument +headers+ is not given and the table is empty (has no rows),
186
+ # the headers are also empty:
187
+ # table = CSV::Table.new([])
188
+ # table.headers # => []
189
+ #
190
+ # ---
191
+ #
192
+ # Raises an exception if argument +array_of_rows+ is not an \Array object:
193
+ # # Raises NoMethodError (undefined method `first' for :foo:Symbol):
194
+ # CSV::Table.new(:foo)
195
+ #
196
+ # Raises an exception if an element of +array_of_rows+ is not a \CSV::Table object:
197
+ # # Raises NoMethodError (undefined method `headers' for :foo:Symbol):
198
+ # CSV::Table.new([:foo])
31
199
  def initialize(array_of_rows, headers: nil)
32
200
  @table = array_of_rows
33
201
  @headers = headers
@@ -54,88 +222,141 @@ class CSV
54
222
  extend Forwardable
55
223
  def_delegators :@table, :empty?, :length, :size
56
224
 
225
+ # :call-seq:
226
+ # table.by_col
57
227
  #
58
- # Returns a duplicate table object, in column mode. This is handy for
59
- # chaining in a single call without changing the table mode, but be aware
60
- # that this method can consume a fair amount of memory for bigger data sets.
228
+ # Returns a duplicate of +self+, in column mode
229
+ # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]):
230
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
231
+ # table = CSV.parse(source, headers: true)
232
+ # table.mode # => :col_or_row
233
+ # dup_table = table.by_col
234
+ # dup_table.mode # => :col
235
+ # dup_table.equal?(table) # => false # It's a dup
61
236
  #
62
- # This method returns the duplicate table for chaining. Don't chain
63
- # destructive methods (like []=()) this way though, since you are working
64
- # with a duplicate.
237
+ # This may be used to chain method calls without changing the mode
238
+ # (but also will affect performance and memory usage):
239
+ # dup_table.by_col['Name']
65
240
  #
241
+ # Also note that changes to the duplicate table will not affect the original.
66
242
  def by_col
67
243
  self.class.new(@table.dup).by_col!
68
244
  end
69
245
 
70
- #
71
- # Switches the mode of this table to column mode. All calls to indexing and
72
- # iteration methods will work with columns until the mode is changed again.
73
- #
74
- # This method returns the table and is safe to chain.
75
- #
246
+ # :call-seq:
247
+ # table.by_col!
248
+ #
249
+ # Sets the mode for +self+ to column mode
250
+ # (see {Column Mode}[#class-CSV::Table-label-Column+Mode]); returns +self+:
251
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
252
+ # table = CSV.parse(source, headers: true)
253
+ # table.mode # => :col_or_row
254
+ # table1 = table.by_col!
255
+ # table.mode # => :col
256
+ # table1.equal?(table) # => true # Returned self
76
257
  def by_col!
77
258
  @mode = :col
78
259
 
79
260
  self
80
261
  end
81
262
 
263
+ # :call-seq:
264
+ # table.by_col_or_row
82
265
  #
83
- # Returns a duplicate table object, in mixed mode. This is handy for
84
- # chaining in a single call without changing the table mode, but be aware
85
- # that this method can consume a fair amount of memory for bigger data sets.
266
+ # Returns a duplicate of +self+, in mixed mode
267
+ # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]):
268
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
269
+ # table = CSV.parse(source, headers: true).by_col!
270
+ # table.mode # => :col
271
+ # dup_table = table.by_col_or_row
272
+ # dup_table.mode # => :col_or_row
273
+ # dup_table.equal?(table) # => false # It's a dup
86
274
  #
87
- # This method returns the duplicate table for chaining. Don't chain
88
- # destructive methods (like []=()) this way though, since you are working
89
- # with a duplicate.
275
+ # This may be used to chain method calls without changing the mode
276
+ # (but also will affect performance and memory usage):
277
+ # dup_table.by_col_or_row['Name']
90
278
  #
279
+ # Also note that changes to the duplicate table will not affect the original.
91
280
  def by_col_or_row
92
281
  self.class.new(@table.dup).by_col_or_row!
93
282
  end
94
283
 
95
- #
96
- # Switches the mode of this table to mixed mode. All calls to indexing and
97
- # iteration methods will use the default intelligent indexing system until
98
- # the mode is changed again. In mixed mode an index is assumed to be a row
99
- # reference while anything else is assumed to be column access by headers.
100
- #
101
- # This method returns the table and is safe to chain.
102
- #
284
+ # :call-seq:
285
+ # table.by_col_or_row!
286
+ #
287
+ # Sets the mode for +self+ to mixed mode
288
+ # (see {Mixed Mode}[#class-CSV::Table-label-Mixed+Mode]); returns +self+:
289
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
290
+ # table = CSV.parse(source, headers: true).by_col!
291
+ # table.mode # => :col
292
+ # table1 = table.by_col_or_row!
293
+ # table.mode # => :col_or_row
294
+ # table1.equal?(table) # => true # Returned self
103
295
  def by_col_or_row!
104
296
  @mode = :col_or_row
105
297
 
106
298
  self
107
299
  end
108
300
 
301
+ # :call-seq:
302
+ # table.by_row
109
303
  #
110
- # Returns a duplicate table object, in row mode. This is handy for chaining
111
- # in a single call without changing the table mode, but be aware that this
112
- # method can consume a fair amount of memory for bigger data sets.
304
+ # Returns a duplicate of +self+, in row mode
305
+ # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]):
306
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
307
+ # table = CSV.parse(source, headers: true)
308
+ # table.mode # => :col_or_row
309
+ # dup_table = table.by_row
310
+ # dup_table.mode # => :row
311
+ # dup_table.equal?(table) # => false # It's a dup
113
312
  #
114
- # This method returns the duplicate table for chaining. Don't chain
115
- # destructive methods (like []=()) this way though, since you are working
116
- # with a duplicate.
313
+ # This may be used to chain method calls without changing the mode
314
+ # (but also will affect performance and memory usage):
315
+ # dup_table.by_row[1]
117
316
  #
317
+ # Also note that changes to the duplicate table will not affect the original.
118
318
  def by_row
119
319
  self.class.new(@table.dup).by_row!
120
320
  end
121
321
 
122
- #
123
- # Switches the mode of this table to row mode. All calls to indexing and
124
- # iteration methods will work with rows until the mode is changed again.
125
- #
126
- # This method returns the table and is safe to chain.
127
- #
322
+ # :call-seq:
323
+ # table.by_row!
324
+ #
325
+ # Sets the mode for +self+ to row mode
326
+ # (see {Row Mode}[#class-CSV::Table-label-Row+Mode]); returns +self+:
327
+ # source = "Name,Value\nfoo,0\nbar,1\nbaz,2\n"
328
+ # table = CSV.parse(source, headers: true)
329
+ # table.mode # => :col_or_row
330
+ # table1 = table.by_row!
331
+ # table.mode # => :row
332
+ # table1.equal?(table) # => true # Returned self
128
333
  def by_row!
129
334
  @mode = :row
130
335
 
131
336
  self
132
337
  end
133
338
 
134
- #
135
- # Returns the headers for the first row of this table (assumed to match all
136
- # other rows). The headers Array passed to CSV::Table.new is returned for
137
- # empty tables.
138
- #
339
+ # :call-seq:
340
+ # table.headers
341
+ #
342
+ # Returns a new \Array containing the \String headers for the table.
343
+ #
344
+ # If the table is not empty, returns the headers from the first row:
345
+ # rows = [
346
+ # CSV::Row.new(['Foo', 'Bar'], []),
347
+ # CSV::Row.new(['FOO', 'BAR'], []),
348
+ # CSV::Row.new(['foo', 'bar'], []),
349
+ # ]
350
+ # table = CSV::Table.new(rows)
351
+ # table.headers # => ["Foo", "Bar"]
352
+ # table.delete(0)
353
+ # table.headers # => ["FOO", "BAR"]
354
+ # table.delete(0)
355
+ # table.headers # => ["foo", "bar"]
356
+ #
357
+ # If the table is empty, returns a copy of the headers in the table itself:
358
+ # table.delete(0)
359
+ # table.headers # => ["Foo", "Bar"]
139
360
  def headers
140
361
  if @table.empty?
141
362
  @headers.dup
@@ -2,5 +2,5 @@
2
2
 
3
3
  class CSV
4
4
  # The version of the installed library.
5
- VERSION = "3.1.6"
5
+ VERSION = "3.1.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.1.6
4
+ version: 3.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Edward Gray II
@@ -9,22 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-07-19 00:00:00.000000000 Z
12
+ date: 2020-08-04 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: stringio
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: 0.1.3
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: 0.1.3
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: bundler
30
16
  requirement: !ruby/object:Gem::Requirement
@@ -97,28 +83,28 @@ files:
97
83
  - LICENSE.txt
98
84
  - NEWS.md
99
85
  - README.md
100
- - doc/arguments/io.rdoc
101
- - doc/options/common/col_sep.rdoc
102
- - doc/options/common/quote_char.rdoc
103
- - doc/options/common/row_sep.rdoc
104
- - doc/options/generating/force_quotes.rdoc
105
- - doc/options/generating/quote_empty.rdoc
106
- - doc/options/generating/write_converters.rdoc
107
- - doc/options/generating/write_empty_value.rdoc
108
- - doc/options/generating/write_headers.rdoc
109
- - doc/options/generating/write_nil_value.rdoc
110
- - doc/options/parsing/converters.rdoc
111
- - doc/options/parsing/empty_value.rdoc
112
- - doc/options/parsing/field_size_limit.rdoc
113
- - doc/options/parsing/header_converters.rdoc
114
- - doc/options/parsing/headers.rdoc
115
- - doc/options/parsing/liberal_parsing.rdoc
116
- - doc/options/parsing/nil_value.rdoc
117
- - doc/options/parsing/return_headers.rdoc
118
- - doc/options/parsing/skip_blanks.rdoc
119
- - doc/options/parsing/skip_lines.rdoc
120
- - doc/options/parsing/strip.rdoc
121
- - doc/options/parsing/unconverted_fields.rdoc
86
+ - doc/csv/arguments/io.rdoc
87
+ - doc/csv/options/common/col_sep.rdoc
88
+ - doc/csv/options/common/quote_char.rdoc
89
+ - doc/csv/options/common/row_sep.rdoc
90
+ - doc/csv/options/generating/force_quotes.rdoc
91
+ - doc/csv/options/generating/quote_empty.rdoc
92
+ - doc/csv/options/generating/write_converters.rdoc
93
+ - doc/csv/options/generating/write_empty_value.rdoc
94
+ - doc/csv/options/generating/write_headers.rdoc
95
+ - doc/csv/options/generating/write_nil_value.rdoc
96
+ - doc/csv/options/parsing/converters.rdoc
97
+ - doc/csv/options/parsing/empty_value.rdoc
98
+ - doc/csv/options/parsing/field_size_limit.rdoc
99
+ - doc/csv/options/parsing/header_converters.rdoc
100
+ - doc/csv/options/parsing/headers.rdoc
101
+ - doc/csv/options/parsing/liberal_parsing.rdoc
102
+ - doc/csv/options/parsing/nil_value.rdoc
103
+ - doc/csv/options/parsing/return_headers.rdoc
104
+ - doc/csv/options/parsing/skip_blanks.rdoc
105
+ - doc/csv/options/parsing/skip_lines.rdoc
106
+ - doc/csv/options/parsing/strip.rdoc
107
+ - doc/csv/options/parsing/unconverted_fields.rdoc
122
108
  - lib/csv.rb
123
109
  - lib/csv/core_ext/array.rb
124
110
  - lib/csv/core_ext/string.rb
@@ -144,14 +130,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
144
130
  requirements:
145
131
  - - ">="
146
132
  - !ruby/object:Gem::Version
147
- version: 2.3.0
133
+ version: 2.5.0
148
134
  required_rubygems_version: !ruby/object:Gem::Requirement
149
135
  requirements:
150
136
  - - ">="
151
137
  - !ruby/object:Gem::Version
152
138
  version: '0'
153
139
  requirements: []
154
- rubygems_version: 3.2.0.pre1
140
+ rubygems_version: 3.2.0.rc.1
155
141
  signing_key:
156
142
  specification_version: 4
157
143
  summary: CSV Reading and Writing