dumb_delimited 1.1.0 → 2.0.0
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 +4 -4
- data/CHANGELOG.md +24 -3
- data/README.md +40 -26
- data/dumb_delimited.gemspec +0 -2
- data/lib/dumb_delimited.rb +216 -88
- data/lib/dumb_delimited/version.rb +1 -1
- metadata +3 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '049c4e61de54be67ca44c3b01d7f97fd5f1972953415ed339a14c55ec0c6d0c4'
|
4
|
+
data.tar.gz: 38f8e15d6a44ab8af339cf4da8e8addb232c8118ba1b8603e31deb934ca95989
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47aea2da44d901f3606f9e997f6024dd0413f131bec2f7f53db73863c7975a4102996f45a6eef2ffecdbf97d12c2b27132b9689dbb51f1911b1fa4f3419d9c4f
|
7
|
+
data.tar.gz: a4e067dccc15b8588ab721da6e12e5175be63febce3b25198178324944e60b248a1e9927c23b738beba4df6752525c451a8eeaf65553bacf79a017a069a4dbb4
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,29 @@
|
|
1
|
+
## 2.0.0
|
2
|
+
|
3
|
+
* [BREAKING] Do not activate `options[:converters]` by default
|
4
|
+
* [BREAKING] Remove *pleasant_path* dependency. This is considered a
|
5
|
+
breaking change because *pleasant_path* adds extension methods to
|
6
|
+
several Ruby core classes that consumer code may depend on.
|
7
|
+
* [BREAKING] Remove `append_to_file` instance method
|
8
|
+
* [BREAKING] Rename `each_in_file` class method to `read_each`
|
9
|
+
* Add `parse_each` class method
|
10
|
+
* Rename `parse_file` class method to `read`, and alias as `parse_file`
|
11
|
+
* Rename `parse_text` class method to `parse`, and alias as `parse_text`
|
12
|
+
* Add `write` class method
|
13
|
+
* Add `append` class method
|
14
|
+
* Add `eol` parameter to `to_s` instance method
|
15
|
+
* Add `DumbDelimited.csv`, `.psv`, and `.tsv` convenience shortcuts
|
16
|
+
* Activate `options[:liberal_parsing]` by default
|
17
|
+
* Make `options` inheritable
|
18
|
+
* Fix behavior when `options[:headers]` is set
|
19
|
+
* Fix behavior when `options[:write_headers]` is set
|
20
|
+
|
21
|
+
|
1
22
|
## 1.1.0
|
2
23
|
|
3
|
-
*
|
4
|
-
*
|
5
|
-
*
|
24
|
+
* Add `parse_text` class method
|
25
|
+
* Add `append_to_file` instance method
|
26
|
+
* Fix gem load on case-sensitive file systems
|
6
27
|
|
7
28
|
|
8
29
|
## 1.0.0
|
data/README.md
CHANGED
@@ -33,7 +33,7 @@ used as a superclass or simply assigned to a constant.
|
|
33
33
|
```ruby
|
34
34
|
class Product < DumbDelimited[:sku, :name, :base_price, :sale_price]
|
35
35
|
def on_sale?
|
36
|
-
sale_price < base_price
|
36
|
+
sale_price.to_f < base_price.to_f
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -42,57 +42,71 @@ Customer.delimiter = "|"
|
|
42
42
|
```
|
43
43
|
|
44
44
|
Because "customers.psv" is pipe-delimited, we also set the delimiter
|
45
|
-
for the Customer class. By default, model
|
46
|
-
|
45
|
+
for the Customer class. By default, a model class uses a comma (`","`)
|
46
|
+
as its delimiter. Whenever a delimiter is set, it applies to all future
|
47
47
|
IO operations for that model class.
|
48
48
|
|
49
|
-
|
49
|
+
Convenience shortcuts that create a model class and set its delimiter
|
50
|
+
are also provided for a few common delimiters. Notably,
|
51
|
+
`DumbDelimited::psv` for a model class with a pipe (`"|"`) delimiter.
|
52
|
+
Thus, the `Customer` class could alternatively be written as:
|
50
53
|
|
51
54
|
```ruby
|
52
|
-
|
53
|
-
customers = Customer.parse_file("customers.psv")
|
55
|
+
Customer = DumbDelimited.psv(:name, :email, :address)
|
54
56
|
```
|
55
57
|
|
56
|
-
|
58
|
+
Using our model classes, we can read each flat file, and recieve an
|
59
|
+
array of model objects:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
products = Product.read("products.csv")
|
63
|
+
customers = Customer.read("customers.psv")
|
64
|
+
```
|
65
|
+
|
66
|
+
However, this will load the entire contents of each file into memory.
|
57
67
|
Let's say our customers file is very large, and we would prefer to
|
58
|
-
iterate over it rather than load it all into memory at
|
59
|
-
we can use the `
|
60
|
-
|
61
|
-
|
62
|
-
|
68
|
+
iterate over it one row at a time rather than load it all into memory at
|
69
|
+
once. To do so, we can use the `read_each` method. Below is a complete
|
70
|
+
example in which we load our product data, create a listing of products
|
71
|
+
on sale, and iterate over our customers, notifying each customer of the
|
72
|
+
sale products:
|
63
73
|
|
64
74
|
```ruby
|
65
|
-
products = Product.
|
75
|
+
products = Product.read("products.csv")
|
66
76
|
|
67
77
|
listing = products.select(&:on_sale?).map do |product|
|
68
78
|
"* #{product.name} (#{product.sale_price})"
|
69
79
|
end.join("\n")
|
70
80
|
|
71
|
-
Customer.
|
72
|
-
message =
|
73
|
-
|
74
|
-
|
81
|
+
Customer.read_each("customers.psv") do |customer|
|
82
|
+
message = <<~MESSAGE
|
83
|
+
Hi #{customer.name}!
|
84
|
+
|
85
|
+
The following products are on sale:
|
86
|
+
|
87
|
+
#{listing}
|
88
|
+
MESSAGE
|
75
89
|
|
76
90
|
notify(customer.email, message)
|
77
91
|
end
|
78
92
|
```
|
79
93
|
|
80
94
|
Let's say the sale is now over, and we want to change our sale prices
|
81
|
-
back to our base prices.
|
82
|
-
|
83
|
-
offers a fluent API for writing files. To finish our task, we use the
|
84
|
-
`Array#write_to_file` method provided by *pleasant_path*, which in turn
|
85
|
-
invokes `Product#to_s` (provided by *dumb_delimited*) on each model
|
86
|
-
object.
|
95
|
+
back to our base prices. We can load our product data, modify it
|
96
|
+
directly, and finally persist it back with the `write` method:
|
87
97
|
|
88
98
|
```ruby
|
89
|
-
Product.
|
99
|
+
products = Product.read("products.csv")
|
100
|
+
|
101
|
+
products.each do |product|
|
90
102
|
product.sale_price = product.base_price
|
91
|
-
end
|
103
|
+
end
|
104
|
+
|
105
|
+
Product.write("products.csv", products)
|
92
106
|
```
|
93
107
|
|
94
108
|
For a more detailed explanation of the *dumb_delimited* API, browse the
|
95
|
-
[
|
109
|
+
[API documentation](http://www.rubydoc.info/gems/dumb_delimited/).
|
96
110
|
|
97
111
|
|
98
112
|
## Installation
|
data/dumb_delimited.gemspec
CHANGED
@@ -20,8 +20,6 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_runtime_dependency "pleasant_path", "~> 1.0"
|
24
|
-
|
25
23
|
spec.add_development_dependency "bundler", "~> 1.15"
|
26
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
25
|
spec.add_development_dependency "minitest", "~> 5.0"
|
data/lib/dumb_delimited.rb
CHANGED
@@ -1,28 +1,29 @@
|
|
1
1
|
require "csv"
|
2
|
-
require "pleasant_path"
|
3
2
|
require_relative "dumb_delimited/version"
|
4
3
|
|
5
4
|
|
6
5
|
module DumbDelimited
|
7
6
|
|
8
7
|
# Returns a model class for delimited data consisting of the specified
|
9
|
-
# columns
|
10
|
-
# {https://ruby-
|
11
|
-
# manipulation via accessor methods, via indexing by column name,
|
12
|
-
# via indexing by column number. See {ClassMethods} and
|
13
|
-
# {InstanceMethods} for the
|
8
|
+
# +columns+. The returned class inherits from Ruby's
|
9
|
+
# {https://docs.ruby-lang.org/en/trunk/Struct.html +Struct+}, allowing
|
10
|
+
# data manipulation via accessor methods, via indexing by column name,
|
11
|
+
# and via indexing by column number. See {ClassMethods} and
|
12
|
+
# {InstanceMethods} for the additional methods the returned class
|
13
|
+
# provides.
|
14
14
|
#
|
15
15
|
# @example
|
16
16
|
# class Product < DumbDelimited[:sku, :name, :base_price, :sale_price]
|
17
17
|
# def on_sale?
|
18
|
-
# sale_price < base_price
|
18
|
+
# sale_price.to_f < base_price.to_f
|
19
19
|
# end
|
20
20
|
# end
|
21
21
|
#
|
22
|
+
# @example
|
22
23
|
# Customer = DumbDelimited[:name, :email, :address]
|
23
24
|
#
|
24
|
-
# @param columns [
|
25
|
-
# @return [Class]
|
25
|
+
# @param columns [Array<Symbol>]
|
26
|
+
# @return [Class<Struct>]
|
26
27
|
def self.[](*columns)
|
27
28
|
Struct.new(*columns) do
|
28
29
|
extend DumbDelimited::ClassMethods
|
@@ -30,42 +31,111 @@ module DumbDelimited
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
34
|
+
# Convenience shortcut to create a model class and set
|
35
|
+
# {ClassMethods#delimiter} to +","+.
|
36
|
+
#
|
37
|
+
# Note: This method exists mostly for parity with {psv} and {tsv}.
|
38
|
+
# Unless +CSV::DEFAULT_OPTIONS+ has been modified, the delimiter will
|
39
|
+
# already default to +","+.
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# # This...
|
43
|
+
# Point = DumbDelimited.csv(:x, :y, :z)
|
44
|
+
#
|
45
|
+
# # ...is equivalent to:
|
46
|
+
# Point = DumbDelimited[:x, :y, :z]
|
47
|
+
# Point.delimiter = ","
|
48
|
+
#
|
49
|
+
# @param columns [Array<Symbol>]
|
50
|
+
# @return [Class<Struct>]
|
51
|
+
def self.csv(*columns)
|
52
|
+
klass = self[*columns]
|
53
|
+
klass.delimiter = ","
|
54
|
+
klass
|
55
|
+
end
|
56
|
+
|
57
|
+
# Convenience shortcut to create a model class and set
|
58
|
+
# {ClassMethods#delimiter} to +"|"+.
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# # This...
|
62
|
+
# Point = DumbDelimited.psv(:x, :y, :z)
|
63
|
+
#
|
64
|
+
# # ...is equivalent to:
|
65
|
+
# Point = DumbDelimited[:x, :y, :z]
|
66
|
+
# Point.delimiter = "|"
|
67
|
+
#
|
68
|
+
# @param columns [Array<Symbol>]
|
69
|
+
# @return [Class<Struct>]
|
70
|
+
def self.psv(*columns)
|
71
|
+
klass = self[*columns]
|
72
|
+
klass.delimiter = "|"
|
73
|
+
klass
|
74
|
+
end
|
75
|
+
|
76
|
+
# Convenience shortcut to create a model class and set
|
77
|
+
# {ClassMethods#delimiter} to <code>"\t"</code>.
|
78
|
+
#
|
79
|
+
# @example
|
80
|
+
# # This...
|
81
|
+
# Point = DumbDelimited.tsv(:x, :y, :z)
|
82
|
+
#
|
83
|
+
# # ...is equivalent to:
|
84
|
+
# Point = DumbDelimited[:x, :y, :z]
|
85
|
+
# Point.delimiter = "\t"
|
86
|
+
#
|
87
|
+
# @param columns [Array<Symbol>]
|
88
|
+
# @return [Class<Struct>]
|
89
|
+
def self.tsv(*columns)
|
90
|
+
klass = self[*columns]
|
91
|
+
klass.delimiter = "\t"
|
92
|
+
klass
|
93
|
+
end
|
94
|
+
|
33
95
|
end
|
34
96
|
|
35
97
|
|
36
98
|
module DumbDelimited::ClassMethods
|
37
99
|
|
38
|
-
# Returns the
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# about available options, see Ruby's
|
42
|
-
# {http://ruby-doc.org/stdlib/libdoc/csv/rdoc/CSV.html#method-c-new
|
43
|
-
# CSV module}.
|
100
|
+
# Returns the CSV options Hash. The Hash is not +dup+ed and can be
|
101
|
+
# modified directly. Any modifications will be applied to all future
|
102
|
+
# IO operations for the model class.
|
44
103
|
#
|
45
|
-
#
|
104
|
+
# For detailed information about available options, see Ruby's
|
105
|
+
# {https://docs.ruby-lang.org/en/trunk/CSV.html#method-c-new CSV
|
106
|
+
# class}.
|
107
|
+
#
|
108
|
+
# @return [Hash<Symbol, Object>]
|
46
109
|
def options
|
47
|
-
@options ||=
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
110
|
+
@options ||= if superclass == Struct
|
111
|
+
CSV::DEFAULT_OPTIONS.merge(
|
112
|
+
skip_blanks: true,
|
113
|
+
liberal_parsing: true,
|
114
|
+
)
|
115
|
+
else
|
116
|
+
superclass.options.dup
|
117
|
+
end
|
52
118
|
end
|
53
119
|
|
54
|
-
# Sets the
|
55
|
-
#
|
56
|
-
#
|
57
|
-
# detailed information about available options, see Ruby's
|
58
|
-
# {http://ruby-doc.org/stdlib/libdoc/csv/rdoc/CSV.html#method-c-new
|
59
|
-
# CSV module}.
|
120
|
+
# Sets the CSV options Hash. The entire Hash is replaced, and the new
|
121
|
+
# values will be applied to all future IO operations for the model
|
122
|
+
# class. To set options individually, see {options}.
|
60
123
|
#
|
61
|
-
#
|
62
|
-
|
63
|
-
|
124
|
+
# For detailed information about available options, see Ruby's
|
125
|
+
# {https://docs.ruby-lang.org/en/trunk/CSV.html#method-c-new CSV
|
126
|
+
# class}.
|
127
|
+
#
|
128
|
+
# @param opts [Hash<Symbol, Object>]
|
129
|
+
# @return [Hash<Symbol, Object>]
|
130
|
+
def options=(opts)
|
131
|
+
@options = opts
|
64
132
|
end
|
65
133
|
|
66
134
|
# Returns the column delimiter used in IO operations. Defaults to a
|
67
135
|
# comma (<code>","</code>).
|
68
136
|
#
|
137
|
+
# Equivalent to <code>options[:col_sep]</code>.
|
138
|
+
#
|
69
139
|
# @return [String]
|
70
140
|
def delimiter
|
71
141
|
self.options[:col_sep]
|
@@ -76,6 +146,8 @@ module DumbDelimited::ClassMethods
|
|
76
146
|
# delimiter can be safely chosen, and all IO operations will quote
|
77
147
|
# field values as necessary.
|
78
148
|
#
|
149
|
+
# Equivalent to <code>options[:col_sep] = delim</code>.
|
150
|
+
#
|
79
151
|
# @example
|
80
152
|
# Point = DumbDelimited[:x, :y, :z]
|
81
153
|
# p = Point.new(1, 2, 3)
|
@@ -83,9 +155,10 @@ module DumbDelimited::ClassMethods
|
|
83
155
|
# Point.delimiter = "|"
|
84
156
|
# p.to_s # == "1|2|3"
|
85
157
|
#
|
86
|
-
# @param
|
87
|
-
|
88
|
-
|
158
|
+
# @param delim [String]
|
159
|
+
# @return [String]
|
160
|
+
def delimiter=(delim)
|
161
|
+
self.options[:col_sep] = delim
|
89
162
|
end
|
90
163
|
|
91
164
|
# Parses a single delimited line into a model object.
|
@@ -95,34 +168,53 @@ module DumbDelimited::ClassMethods
|
|
95
168
|
# Point.parse_line("1,2,3") # == Point.new(1, 2, 3)
|
96
169
|
#
|
97
170
|
# @param line [String]
|
98
|
-
# @return [
|
171
|
+
# @return [Struct]
|
99
172
|
def parse_line(line)
|
100
|
-
|
173
|
+
parse_each(line).first
|
101
174
|
end
|
102
175
|
|
103
|
-
# Parses a string into an array of model objects.
|
176
|
+
# Parses a string or IO object into an array of model objects.
|
104
177
|
#
|
105
178
|
# @example
|
106
179
|
# Point = DumbDelimited[:x, :y, :z]
|
107
|
-
# Point.
|
180
|
+
# Point.parse("1,2,3\n4,5,6\n7,8,9\n")
|
108
181
|
# # == [
|
109
182
|
# # Point.new(1, 2, 3),
|
110
183
|
# # Point.new(4, 5, 6),
|
111
184
|
# # Point.new(7, 8, 9)
|
112
185
|
# # ]
|
113
186
|
#
|
114
|
-
# @param
|
115
|
-
# @return [Array<
|
116
|
-
def
|
117
|
-
|
118
|
-
|
119
|
-
|
187
|
+
# @param data [String, IO]
|
188
|
+
# @return [Array<Struct>]
|
189
|
+
def parse(data)
|
190
|
+
parse_each(data).to_a
|
191
|
+
end
|
192
|
+
|
193
|
+
alias_method :parse_text, :parse
|
194
|
+
|
195
|
+
# Parses a string or IO object one line at a time, yielding a model
|
196
|
+
# object for each line.
|
197
|
+
#
|
198
|
+
# An Enumerator is returned if no block is given.
|
199
|
+
#
|
200
|
+
# @overload parse_each(data, &block)
|
201
|
+
# @param data [String, IO]
|
202
|
+
# @yieldparam model [Struct]
|
203
|
+
# @return [void]
|
204
|
+
#
|
205
|
+
# @overload parse_each(data)
|
206
|
+
# @param data [String, IO]
|
207
|
+
# @return [Enumerator<Struct>]
|
208
|
+
def parse_each(data, &block)
|
209
|
+
return to_enum(__method__, data) unless block_given?
|
210
|
+
|
211
|
+
csv_each(CSV.new(data, self.options), &block)
|
120
212
|
end
|
121
213
|
|
122
|
-
# Parses
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
214
|
+
# Parses a file into an array of model objects. This will load the
|
215
|
+
# entire contents of the file into memory, and may not be suitable for
|
216
|
+
# large files. To iterate over file contents without loading it all
|
217
|
+
# into memory at once, use {read_each}.
|
126
218
|
#
|
127
219
|
# @example
|
128
220
|
# # CONTENTS OF FILE "points.csv":
|
@@ -131,7 +223,7 @@ module DumbDelimited::ClassMethods
|
|
131
223
|
# # 7,8,9
|
132
224
|
#
|
133
225
|
# Point = DumbDelimited[:x, :y, :z]
|
134
|
-
# Point.
|
226
|
+
# Point.read("points.csv")
|
135
227
|
# # == [
|
136
228
|
# # Point.new(1, 2, 3),
|
137
229
|
# # Point.new(4, 5, 6),
|
@@ -139,27 +231,77 @@ module DumbDelimited::ClassMethods
|
|
139
231
|
# # ]
|
140
232
|
#
|
141
233
|
# @param path [String, Pathname]
|
142
|
-
# @return [Array<
|
143
|
-
def
|
144
|
-
|
234
|
+
# @return [Array<Struct>]
|
235
|
+
def read(path)
|
236
|
+
read_each(path).to_a
|
145
237
|
end
|
146
238
|
|
147
|
-
|
148
|
-
|
149
|
-
#
|
150
|
-
#
|
151
|
-
#
|
152
|
-
# methods, such as +Enumerator#to_a+, will cause the entire contents
|
153
|
-
# of the file to be loaded into memory regardless.
|
239
|
+
alias_method :parse_file, :read
|
240
|
+
|
241
|
+
# Parses a file one line at a time, yielding a model object for each
|
242
|
+
# line. This avoids loading the entire contents of the file into
|
243
|
+
# memory at once.
|
154
244
|
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
|
245
|
+
# An Enumerator is returned if no block is given. Note that some
|
246
|
+
# Enumerator methods, such as +Enumerator#to_a+, can cause the entire
|
247
|
+
# contents of the file to be loaded into memory.
|
248
|
+
#
|
249
|
+
# @overload read_each(path, &block)
|
250
|
+
# @param path [String, Pathname]
|
251
|
+
# @yieldparam model [Struct]
|
252
|
+
# @return [void]
|
253
|
+
#
|
254
|
+
# @overload read_each(path)
|
255
|
+
# @param path [String, Pathname]
|
256
|
+
# @return [Enumerator<Struct>]
|
257
|
+
def read_each(path, &block)
|
159
258
|
return to_enum(__method__, path) unless block_given?
|
160
259
|
|
161
|
-
CSV.
|
162
|
-
|
260
|
+
CSV.open(path, self.options) do |csv|
|
261
|
+
csv_each(csv, &block)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# Writes a collection of model objects to a file in delimited format.
|
266
|
+
# The previous contents of the file are overwritten, unless +append+
|
267
|
+
# is set to true.
|
268
|
+
#
|
269
|
+
# Column headers are written to the file if +:write_headers+ in
|
270
|
+
# {options} is set to true *and* either +append+ is false or the file
|
271
|
+
# is empty / non-existent. The column headers will be derived from
|
272
|
+
# either the value of +:headers+ in {options} if it is an Array, or
|
273
|
+
# otherwise from the columns defined by the model.
|
274
|
+
#
|
275
|
+
# @param path [String, Pathname]
|
276
|
+
# @param models [Enumerable<Struct>]
|
277
|
+
# @param append [Boolean]
|
278
|
+
# @return [void]
|
279
|
+
def write(path, models, append: false)
|
280
|
+
mode = append ? "a" : "w"
|
281
|
+
write_headers = options[:write_headers] && !(append && File.exist?(path) && File.size(path) > 0)
|
282
|
+
headers = (!options[:headers].is_a?(Array) && write_headers) ? members : options[:headers]
|
283
|
+
|
284
|
+
CSV.open(path, mode, **options, write_headers: write_headers, headers: headers) do |csv|
|
285
|
+
models.each{|model| csv << model }
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Appends a collection of model objects to a file in delimited format.
|
290
|
+
# Convenience shortcut for {write} with +append: true+.
|
291
|
+
#
|
292
|
+
# @param path [String, Pathname]
|
293
|
+
# @param models [Enumerable<Struct>]
|
294
|
+
# @return [void]
|
295
|
+
def append(path, models)
|
296
|
+
write(path, models, append: true)
|
297
|
+
end
|
298
|
+
|
299
|
+
private
|
300
|
+
|
301
|
+
def csv_each(csv, &block)
|
302
|
+
csv.each do |row|
|
303
|
+
row = row.fields if row.is_a?(CSV::Row)
|
304
|
+
block.call(self.new(*row))
|
163
305
|
end
|
164
306
|
end
|
165
307
|
|
@@ -169,33 +311,19 @@ end
|
|
169
311
|
module DumbDelimited::InstanceMethods
|
170
312
|
|
171
313
|
# Serializes a model object to a delimited string, using the delimiter
|
172
|
-
# specified by {ClassMethods
|
314
|
+
# specified by {ClassMethods#delimiter}. By default, the string will
|
315
|
+
# not end with a line terminator. To end the string with a line
|
316
|
+
# terminator designated by +:row_sep+ in {ClassMethods#options}, set
|
317
|
+
# +eol+ to true.
|
173
318
|
#
|
319
|
+
# @param eol [Boolean]
|
174
320
|
# @return [String]
|
175
|
-
def to_s
|
176
|
-
|
177
|
-
end
|
321
|
+
def to_s(eol = false)
|
322
|
+
row_sep = eol ? self.class.options[:row_sep] : -""
|
178
323
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
# This method is convenient when working with single model objects,
|
183
|
-
# for example, appending a single entry to a log file. However, it is
|
184
|
-
# not recommended for use with an array of model objects, due to the
|
185
|
-
# overhead of opening and closing the file for each append.
|
186
|
-
#
|
187
|
-
# @example
|
188
|
-
# Point = DumbDelimited[:x, :y, :z]
|
189
|
-
# Point.new(1, 2, 3).append_to_file("out.txt") # == Point.new(1, 2, 3)
|
190
|
-
# File.read("out.txt") # == "1,2,3\n"
|
191
|
-
# Point.new(4, 5, 6).append_to_file("out.txt") # == Point.new(4, 5, 6)
|
192
|
-
# File.read("out.txt") # == "1,2,3\n4,5,6\n"
|
193
|
-
#
|
194
|
-
# @param file [String, Pathname]
|
195
|
-
# @return [self]
|
196
|
-
def append_to_file(file)
|
197
|
-
CSV.generate_line(self, self.class.options).append_to_file(file)
|
198
|
-
self
|
324
|
+
CSV.generate(**self.class.options, row_sep: row_sep, write_headers: false) do |csv|
|
325
|
+
csv << self
|
326
|
+
end
|
199
327
|
end
|
200
328
|
|
201
329
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dumb_delimited
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Hefner
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-07-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: pleasant_path
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: bundler
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,8 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
102
|
- !ruby/object:Gem::Version
|
117
103
|
version: '0'
|
118
104
|
requirements: []
|
119
|
-
|
120
|
-
rubygems_version: 2.7.6
|
105
|
+
rubygems_version: 3.0.1
|
121
106
|
signing_key:
|
122
107
|
specification_version: 4
|
123
108
|
summary: Library for unsophisticated delimited flat file IO
|