Skip to content

Commit

Permalink
Add Dry::Struct.merge
Browse files Browse the repository at this point in the history
It's also aliased as `.compose`
  • Loading branch information
waiting-for-dev committed Jan 5, 2020
1 parent ecdf7bb commit 8535da0
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 0 deletions.
23 changes: 23 additions & 0 deletions docsite/source/hash-schemas.html.md
Expand Up @@ -124,6 +124,29 @@ user_hash['name' => 'Jane', 'city' => 'London']
# => Dry::Types::UnknownKeysError: unexpected keys [:city] in Hash input
```

### Merging schemas

Similar than inheritance, two schemas can be merged.
The resulting schema will have the sum of both sets
of attributes.

user_hash = Types::Hash.schema(
name: Types::String
)

address_hash = Types::Hash.schema(
address: Types::String
)

user_with_address_schema = user_hash.merge(address_hash)

user_with_address_schema['name' => 'Jane', 'address' => 'C/ Foo']
# => { name: 'Jane', address: 'C/ Foo' }
```
Keep in mind that key transformations from the caller schema are preserved,
while each attribute keeps the type transformations from its original hash.
### Transforming types
A schema can transform types with a block. For example, the following code makes all keys optional:
Expand Down
3 changes: 3 additions & 0 deletions lib/dry/types/hash/constructor.rb
Expand Up @@ -13,6 +13,9 @@ class Constructor < ::Dry::Types::Constructor
def constructor_type
::Dry::Types::Hash::Constructor
end

def merge
end

# @return [Lax]
#
Expand Down
13 changes: 13 additions & 0 deletions lib/dry/types/schema.rb
Expand Up @@ -284,6 +284,19 @@ def lax
Lax.new(schema(keys.map(&:lax)))
end

# Merges given schema keys into current schema.
#
# A new instance is returned.
#
# @param schema [Schema]
# @return [Schema]
#
# @api public
def merge(schema)
with(keys: keys + schema.keys)
end
alias_method :compose, :merge

private

# @param [Array<Dry::Types::Schema::Keys>] keys
Expand Down
41 changes: 41 additions & 0 deletions spec/dry/types/schema_spec.rb
Expand Up @@ -501,4 +501,45 @@ def self.meta
specify { expect(type.transform_keys?).to be(true) }
end
end

describe '#merge' do
it 'adds keys and values from given schema' do
schema1 = Dry::Types['hash'].schema(name: 'string')
schema2 = Dry::Types['hash'].schema(age: 'integer')

schema = schema1.merge(schema2)

expect(schema.keys.map(&:name)).to eq([:name, :age])
end

it 'gives preference to given struct' do
schema1 = Dry::Types['hash'].schema(foo: 'string')
schema2 = Dry::Types['hash'].schema(foo: 'integer')

schema = schema1.merge(schema2)

expect(schema[foo: 1]).to eq(foo: 1)
end

it 'keeps type transformations from both schemas' do
transf1 = ->(key) { key.constructor { 'Transf 1' } }
transf2 = ->(key) { key.constructor { 'Transf 2' } }
schema1 = Dry::Types['hash'].with_type_transform(transf1).schema(foo: 'string')
schema2 = Dry::Types['hash'].with_type_transform(transf2).schema(bar: 'string')

schema = schema1.merge(schema2)

expect(schema[foo: 'foo', bar: 'bar']).to eq(foo: 'Transf 1', bar: 'Transf 2')
end

it 'preserves key transformations from the caller schema' do
schema1 = Dry::Types['hash'].schema(foo: 'string').with_key_transform(&:to_sym)
transf2 = ->(key) { (key + '_').to_sym }
schema2 = Dry::Types['hash'].schema(bar: 'string').with_key_transform(transf2)

schema = schema1.merge(schema2)

expect(schema['foo' => 'foo', 'bar' => 'bar']).to eq(foo: 'foo', bar: 'bar')
end
end
end

0 comments on commit 8535da0

Please sign in to comment.