A link between dry-validation
and
mutations
gems. This gem enables
support for dry-validation
schemas to be used within legacy mutations
-based
syntax.
Add this line to your application's Gemfile:
gem 'dry-mutations'
And then execute:
$ bundle
Or install it yourself as:
$ gem install dry-mutations
class ComposedMutation < Mutations::Command
...
def validate
additional_validate(input1, input2)
@nested = NestedMutation.new(inputs, input1: input1, input2: input2)
unless @nested.validation_outcome.success?
@nested.validation_outcome.errors.each do |key, error|
add_error(key.to_sym, error.symbolic, error.message)
end
end
end
def execute
@nested.run!
end
end
class ComposedValidation < Mutations::Command
prepend ::Dry::Mutations::Extensions::Command
prepend ::Dry::Mutations::Extensions::Dummy
...
def validate
additional_validate(input1, input2)
end
end
class ComposedTransform < Mutations::Command
prepend ::Dry::Mutations::Extensions::Command
...
def execute
inputs.merge(input1: input1, input2: input2)
end
end
class ComposedMutation
extend ::Dry::Mutations::Transactions::DSL
chain do
validate ComposedValidation
transform ComposedTransform
mutate NestedMutation
end
end
Basically, any call syntax is supported:
# preferred
ComposedMutation.(input) # returns (Either ∨ Outcome) object
# legacy
ComposedMutation.run(input) # returns (Either ∨ Outcome) object
ComposedMutation.new(input).run # returns (Either ∨ Outcome) object
ComposedMutation.run!(input) # throws Mutation::ValidationException
ComposedMutation.new(input).run! # throws Mutation::ValidationException
Prepend a ::Dry::Mutations::Extensions::Command
module to your Mutation::Command
instance:
class MyMutation < Mutations::Command
prepend ::Dry::Mutations::Extensions::Command
required do
model :company, class: 'Profile'
model :user
hash :maturity_set do
string :maturity_choice, in: %w(spot forward_days fixed_date)
optional do
hash :maturity_days_set do
integer :days # For spot or forward_days options
end
hash :maturity_date_set do
date :date # When passing a fixed date
end
end
end
...
It is possible to mix standard mutations’ syntax with dry-rb
schemas:
class MyMutation < Mutations::Command
prepend ::Dry::Mutations::Extensions::Command
required do
model :company, class: 'Profile'
end
schema do
required(:maturity_choice).filled(:str?, included_in?: %w(spot forward_days fixed_date))
end
Basically, everything written here is applicable. Syntax to include the nested schema is as simple as:
UserSchema = Dry::Validation.Schema do
required(:email).filled(:str?)
required(:name).filled(:str?)
required(:address).schema(AddressSchema)
end
or, in legacy mutations
syntax (NB! This is not yet implemented!):
required do
string :email
string :name
schema :address, AddressSchema
end
let!(:command) do
Class.new(::Mutations::Command) do
prepend ::Dry::Mutations::Extensions::Command
required { string :name, max_length: 5 }
schema { required(:amount).filled(:int?, gt?: 0) }
def execute
@inputs
end
end
end
outcome = command.new(name: 'John', amount: 42).run
outcome.right?
#⇒ true
outcome.either.value
#⇒ { 'name' => 'John', 'amount' => 42 }
outcome = command.new(name: 'John Donne', amount: -500).run
outcome.right?
#⇒ false
outcome.left?
#⇒ true
outcome.either
#⇒ Left({
# "name"=>#<Dry::Mutations::Errors::ErrorAtom:0x00000003b4e7b0
# @key="name",
# @symbol=:max_length,
# @message="size cannot be greater than 5",
# @index=0,
# @dry_message=#<Dry::Validation::Message
# predicate=:max_size?
# path=[:name]
# text="size cannot be greater than 5"
# options={:args=>[5], :rule=>:name, :each=>false}>>,
# "amount"=>#<Dry::Mutations::Errors::ErrorAtom:0x00000003b4e508
# @key="amount",
# @symbol=:gt?,
# @message="must be greater than 0",
# @index=1,
# @dry_message=#<Dry::Validation::Message
# predicate=:gt?
# path=[:amount]
# text="must be greater than 0"
# options={:args=>[0], :rule=>:amount, :each=>false}>>
# })
outcome.either.value
#⇒ the hash ⇑ above
expect(outcome.match { |m| m.success(&:keys) }).to match_array(%w(amount name))
expect(outcome.match { |m| m.failure(&:keys) }).to be_nil
ENV['GLOBAL_DRY_MUTATIONS'] = 'true' && rake
That way all mutations all over the system will be patched/injected with new functionality. This is untested in all possible environments.
Bug reports are very welcome!
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/dry-mutations. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
The gem is available as open source under the terms of the MIT License.