Skip to content

Commit

Permalink
Merge pull request #1 from Verseth/feature/collections
Browse files Browse the repository at this point in the history
Add support for collections
  • Loading branch information
Verseth authored Oct 11, 2023
2 parents dbd4d45 + 3618432 commit eca0f64
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
CI: true
strategy:
matrix:
ruby-version: ['2.7', '3.0', '3.1', '3.2']
ruby-version: ['3.0', '3.1', '3.2']
steps:
- name: Checkout code
uses: actions/checkout@v3
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
/pkg/
/spec/reports/
/tmp/
.byebug_history
ignore.*
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ inherit_gem:
rubocop-espago: rubocop.yml

AllCops:
TargetRubyVersion: 2.7
TargetRubyVersion: 3.0
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in diggable.gemspec
gemspec

gem 'byebug', '~> 11.1' # debugger
gem 'minitest', '~> 5.0' # test framework
gem 'rake', '~> 13.0' # automation tasks
gem 'rubocop-espago', '~> 1.0' # ruby linter
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ GEM
ast (2.4.2)
backport (1.2.0)
benchmark (0.2.1)
byebug (11.1.3)
diff-lcs (1.5.0)
e2mmap (0.1.0)
jaro_winkler (1.5.4)
Expand Down Expand Up @@ -77,6 +78,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
byebug (~> 11.1)
minitest (~> 5.0)
rake (~> 13.0)
rubocop-espago (~> 1.0)
Expand Down
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class PaymentInstrument < Shale::Mapper
attribute :expiration_month, ::Shale::Type::Integer
end

class Transaction < ::Shale::Mapper
class Transaction < Shale::Mapper
include Shale::Builder

attribute :cvv_code, Shale::Type::String
Expand Down Expand Up @@ -142,6 +142,52 @@ non-primitive types have been overridden to accept blocks.
When a block is given to such a getter, it instantiates an empty object
of its type and yields it to the block.

### Collections

Whenever you call a getter with a block for a collection attribute, the built object will be appended to the array.

Let's define a schema like this.

```rb
class Client < Shale::Mapper
include Shale::Builder

attribute :first_name, Shale::Type::String
attribute :last_name, Shale::Type::String
attribute :email, Shale::Type::String
end

class Transaction < Shale::Mapper
include Shale::Builder

attribute :clients, Client, collection: true
end
```

You can easily build add new clients to the collection like so:

```rb
transaction = Transaction.build do |t|
# this will be added as the first element of the collection
t.clients do |c|
c.first_name = 'Foo'
c.last_name = 'Bar'
end

# this will be added as the second element of the collection
t.clients do |c|
c.first_name = 'Grant'
c.last_name = 'Taylor'
end
end

p transaction.clients
# [
# #<Client:0x00000001066c2828 @first_name="Foo", @last_name="Bar", @email=nil>,
# #<Client:0x00000001066c24b8 @first_name="Grant", @last_name="Taylor", @email=nil>
# ]
```

### Conditional building

This DSL makes it extremely easy to build nested
Expand Down
17 changes: 16 additions & 1 deletion lib/shale/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,25 @@ def build
# @param name [String, Symbol]
# @param type [Class]
# @return [void]
def attribute(name, type, *args, **kwargs, &block)
def attribute(name, type, *args, collection: false, **kwargs, &block)
super
return unless type < ::Shale::Mapper

if collection
@builder_methods_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
def #{name} # def clients
return super unless block_given? # return super unless block_given?
#
arr = self.#{name} ||= [] # arr = self.clients ||= []
object = #{type}.new # object = Client.new
yield(object) # yield(object)
arr << object # arr << object
object # object
end # end
RUBY
return
end

@builder_methods_module.class_eval <<~RUBY, __FILE__, __LINE__ + 1
def #{name} # def amount
return super unless block_given? # return super unless block_given?
Expand Down
2 changes: 1 addition & 1 deletion shale-builder.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
spec.description = spec.summary
spec.homepage = 'https://github.com/Verseth/ruby-shale-builder'
spec.license = 'MIT'
spec.required_ruby_version = '>= 2.7.0'
spec.required_ruby_version = '>= 3.0.0'

spec.metadata['homepage_uri'] = spec.homepage
spec.metadata['source_code_uri'] = spec.homepage
Expand Down
42 changes: 42 additions & 0 deletions test/shale/builder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class TestEnhancedTransactionType < TestTransactionType
attribute :client_data, TestClientDataType
end

class TestMultipleClientTransactionType < TestTransactionType
attribute :clients, TestClientDataType, collection: true
end

context 'inheritance' do
should 'correctly set up a class after inheriting' do
mod_parent = TestTransactionType.builder_methods_module
Expand Down Expand Up @@ -152,6 +156,44 @@ class TestEnhancedTransactionType < TestTransactionType
assert_equal 'USD', obj.amount.currency
end

should 'build an object with a collection attribute' do
obj = TestMultipleClientTransactionType.build do |t|
t.cvv_code = '321'
t.amount do |a|
a.value = 45.0
a.currency = 'USD'
end
t.clients do |c|
c.first_name = 'Mateusz'
c.last_name = 'Gobbins'
c.email = '[email protected]'
end
t.clients do |c|
c.first_name = 'Michal'
c.last_name = 'Zapow'
c.email = '[email protected]'
end
end

assert obj.is_a?(TestTransactionType)
assert_equal '321', obj.cvv_code
assert obj.amount.is_a?(TestAmountType)
assert_equal 45.0, obj.amount.value
assert_equal 'USD', obj.amount.currency
assert obj.clients.is_a?(::Array), "Should be and Array, got: #{obj.clients.class.inspect}"
assert_equal 2, obj.clients.length

cli = obj.clients.first
assert_equal 'Mateusz', cli.first_name
assert_equal 'Gobbins', cli.last_name
assert_equal '[email protected]', cli.email

cli = obj.clients.last
assert_equal 'Michal', cli.first_name
assert_equal 'Zapow', cli.last_name
assert_equal '[email protected]', cli.email
end

should 'build an object through the alt DSL' do
obj = TestTransactionType.build do |t|
t.cvv_code = '321'
Expand Down
1 change: 1 addition & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@

require 'minitest/autorun'
require 'shoulda-context'
require 'byebug'

0 comments on commit eca0f64

Please sign in to comment.