csv 3.1.3 → 3.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/NEWS.md +110 -0
- data/README.md +5 -0
- data/doc/csv/arguments/io.rdoc +5 -0
- data/doc/csv/options/common/col_sep.rdoc +57 -0
- data/doc/csv/options/common/quote_char.rdoc +42 -0
- data/doc/csv/options/common/row_sep.rdoc +91 -0
- data/doc/csv/options/generating/force_quotes.rdoc +17 -0
- data/doc/csv/options/generating/quote_empty.rdoc +12 -0
- data/doc/csv/options/generating/write_converters.rdoc +25 -0
- data/doc/csv/options/generating/write_empty_value.rdoc +15 -0
- data/doc/csv/options/generating/write_headers.rdoc +29 -0
- data/doc/csv/options/generating/write_nil_value.rdoc +14 -0
- data/doc/csv/options/parsing/converters.rdoc +46 -0
- data/doc/csv/options/parsing/empty_value.rdoc +13 -0
- data/doc/csv/options/parsing/field_size_limit.rdoc +39 -0
- data/doc/csv/options/parsing/header_converters.rdoc +43 -0
- data/doc/csv/options/parsing/headers.rdoc +63 -0
- data/doc/csv/options/parsing/liberal_parsing.rdoc +19 -0
- data/doc/csv/options/parsing/nil_value.rdoc +12 -0
- data/doc/csv/options/parsing/return_headers.rdoc +22 -0
- data/doc/csv/options/parsing/skip_blanks.rdoc +31 -0
- data/doc/csv/options/parsing/skip_lines.rdoc +37 -0
- data/doc/csv/options/parsing/strip.rdoc +15 -0
- data/doc/csv/options/parsing/unconverted_fields.rdoc +27 -0
- data/doc/csv/recipes/filtering.rdoc +158 -0
- data/doc/csv/recipes/generating.rdoc +298 -0
- data/doc/csv/recipes/parsing.rdoc +545 -0
- data/doc/csv/recipes/recipes.rdoc +6 -0
- data/lib/csv.rb +1724 -568
- data/lib/csv/fields_converter.rb +1 -1
- data/lib/csv/parser.rb +1 -1
- data/lib/csv/row.rb +477 -132
- data/lib/csv/table.rb +750 -108
- data/lib/csv/version.rb +1 -1
- data/lib/csv/writer.rb +45 -4
- metadata +41 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a9f965a5db6ac6d07f525f513e212f082c137b487d73328373d0afef7bb8312
|
4
|
+
data.tar.gz: 5184ad5878a6dee8603e3b48fc820b50e3687eafb96cbcec152a8d621ceaffd6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 422a1a8751e4e8c21884848e7f465e39090b946c3febff711bb756c98e61b7e6aebf0e552adee851d653f89e9ef2772984db2b05a243c7fac8060c64def34ae0
|
7
|
+
data.tar.gz: 9d85fa756cd744e74da2ae1e89650768370566a5fd8ce1830337d528f9875d07422508eb007522ae3e7ec9c20c9ec055983f09c9b951e78441706bb2d33fdd59
|
data/NEWS.md
CHANGED
@@ -1,5 +1,115 @@
|
|
1
1
|
# News
|
2
2
|
|
3
|
+
## 3.1.8 - 2020-11-18
|
4
|
+
|
5
|
+
### Improvements
|
6
|
+
|
7
|
+
* Improved documentation.
|
8
|
+
[Patch by Burdette Lamar]
|
9
|
+
|
10
|
+
### Thanks
|
11
|
+
|
12
|
+
* Burdette Lamar
|
13
|
+
|
14
|
+
## 3.1.7 - 2020-08-04
|
15
|
+
|
16
|
+
### Improvements
|
17
|
+
|
18
|
+
* Improved document.
|
19
|
+
[GitHub#158][GitHub#160][GitHub#161]
|
20
|
+
[Patch by Burdette Lamar]
|
21
|
+
|
22
|
+
* Updated required Ruby version to 2.5.0 or later.
|
23
|
+
[GitHub#159]
|
24
|
+
[Patch by Gabriel Nagy]
|
25
|
+
|
26
|
+
* Removed stringio 0.1.3 or later dependency.
|
27
|
+
|
28
|
+
### Thanks
|
29
|
+
|
30
|
+
* Burdette Lamar
|
31
|
+
|
32
|
+
* Gabriel Nagy
|
33
|
+
|
34
|
+
## 3.1.6 - 2020-07-20
|
35
|
+
|
36
|
+
### Improvements
|
37
|
+
|
38
|
+
* Improved document.
|
39
|
+
[GitHub#127][GitHub#135][GitHub#136][GitHub#137][GitHub#139][GitHub#140]
|
40
|
+
[GitHub#141][GitHub#142][GitHub#143][GitHub#145][GitHub#146][GitHub#148]
|
41
|
+
[GitHub#148][GitHub#151][GitHub#152][GitHub#154][GitHub#155][GitHub#157]
|
42
|
+
[Patch by Burdette Lamar]
|
43
|
+
|
44
|
+
* `CSV.open`: Added support for `undef: :replace`.
|
45
|
+
[GitHub#129][Patch by Koichi ITO]
|
46
|
+
|
47
|
+
* `CSV.open`: Added support for `invalid: :replace`.
|
48
|
+
[GitHub#129][Patch by Koichi ITO]
|
49
|
+
|
50
|
+
* Don't run quotable check for invalid encoding field values.
|
51
|
+
[GitHub#131][Patch by Koichi ITO]
|
52
|
+
|
53
|
+
* Added support for specifying the target indexes and names to
|
54
|
+
`force_quotes:`.
|
55
|
+
[GitHub#153][Reported by Aleksandr]
|
56
|
+
|
57
|
+
* `CSV.generate`: Changed to use the encoding of the first non-ASCII
|
58
|
+
field rather than the encoding of ASCII only field.
|
59
|
+
|
60
|
+
* Changed to require the stringio gem 0.1.3 or later.
|
61
|
+
|
62
|
+
### Thanks
|
63
|
+
|
64
|
+
* Burdette Lamar
|
65
|
+
|
66
|
+
* Koichi ITO
|
67
|
+
|
68
|
+
* Aleksandr
|
69
|
+
|
70
|
+
## 3.1.5 - 2020-05-18
|
71
|
+
|
72
|
+
### Improvements
|
73
|
+
|
74
|
+
* Improved document.
|
75
|
+
[GitHub#124][Patch by Burdette Lamar]
|
76
|
+
|
77
|
+
### Fixes
|
78
|
+
|
79
|
+
* Added missing document files.
|
80
|
+
[GitHub#125][Reported by joast]
|
81
|
+
|
82
|
+
### Thanks
|
83
|
+
|
84
|
+
* Burdette Lamar
|
85
|
+
|
86
|
+
* joast
|
87
|
+
|
88
|
+
## 3.1.4 - 2020-05-17
|
89
|
+
|
90
|
+
### Improvements
|
91
|
+
|
92
|
+
* Improved document.
|
93
|
+
[GitHub#122][Patch by Burdette Lamar]
|
94
|
+
|
95
|
+
* Stopped to dropping stack trace for exception caused by
|
96
|
+
`CSV.parse_line`.
|
97
|
+
[GitHub#120][Reported by Kyle d'Oliveira]
|
98
|
+
|
99
|
+
### Fixes
|
100
|
+
|
101
|
+
* Fixed a bug that `:write_nil_value` or `:write_empty_value` don't
|
102
|
+
work with non `String` objects.
|
103
|
+
[GitHub#123][Reported by asm256]
|
104
|
+
|
105
|
+
### Thanks
|
106
|
+
|
107
|
+
* Burdette Lamar
|
108
|
+
|
109
|
+
* asm256
|
110
|
+
|
111
|
+
* Kyle d'Oliveira
|
112
|
+
|
3
113
|
## 3.1.3 - 2020-05-09
|
4
114
|
|
5
115
|
### Improvements
|
data/README.md
CHANGED
@@ -31,6 +31,11 @@ CSV.foreach("path/to/file.csv") do |row|
|
|
31
31
|
end
|
32
32
|
```
|
33
33
|
|
34
|
+
## Documentation
|
35
|
+
|
36
|
+
- {API}[CSV.html]: all classes, methods, and constants.
|
37
|
+
- {Recipes}[doc/csv/recipes/recipes_rdoc.html]: specific code for specific tasks.
|
38
|
+
|
34
39
|
## Development
|
35
40
|
|
36
41
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -0,0 +1,5 @@
|
|
1
|
+
* Argument +io+ should be an IO object that is:
|
2
|
+
* Open for reading; on return, the IO object will be closed.
|
3
|
+
* Positioned at the beginning.
|
4
|
+
To position at the end, for appending, use method CSV.generate.
|
5
|
+
For any other positioning, pass a preset \StringIO object instead.
|
@@ -0,0 +1,57 @@
|
|
1
|
+
====== Option +col_sep+
|
2
|
+
|
3
|
+
Specifies the \String column separator to be used
|
4
|
+
for both parsing and generating.
|
5
|
+
The \String will be transcoded into the data's \Encoding before use.
|
6
|
+
|
7
|
+
Default value:
|
8
|
+
CSV::DEFAULT_OPTIONS.fetch(:col_sep) # => "," (comma)
|
9
|
+
|
10
|
+
Using the default (comma):
|
11
|
+
str = CSV.generate do |csv|
|
12
|
+
csv << [:foo, 0]
|
13
|
+
csv << [:bar, 1]
|
14
|
+
csv << [:baz, 2]
|
15
|
+
end
|
16
|
+
str # => "foo,0\nbar,1\nbaz,2\n"
|
17
|
+
ary = CSV.parse(str)
|
18
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
19
|
+
|
20
|
+
Using +:+ (colon):
|
21
|
+
col_sep = ':'
|
22
|
+
str = CSV.generate(col_sep: col_sep) do |csv|
|
23
|
+
csv << [:foo, 0]
|
24
|
+
csv << [:bar, 1]
|
25
|
+
csv << [:baz, 2]
|
26
|
+
end
|
27
|
+
str # => "foo:0\nbar:1\nbaz:2\n"
|
28
|
+
ary = CSV.parse(str, col_sep: col_sep)
|
29
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
30
|
+
|
31
|
+
Using +::+ (two colons):
|
32
|
+
col_sep = '::'
|
33
|
+
str = CSV.generate(col_sep: col_sep) do |csv|
|
34
|
+
csv << [:foo, 0]
|
35
|
+
csv << [:bar, 1]
|
36
|
+
csv << [:baz, 2]
|
37
|
+
end
|
38
|
+
str # => "foo::0\nbar::1\nbaz::2\n"
|
39
|
+
ary = CSV.parse(str, col_sep: col_sep)
|
40
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
41
|
+
|
42
|
+
Using <tt>''</tt> (empty string):
|
43
|
+
col_sep = ''
|
44
|
+
str = CSV.generate(col_sep: col_sep) do |csv|
|
45
|
+
csv << [:foo, 0]
|
46
|
+
csv << [:bar, 1]
|
47
|
+
csv << [:baz, 2]
|
48
|
+
end
|
49
|
+
str # => "foo0\nbar1\nbaz2\n"
|
50
|
+
|
51
|
+
---
|
52
|
+
|
53
|
+
Raises an exception if parsing with the empty \String:
|
54
|
+
col_sep = ''
|
55
|
+
# Raises ArgumentError (:col_sep must be 1 or more characters: "")
|
56
|
+
CSV.parse("foo0\nbar1\nbaz2\n", col_sep: col_sep)
|
57
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
====== Option +quote_char+
|
2
|
+
|
3
|
+
Specifies the character (\String of length 1) used used to quote fields
|
4
|
+
in both parsing and generating.
|
5
|
+
This String will be transcoded into the data's \Encoding before use.
|
6
|
+
|
7
|
+
Default value:
|
8
|
+
CSV::DEFAULT_OPTIONS.fetch(:quote_char) # => "\"" (double quote)
|
9
|
+
|
10
|
+
This is useful for an application that incorrectly uses <tt>'</tt> (single-quote)
|
11
|
+
to quote fields, instead of the correct <tt>"</tt> (double-quote).
|
12
|
+
|
13
|
+
Using the default (double quote):
|
14
|
+
str = CSV.generate do |csv|
|
15
|
+
csv << ['foo', 0]
|
16
|
+
csv << ["'bar'", 1]
|
17
|
+
csv << ['"baz"', 2]
|
18
|
+
end
|
19
|
+
str # => "foo,0\n'bar',1\n\"\"\"baz\"\"\",2\n"
|
20
|
+
ary = CSV.parse(str)
|
21
|
+
ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
|
22
|
+
|
23
|
+
Using <tt>'</tt> (single-quote):
|
24
|
+
quote_char = "'"
|
25
|
+
str = CSV.generate(quote_char: quote_char) do |csv|
|
26
|
+
csv << ['foo', 0]
|
27
|
+
csv << ["'bar'", 1]
|
28
|
+
csv << ['"baz"', 2]
|
29
|
+
end
|
30
|
+
str # => "foo,0\n'''bar''',1\n\"baz\",2\n"
|
31
|
+
ary = CSV.parse(str, quote_char: quote_char)
|
32
|
+
ary # => [["foo", "0"], ["'bar'", "1"], ["\"baz\"", "2"]]
|
33
|
+
|
34
|
+
---
|
35
|
+
|
36
|
+
Raises an exception if the \String length is greater than 1:
|
37
|
+
# Raises ArgumentError (:quote_char has to be nil or a single character String)
|
38
|
+
CSV.new('', quote_char: 'xx')
|
39
|
+
|
40
|
+
Raises an exception if the value is not a \String:
|
41
|
+
# Raises ArgumentError (:quote_char has to be nil or a single character String)
|
42
|
+
CSV.new('', quote_char: :foo)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
====== Option +row_sep+
|
2
|
+
|
3
|
+
Specifies the row separator, a \String or the \Symbol <tt>:auto</tt> (see below),
|
4
|
+
to be used for both parsing and generating.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:row_sep) # => :auto
|
8
|
+
|
9
|
+
---
|
10
|
+
|
11
|
+
When +row_sep+ is a \String, that \String becomes the row separator.
|
12
|
+
The String will be transcoded into the data's Encoding before use.
|
13
|
+
|
14
|
+
Using <tt>"\n"</tt>:
|
15
|
+
row_sep = "\n"
|
16
|
+
str = CSV.generate(row_sep: row_sep) do |csv|
|
17
|
+
csv << [:foo, 0]
|
18
|
+
csv << [:bar, 1]
|
19
|
+
csv << [:baz, 2]
|
20
|
+
end
|
21
|
+
str # => "foo,0\nbar,1\nbaz,2\n"
|
22
|
+
ary = CSV.parse(str)
|
23
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
24
|
+
|
25
|
+
Using <tt>|</tt> (pipe):
|
26
|
+
row_sep = '|'
|
27
|
+
str = CSV.generate(row_sep: row_sep) do |csv|
|
28
|
+
csv << [:foo, 0]
|
29
|
+
csv << [:bar, 1]
|
30
|
+
csv << [:baz, 2]
|
31
|
+
end
|
32
|
+
str # => "foo,0|bar,1|baz,2|"
|
33
|
+
ary = CSV.parse(str, row_sep: row_sep)
|
34
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
35
|
+
|
36
|
+
Using <tt>--</tt> (two hyphens):
|
37
|
+
row_sep = '--'
|
38
|
+
str = CSV.generate(row_sep: row_sep) do |csv|
|
39
|
+
csv << [:foo, 0]
|
40
|
+
csv << [:bar, 1]
|
41
|
+
csv << [:baz, 2]
|
42
|
+
end
|
43
|
+
str # => "foo,0--bar,1--baz,2--"
|
44
|
+
ary = CSV.parse(str, row_sep: row_sep)
|
45
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
46
|
+
|
47
|
+
Using <tt>''</tt> (empty string):
|
48
|
+
row_sep = ''
|
49
|
+
str = CSV.generate(row_sep: row_sep) do |csv|
|
50
|
+
csv << [:foo, 0]
|
51
|
+
csv << [:bar, 1]
|
52
|
+
csv << [:baz, 2]
|
53
|
+
end
|
54
|
+
str # => "foo,0bar,1baz,2"
|
55
|
+
ary = CSV.parse(str, row_sep: row_sep)
|
56
|
+
ary # => [["foo", "0bar", "1baz", "2"]]
|
57
|
+
|
58
|
+
---
|
59
|
+
|
60
|
+
When +row_sep+ is the \Symbol +:auto+ (the default),
|
61
|
+
generating uses <tt>"\n"</tt> as the row separator:
|
62
|
+
str = CSV.generate do |csv|
|
63
|
+
csv << [:foo, 0]
|
64
|
+
csv << [:bar, 1]
|
65
|
+
csv << [:baz, 2]
|
66
|
+
end
|
67
|
+
str # => "foo,0\nbar,1\nbaz,2\n"
|
68
|
+
|
69
|
+
Parsing, on the other hand, invokes auto-discovery of the row separator.
|
70
|
+
|
71
|
+
Auto-discovery reads ahead in the data looking for the next <tt>\r\n</tt>, +\n+, or +\r+ sequence.
|
72
|
+
The sequence will be selected even if it occurs in a quoted field,
|
73
|
+
assuming that you would have the same line endings there.
|
74
|
+
|
75
|
+
Example:
|
76
|
+
str = CSV.generate do |csv|
|
77
|
+
csv << [:foo, 0]
|
78
|
+
csv << [:bar, 1]
|
79
|
+
csv << [:baz, 2]
|
80
|
+
end
|
81
|
+
str # => "foo,0\nbar,1\nbaz,2\n"
|
82
|
+
ary = CSV.parse(str)
|
83
|
+
ary # => [["foo", "0"], ["bar", "1"], ["baz", "2"]]
|
84
|
+
|
85
|
+
The default <tt>$INPUT_RECORD_SEPARATOR</tt> (<tt>$/</tt>) is used
|
86
|
+
if any of the following is true:
|
87
|
+
* None of those sequences is found.
|
88
|
+
* Data is +ARGF+, +STDIN+, +STDOUT+, or +STDERR+.
|
89
|
+
* The stream is only available for output.
|
90
|
+
|
91
|
+
Obviously, discovery takes a little time. Set manually if speed is important. Also note that IO objects should be opened in binary mode on Windows if this feature will be used as the line-ending translation can cause problems with resetting the document position to where it was before the read ahead.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
====== Option +force_quotes+
|
2
|
+
|
3
|
+
Specifies the boolean that determines whether each output field is to be double-quoted.
|
4
|
+
|
5
|
+
Default value:
|
6
|
+
CSV::DEFAULT_OPTIONS.fetch(:force_quotes) # => false
|
7
|
+
|
8
|
+
For examples in this section:
|
9
|
+
ary = ['foo', 0, nil]
|
10
|
+
|
11
|
+
Using the default, +false+:
|
12
|
+
str = CSV.generate_line(ary)
|
13
|
+
str # => "foo,0,\n"
|
14
|
+
|
15
|
+
Using +true+:
|
16
|
+
str = CSV.generate_line(ary, force_quotes: true)
|
17
|
+
str # => "\"foo\",\"0\",\"\"\n"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
====== Option +quote_empty+
|
2
|
+
|
3
|
+
Specifies the boolean that determines whether an empty value is to be double-quoted.
|
4
|
+
|
5
|
+
Default value:
|
6
|
+
CSV::DEFAULT_OPTIONS.fetch(:quote_empty) # => true
|
7
|
+
|
8
|
+
With the default +true+:
|
9
|
+
CSV.generate_line(['"', ""]) # => "\"\"\"\",\"\"\n"
|
10
|
+
|
11
|
+
With +false+:
|
12
|
+
CSV.generate_line(['"', ""], quote_empty: false) # => "\"\"\"\",\n"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
====== Option +write_converters+
|
2
|
+
|
3
|
+
Specifies converters to be used in generating fields.
|
4
|
+
See {Write Converters}[#class-CSV-label-Write+Converters]
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:write_converters) # => nil
|
8
|
+
|
9
|
+
With no write converter:
|
10
|
+
str = CSV.generate_line(["\na\n", "\tb\t", " c "])
|
11
|
+
str # => "\"\na\n\",\tb\t, c \n"
|
12
|
+
|
13
|
+
With a write converter:
|
14
|
+
strip_converter = proc {|field| field.strip }
|
15
|
+
str = CSV.generate_line(["\na\n", "\tb\t", " c "], write_converters: strip_converter)
|
16
|
+
str # => "a,b,c\n"
|
17
|
+
|
18
|
+
With two write converters (called in order):
|
19
|
+
upcase_converter = proc {|field| field.upcase }
|
20
|
+
downcase_converter = proc {|field| field.downcase }
|
21
|
+
write_converters = [upcase_converter, downcase_converter]
|
22
|
+
str = CSV.generate_line(['a', 'b', 'c'], write_converters: write_converters)
|
23
|
+
str # => "a,b,c\n"
|
24
|
+
|
25
|
+
See also {Write Converters}[#class-CSV-label-Write+Converters]
|
@@ -0,0 +1,15 @@
|
|
1
|
+
====== Option +write_empty_value+
|
2
|
+
|
3
|
+
Specifies the object that is to be substituted for each field
|
4
|
+
that has an empty \String.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:write_empty_value) # => ""
|
8
|
+
|
9
|
+
Without the option:
|
10
|
+
str = CSV.generate_line(['a', '', 'c', ''])
|
11
|
+
str # => "a,\"\",c,\"\"\n"
|
12
|
+
|
13
|
+
With the option:
|
14
|
+
str = CSV.generate_line(['a', '', 'c', ''], write_empty_value: "x")
|
15
|
+
str # => "a,x,c,x\n"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
====== Option +write_headers+
|
2
|
+
|
3
|
+
Specifies the boolean that determines whether a header row is included in the output;
|
4
|
+
ignored if there are no headers.
|
5
|
+
|
6
|
+
Default value:
|
7
|
+
CSV::DEFAULT_OPTIONS.fetch(:write_headers) # => nil
|
8
|
+
|
9
|
+
Without +write_headers+:
|
10
|
+
file_path = 't.csv'
|
11
|
+
CSV.open(file_path,'w',
|
12
|
+
:headers => ['Name','Value']
|
13
|
+
) do |csv|
|
14
|
+
csv << ['foo', '0']
|
15
|
+
end
|
16
|
+
CSV.open(file_path) do |csv|
|
17
|
+
csv.shift
|
18
|
+
end # => ["foo", "0"]
|
19
|
+
|
20
|
+
With +write_headers+":
|
21
|
+
CSV.open(file_path,'w',
|
22
|
+
:write_headers=> true,
|
23
|
+
:headers => ['Name','Value']
|
24
|
+
) do |csv|
|
25
|
+
csv << ['foo', '0']
|
26
|
+
end
|
27
|
+
CSV.open(file_path) do |csv|
|
28
|
+
csv.shift
|
29
|
+
end # => ["Name", "Value"]
|