bumblebee 2.0.1 → 2.1.0

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: 836e600a1fb3228d7704e561fac6a6677e1944b42d624f2a2c9f740035f9c35b
4
- data.tar.gz: 0f222b4d2742bf467837dd89621310b1721e9bae089dc2c69e4db7f7bdc399ba
3
+ metadata.gz: e63f0ffdddd7bea22fb6da178b542556cde964543a2d7c4c68ef6e85b3bb06f3
4
+ data.tar.gz: 28a41de9de06653b0d2d41d52818bb690d786f124e6e84be7e867a496da2a8c6
5
5
  SHA512:
6
- metadata.gz: 31e3cc0c89fe47147b793a1b43e4c8e41e5a1d1580e3955073ac9019e7c45b9e1b34e5c0d1991cb39e0175b40299f612c0d22c20e0d03fffb238897d66f733b7
7
- data.tar.gz: b223693ec13620e7d54ca3e4eed04c2face5774fba8c6e6645c4e44a070b0105e0f940dcc37306af479e62c7da906ca86b00bfd4737a1bd84bca21467692948c
6
+ metadata.gz: 3153683a9564325488926f6bde6cb77ce59d1674ac6fcb3a9e60911a21870a26905b9d14d851f660d2c0c74b7df34986eadfd488a85d21734b3a7ff515e3453c
7
+ data.tar.gz: 9d19b1ea86c98101bb523a8e2ab1d6e0a04230c6b6616a2ba76b0e9bf584ccf2fcb4184a9fb14980e467a5d93749a104bb2b87a26313e48b4b5d1e5653c6376e
@@ -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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bumblebee (2.0.1)
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.2
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
@@ -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')
@@ -19,12 +19,56 @@ require_relative 'template'
19
19
  # procedural way using these.
20
20
  module Bumblebee
21
21
  class << self
22
- def generate_csv(columns, objects, options = {})
23
- ::Bumblebee::Template.new(columns).generate_csv(objects, options)
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
- def parse_csv(columns, string, options = {})
27
- ::Bumblebee::Template.new(columns).parse_csv(string, options)
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
@@ -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)
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module Bumblebee
11
- VERSION = '2.0.1'
11
+ VERSION = '2.1.0'
12
12
  end
@@ -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
@@ -8,6 +8,7 @@
8
8
  #
9
9
 
10
10
  require 'stringio'
11
+ require 'pry'
11
12
 
12
13
  require 'simplecov'
13
14
  require 'simplecov-console'
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.1
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-02-05 00:00:00.000000000 Z
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