https://github.com/am-kantox/dry-mutations
Mutations gem interface implemented with `dry-rb`’s validation schemas.
https://github.com/am-kantox/dry-mutations
Last synced: about 1 month ago
JSON representation
Mutations gem interface implemented with `dry-rb`’s validation schemas.
- Host: GitHub
- URL: https://github.com/am-kantox/dry-mutations
- Owner: am-kantox
- License: mit
- Created: 2016-07-08T12:58:45.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2019-02-26T10:04:42.000Z (about 6 years ago)
- Last Synced: 2025-03-21T12:22:03.544Z (about 1 month ago)
- Language: Ruby
- Size: 160 KB
- Stars: 2
- Watchers: 2
- Forks: 2
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Dry::Mutations
[](https://travis-ci.org/am-kantox/dry-mutations)
[](https://codeclimate.com/github/am-kantox/dry-mutations)---
A link between [`dry-validation`](http://dry-rb.org/gems/dry-validation) and
[`mutations`](http://github.com/cypriss/mutations) gems. This gem enables
support for `dry-validation` schemas to be used within legacy `mutations`-based
syntax.## Installation
Add this line to your application's Gemfile:
```ruby
gem 'dry-mutations'
```And then execute:
$ bundle
Or install it yourself as:
$ gem install dry-mutations
## Was ⇒ Is
### Was
```ruby
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
enddef execute
@nested.run!
end
end
```### Is
```ruby
class ComposedValidation < Mutations::Command
prepend ::Dry::Mutations::Extensions::Command
prepend ::Dry::Mutations::Extensions::Sieve...
def validate
additional_validate(input1, input2)
end
endclass ComposedTransform < Mutations::Command
prepend ::Dry::Mutations::Extensions::Command...
def execute
inputs.merge(input1: input1, input2: input2)
end
endclass ComposedMutation
extend ::Dry::Mutations::Transactions::DSL
chain do
validate ComposedValidation
transform ComposedTransform
mutate NestedMutation
end
end
```### Call syntax
Basically, any call syntax is supported:
```ruby
# 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
```## Usage
### Enable extensions for the specific mutation’s command
Prepend a `::Dry::Mutations::Extensions::Command` module to your `Mutation::Command` instance:
```ruby
class MyMutation < Mutations::Command
prepend ::Dry::Mutations::Extensions::Commandrequired 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, default: 3 # For spot or forward_days options
end
hash :maturity_date_set do
date :date # When passing a fixed date
end
end
end
...
```### `dry-validation` syntax
It is possible to mix standard mutations’ syntax with `dry-rb` schemas:
```ruby
class MyMutation < Mutations::Command
prepend ::Dry::Mutations::Extensions::Commandrequired do
model :company, class: 'Profile'
endschema do
required(:maturity_choice).filled(:str?, included_in?: %w(spot forward_days fixed_date))
end
```### Reusing schema
Basically, everything [written here](http://dry-rb.org/gems/dry-validation/reusing-schemas/)
is applicable. Syntax to include the nested schema is as simple as:```ruby
UserSchema = Dry::Validation.Schema do
required(:email).filled(:str?)
required(:name).filled(:str?)
required(:address).schema(AddressSchema)
end
```or, in legacy `mutations` syntax (**NB! Starting with `0.99.9`!**):
```ruby
required do
string :name
schema :address, AddressSchema
string :email
end
```## `ActiveRecord::Relation` support
```ruby
schema(Dry::Mutations.Schema do
required(:slaves).filled(relation?: Slave)
end)
```## Combining dry schemas with mutation-like syntax
Since version `0.99.9`, one might pass the `Dry::Validation::Schema` directly
to legacy mutations syntax:```ruby
required do
model :user
schema :address, AddressSchema # AddressSchema = ::Dry::Validation.Schema {}
date: Date.today
end
```---
Since version `0.11.1`, one might pass the instance of `Dry::Validation::Schema`
and/or `Dry::Validation::Form` instance to `schema` mutation DSL.Such a block might be _only one_, and it _must be_ the first DSL in the mutation.
**NB** this is not a preferred way to do things, but it might be useful to _share_
schemas (unlikely the above, this will _embed_ the schema, rather than _nest_ it.)### Correct
```ruby
Class.new(::Mutations::Command) do
prepend ::Dry::Mutations::Extensions::Command
prepend ::Dry::Mutations::Extensions::Sieveschema(::Dry::Validation.Form do
required(:integer_value).filled(:int?, gt?: 0)
required(:date_value).filled(:date?)
required(:bool_value).filled(:bool?)
end)required do
integer :forty_two
string :hello
end
end
```### Incorrect
```ruby
Class.new(::Mutations::Command) do
prepend ::Dry::Mutations::Extensions::Command
prepend ::Dry::Mutations::Extensions::Sieverequired do
integer :forty_two
string :hello
endschema(::Dry::Validation.Form do
required(:integer_value).filled(:int?, gt?: 0)
required(:date_value).filled(:date?)
required(:bool_value).filled(:bool?)
end)
end
```## Declare the resulting type of a schema
Using the approach above, one might start with a schema type declaration:
```ruby
# this line must be a first declaration
schema(::Dry::Validation.Form {})# now continue with generic `schema {}` blocks to append features:
schema do
required(:integer_value).filled(:int?, gt?: 0)
required(:date_value).filled(:date?)
required(:bool_value).filled(:bool?)
end
```
see [`schema_spec.rb`](https://github.com/am-kantox/dry-mutations/blob/master/spec/dry/mutations/schema_spec.rb)
for an inspiration.### Subschema’s type
Startign with `0.99.100` we accept `type:` parameter in call to `schema`:
```ruby
schema type: :form do
...
end
```## Dealing with `outcome`
### Command
```ruby
let!(:command) do
Class.new(::Mutations::Command) do
prepend ::Dry::Mutations::Extensions::Commandrequired { string :name, max_length: 5 }
schema { required(:amount).filled(:int?, gt?: 0) }def execute
@inputs
end
end
end
```### Using `Either` monad
```ruby
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"=>#[5], :rule=>:name, :each=>false}>>,
# "amount"=>#[0], :rule=>:amount, :each=>false}>>
# })
outcome.either.value
#⇒ the hash ⇑ above
```### Using `Matcher`
```ruby
expect(outcome.match { |m| m.success(&:keys) }).to match_array(%w(amount name))
expect(outcome.match { |m| m.failure(&:keys) }).to be_nil
```## Turn On Globally (use with caution!)
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!
## Changelog
#### 0.99.1
Support for direct input parameters invocation. 100%-compatibility with `mutations`:```ruby
def validate # input ≡ { date: nil }
date < Date.now
end
```#### 1.1.0
More handy `chain`s, better `dry-rb` integration, improvements.#### 0.99.0
Support for `default:` guard. 99%-compatibility with `mutations`## Development
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](https://rubygems.org).
## Contributing
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](contributor-covenant.org) code of conduct.
## License
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).