bumblebee 2.0.1 → 2.1.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 +4 -0
- data/Gemfile.lock +3 -2
- data/README.md +104 -0
- data/bumblebee.gemspec +1 -0
- data/lib/bumblebee/bumblebee.rb +48 -4
- data/lib/bumblebee/template.rb +34 -2
- data/lib/bumblebee/version.rb +1 -1
- data/spec/bumblebee/bumblebee_spec.rb +22 -2
- data/spec/bumblebee/template_spec.rb +72 -0
- data/spec/spec_helper.rb +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e63f0ffdddd7bea22fb6da178b542556cde964543a2d7c4c68ef6e85b3bb06f3
|
4
|
+
data.tar.gz: 28a41de9de06653b0d2d41d52818bb690d786f124e6e84be7e867a496da2a8c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3153683a9564325488926f6bde6cb77ce59d1674ac6fcb3a9e60911a21870a26905b9d14d851f660d2c0c74b7df34986eadfd488a85d21734b3a7ff515e3453c
|
7
|
+
data.tar.gz: 9d19b1ea86c98101bb523a8e2ab1d6e0a04230c6b6616a2ba76b0e9bf584ccf2fcb4184a9fb14980e467a5d93749a104bb2b87a26313e48b4b5d1e5653c6376e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# 2.1.0 (March 4th, 2019)
|
2
|
+
|
3
|
+
* Added two domain-specific language options for configuring Column objects through Template: subclass Template class or pass in block. This is optional and is backwards compatible.
|
4
|
+
|
1
5
|
# 2.0.1 (February 5th, 2019)
|
2
6
|
|
3
7
|
* Updated acts_as_hashable Dependency
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
bumblebee (2.0
|
4
|
+
bumblebee (2.1.0)
|
5
5
|
acts_as_hashable (~> 1.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -95,10 +95,11 @@ PLATFORMS
|
|
95
95
|
DEPENDENCIES
|
96
96
|
bumblebee!
|
97
97
|
guard-rspec (~> 4.7)
|
98
|
+
pry
|
98
99
|
rspec (~> 3.8)
|
99
100
|
rubocop (~> 0.63.1)
|
100
101
|
simplecov (~> 0.16.1)
|
101
102
|
simplecov-console (~> 0.4.2)
|
102
103
|
|
103
104
|
BUNDLED WITH
|
104
|
-
1.17.
|
105
|
+
1.17.3
|
data/README.md
CHANGED
@@ -244,6 +244,110 @@ The two main methods:
|
|
244
244
|
|
245
245
|
also accept custom options that [Ruby's CSV::new](https://ruby-doc.org/stdlib-2.6/libdoc/csv/rdoc/CSV.html#method-c-new) accepts. The only caveat is that Bumblebee needs headers for its mapping, so it overrides the header options.
|
246
246
|
|
247
|
+
#### Template DSL
|
248
|
+
|
249
|
+
You can choose to pass in a block for template/column specification if you would rather prefer a code-first approach over a configuration-first approach.
|
250
|
+
|
251
|
+
##### Using Blocks
|
252
|
+
|
253
|
+
````ruby
|
254
|
+
csv = Bumblebee.generate_csv(objects) do |t|
|
255
|
+
t.column :id, header: 'ID #',
|
256
|
+
to_object: ->(o) { o['ID #'].to_i }
|
257
|
+
|
258
|
+
t.column :first, header: 'First Name',
|
259
|
+
to_csv: %i[name first],
|
260
|
+
to_object: ->(o) { { first: o['First Name'] } }
|
261
|
+
end
|
262
|
+
|
263
|
+
objects = Bumblebee.parse_csv(data) do |t|
|
264
|
+
t.column :id, header: 'ID #',
|
265
|
+
to_object: ->(o) { o['ID #'].to_i }
|
266
|
+
|
267
|
+
t.column :first, header: 'First Name',
|
268
|
+
to_csv: %i[name first],
|
269
|
+
to_object: ->(o) { { first: o['First Name'] } }
|
270
|
+
end
|
271
|
+
````
|
272
|
+
|
273
|
+
##### Interacting Directly With ::Bumblebee::Template
|
274
|
+
|
275
|
+
You can also choose to interact/build templates directly instead of going through the top-level API:
|
276
|
+
|
277
|
+
````ruby
|
278
|
+
template = Bumblebee::Template.new(columns)
|
279
|
+
|
280
|
+
# or
|
281
|
+
|
282
|
+
template = Bumblebee::Template.new do |t|
|
283
|
+
t.column :id, header: 'ID #',
|
284
|
+
to_object: ->(o) { o['ID #'].to_i }
|
285
|
+
|
286
|
+
t.column :first, header: 'First Name',
|
287
|
+
to_csv: %i[name first],
|
288
|
+
to_object: ->(o) { { first: o['First Name'] } }
|
289
|
+
end
|
290
|
+
````
|
291
|
+
|
292
|
+
Template class has the same top-level methods:
|
293
|
+
|
294
|
+
````ruby
|
295
|
+
csv = template.generate_csv(objects)
|
296
|
+
|
297
|
+
objects = template.parse_csv(data)
|
298
|
+
````
|
299
|
+
|
300
|
+
##### Subclassing ::Bumblebee::Template
|
301
|
+
|
302
|
+
Another option is to subclass Template and declare your columns at the class-level:
|
303
|
+
|
304
|
+
````ruby
|
305
|
+
class PersonTemplate < Bumblebee::Template
|
306
|
+
column :id, header: 'ID #',
|
307
|
+
to_object: ->(o) { o['ID #'].to_i }
|
308
|
+
|
309
|
+
column :first, header: 'First Name',
|
310
|
+
to_csv: %i[name first],
|
311
|
+
to_object: ->(o) { { first: o['First Name'] } }
|
312
|
+
end
|
313
|
+
|
314
|
+
# Usage
|
315
|
+
|
316
|
+
template = PersonTemplate.new
|
317
|
+
csv = template.generate_csv(objects)
|
318
|
+
objects = template.parse_csv(data)
|
319
|
+
````
|
320
|
+
|
321
|
+
##### Column Precedence
|
322
|
+
|
323
|
+
The preceding examples showed three ways to declare columns, and each is additive to the next (in the following order):
|
324
|
+
|
325
|
+
1. Class level (parent-first)
|
326
|
+
2. Argument level (passed into constructor)
|
327
|
+
3. Block level
|
328
|
+
|
329
|
+
To illustrate all three:
|
330
|
+
|
331
|
+
````ruby
|
332
|
+
class PersonTemplate < Bumblebee::Template # first
|
333
|
+
column :id, header: 'ID #',
|
334
|
+
to_object: ->(o) { o['ID #'].to_i }
|
335
|
+
|
336
|
+
column :first, header: 'First Name',
|
337
|
+
to_csv: %i[name first],
|
338
|
+
to_object: ->(o) { { first: o['First Name'] } }
|
339
|
+
end
|
340
|
+
|
341
|
+
# Usage
|
342
|
+
|
343
|
+
template = PersonTemplate.new({ field: :middle, header: 'Middle Name' }) do |t| # second
|
344
|
+
t.column :last, header: 'Last Name' # third
|
345
|
+
end
|
346
|
+
|
347
|
+
````
|
348
|
+
|
349
|
+
When executed to generate a CSV, the columns would be (in order): ```ID #, First Name, Middle Name, Last Name.```
|
350
|
+
|
247
351
|
## Contributing
|
248
352
|
|
249
353
|
### Development Environment Configuration
|
data/bumblebee.gemspec
CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.add_dependency('acts_as_hashable', '~>1.0')
|
26
26
|
|
27
27
|
s.add_development_dependency('guard-rspec', '~>4.7')
|
28
|
+
s.add_development_dependency('pry')
|
28
29
|
s.add_development_dependency('rspec', '~> 3.8')
|
29
30
|
s.add_development_dependency('rubocop', '~>0.63.1')
|
30
31
|
s.add_development_dependency('simplecov', '~>0.16.1')
|
data/lib/bumblebee/bumblebee.rb
CHANGED
@@ -19,12 +19,56 @@ require_relative 'template'
|
|
19
19
|
# procedural way using these.
|
20
20
|
module Bumblebee
|
21
21
|
class << self
|
22
|
-
|
23
|
-
|
22
|
+
# Two signatures for consumption:
|
23
|
+
#
|
24
|
+
# ::Bumblebee.generate_csv(columns = [], objects = [], options = {})
|
25
|
+
#
|
26
|
+
# or
|
27
|
+
#
|
28
|
+
# ::Bumblebee.generate_csv(objects = [], options = {}) do |t|
|
29
|
+
# t.column :id, header: 'ID #'
|
30
|
+
# t.column :first, header: 'First Name'
|
31
|
+
# end
|
32
|
+
def generate_csv(*args, &block)
|
33
|
+
if block_given?
|
34
|
+
objects = args[0] || []
|
35
|
+
options = args[1] || {}
|
36
|
+
else
|
37
|
+
objects = args[1] || []
|
38
|
+
options = args[2] || {}
|
39
|
+
end
|
40
|
+
|
41
|
+
template(args, &block).generate_csv(objects, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Two signatures for consumption:
|
45
|
+
#
|
46
|
+
# ::Bumblebee.parse_csv(columns = [], string = '', options = {})
|
47
|
+
#
|
48
|
+
# or
|
49
|
+
#
|
50
|
+
# ::Bumblebee.parse_csv(string = '', options = {}) do |t|
|
51
|
+
# t.column :id, header: 'ID #'
|
52
|
+
# t.column :first, header: 'First Name'
|
53
|
+
# end
|
54
|
+
def parse_csv(*args, &block)
|
55
|
+
if block_given?
|
56
|
+
string = args[0] || ''
|
57
|
+
options = args[1] || {}
|
58
|
+
else
|
59
|
+
string = args[1] || ''
|
60
|
+
options = args[2] || {}
|
61
|
+
end
|
62
|
+
|
63
|
+
template(args, &block).parse_csv(string, options)
|
24
64
|
end
|
25
65
|
|
26
|
-
|
27
|
-
|
66
|
+
private
|
67
|
+
|
68
|
+
def template(args, &block)
|
69
|
+
columns = block_given? ? [] : (args[0] || [])
|
70
|
+
|
71
|
+
::Bumblebee::Template.new(columns, &block)
|
28
72
|
end
|
29
73
|
end
|
30
74
|
end
|
data/lib/bumblebee/template.rb
CHANGED
@@ -12,10 +12,42 @@ module Bumblebee
|
|
12
12
|
# generate_csv: take in an array of objects and return a string (CSV contents)
|
13
13
|
# parse_csv: take in a string and return an array of OpenStruct objects
|
14
14
|
class Template
|
15
|
+
class << self
|
16
|
+
def columns
|
17
|
+
@columns ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
def column(field, opts = {})
|
21
|
+
columns << ::Bumblebee::Column.make(opts.merge(field: field))
|
22
|
+
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def all_columns
|
27
|
+
ancestors.reverse_each.inject([]) do |arr, ancestor|
|
28
|
+
ancestor < ::Bumblebee::Template ? arr + ancestor.columns : arr
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
15
33
|
attr_reader :columns
|
16
34
|
|
17
|
-
def initialize(columns = [])
|
18
|
-
@columns = ::Bumblebee::Column.array(columns)
|
35
|
+
def initialize(columns = [], &block)
|
36
|
+
@columns = self.class.all_columns + ::Bumblebee::Column.array(columns)
|
37
|
+
|
38
|
+
return unless block_given?
|
39
|
+
|
40
|
+
if block.arity == 1
|
41
|
+
yield self
|
42
|
+
else
|
43
|
+
instance_eval(&block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# New DSL method to use for adding columns
|
48
|
+
def column(field, opts = {})
|
49
|
+
@columns << ::Bumblebee::Column.make(opts.merge(field: field))
|
50
|
+
self
|
19
51
|
end
|
20
52
|
|
21
53
|
# Return array of strings (headers)
|
data/lib/bumblebee/version.rb
CHANGED
@@ -35,12 +35,22 @@ describe ::Bumblebee do
|
|
35
35
|
|
36
36
|
let(:quoted_csv) { "\"name\",\"dob\"\n\"Matt\",\"1901-01-03\"\n\"Nathan\",\"1931-09-03\"\n" }
|
37
37
|
|
38
|
-
it 'should generate a csv' do
|
38
|
+
it 'should generate a csv using column argument' do
|
39
39
|
actual = Bumblebee.generate_csv(columns, people)
|
40
40
|
|
41
41
|
expect(actual).to eq(csv)
|
42
42
|
end
|
43
43
|
|
44
|
+
it 'should generate a csv using block' do
|
45
|
+
actual = Bumblebee.generate_csv(people) do |t|
|
46
|
+
columns.each do |column|
|
47
|
+
t.column column[:field]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(actual).to eq(csv)
|
52
|
+
end
|
53
|
+
|
44
54
|
it 'should generate a csv and accept options' do
|
45
55
|
options = {
|
46
56
|
force_quotes: true
|
@@ -51,12 +61,22 @@ describe ::Bumblebee do
|
|
51
61
|
expect(actual).to eq(quoted_csv)
|
52
62
|
end
|
53
63
|
|
54
|
-
it 'should parse a csv' do
|
64
|
+
it 'should parse a csv using columns argument' do
|
55
65
|
objects = Bumblebee.parse_csv(columns, csv)
|
56
66
|
|
57
67
|
expect(objects).to eq(people)
|
58
68
|
end
|
59
69
|
|
70
|
+
it 'should parse a csv using columns block' do
|
71
|
+
objects = Bumblebee.parse_csv(csv) do |t|
|
72
|
+
columns.each do |column|
|
73
|
+
t.column column[:field]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
expect(objects).to eq(people)
|
78
|
+
end
|
79
|
+
|
60
80
|
it 'should parse a csv with columns in different order than headers' do
|
61
81
|
objects = ::Bumblebee.parse_csv(reverse_columns, csv)
|
62
82
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'spec_helper'
|
11
|
+
|
12
|
+
describe ::Bumblebee::Template do
|
13
|
+
let(:field) { :id }
|
14
|
+
let(:opts) { { header: 'ID #' } }
|
15
|
+
|
16
|
+
context 'with a blank template' do
|
17
|
+
let(:template) { ::Bumblebee::Template.new }
|
18
|
+
|
19
|
+
subject { template }
|
20
|
+
|
21
|
+
it '#column should add a column' do
|
22
|
+
subject.column(field, opts)
|
23
|
+
|
24
|
+
expect(subject.columns.length).to eq(1)
|
25
|
+
expect(subject.columns.first.field).to eq(field)
|
26
|
+
expect(subject.columns.first.header).to eq(opts[:header])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#initialize' do
|
31
|
+
specify 'that initialization accepts a block (with arity) for column creation' do
|
32
|
+
template = ::Bumblebee::Template.new do |t|
|
33
|
+
t.column field, opts
|
34
|
+
end
|
35
|
+
|
36
|
+
expect(template.columns.length).to eq(1)
|
37
|
+
expect(template.columns.first.field).to eq(field)
|
38
|
+
expect(template.columns.first.header).to eq(opts[:header])
|
39
|
+
end
|
40
|
+
|
41
|
+
specify 'that initialization accepts a block (without arity) for column creation' do
|
42
|
+
template = ::Bumblebee::Template.new do
|
43
|
+
column :id, header: 'ID #'
|
44
|
+
end
|
45
|
+
|
46
|
+
expect(template.columns.length).to eq(1)
|
47
|
+
expect(template.columns.first.field).to eq(field)
|
48
|
+
expect(template.columns.first.header).to eq(opts[:header])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'subclassing' do
|
53
|
+
class PersonTemplate < ::Bumblebee::Template
|
54
|
+
column :id, header: 'ID #'
|
55
|
+
end
|
56
|
+
|
57
|
+
class CompletePersonTemplate < PersonTemplate
|
58
|
+
column :first, header: 'First Name'
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should use class-level declared columns in the correct parenting hierarchical order' do
|
62
|
+
template = CompletePersonTemplate.new
|
63
|
+
|
64
|
+
expect(template.columns.length).to eq(2)
|
65
|
+
expect(template.columns.first.field).to eq(:id)
|
66
|
+
expect(template.columns.first.header).to eq('ID #')
|
67
|
+
|
68
|
+
expect(template.columns.last.field).to eq(:first)
|
69
|
+
expect(template.columns.last.header).to eq('First Name')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bumblebee
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: acts_as_hashable
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rspec
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -124,6 +138,7 @@ files:
|
|
124
138
|
- lib/bumblebee/version.rb
|
125
139
|
- spec/bumblebee/bumblebee_spec.rb
|
126
140
|
- spec/bumblebee/column_spec.rb
|
141
|
+
- spec/bumblebee/template_spec.rb
|
127
142
|
- spec/fixtures/custom_readme_example.csv
|
128
143
|
- spec/fixtures/simple_readme_example.csv
|
129
144
|
- spec/spec_helper.rb
|
@@ -153,6 +168,7 @@ summary: Object/CSV Mapper
|
|
153
168
|
test_files:
|
154
169
|
- spec/bumblebee/bumblebee_spec.rb
|
155
170
|
- spec/bumblebee/column_spec.rb
|
171
|
+
- spec/bumblebee/template_spec.rb
|
156
172
|
- spec/fixtures/custom_readme_example.csv
|
157
173
|
- spec/fixtures/simple_readme_example.csv
|
158
174
|
- spec/spec_helper.rb
|