Dry::Validation::Rails
Rails plugin for implementing dry-validation gem for your Active Record Validations.
Installation
gem 'dry-validation-rails', github: 'nejdetkadir/dry-validation-rails', branch: 'main'
Install the gem and add to the application's Gemfile by executing:
$ bundle add dry-validation-rails
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install dry-validation-rails
Configuration
Dry::Validation::Rails.configure do |config|
config.default_schema_prefix = 'ApplicationContract::'
config.default_schema_suffix = 'Schema'
end
Dry::Validation::Rails.configuration.default_schema_prefix = 'ApplicationContract::'
Dry::Validation::Rails.configuration.default_schema_suffix = 'Contract'
Usage
Simply drop in followability to a model:
class User < ActiveRecord::Base
validates_with_dry
end
class UserSchema < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:email).filled(:string)
end
end
user = User.new(name: '', email: '')
user.valid? # => false
user.errors.full_messages # => ["Name is missing", "Name must be a string", "Email is missing", "Email must be a string"]
With DRY Schema
You can use dry-schema for defining your schema and use it in your model.
class User < ActiveRecord::Base
validates_with_dry
end
UserSchema = Dry::Schema.Params do
required(:name).filled(:string)
required(:email).filled(:string)
end
user = User.new(name: '', email: '')
user.valid? # => false
user.errors.full_messages # => ["Name is missing", "Name must be a string", "Email is missing", "Email must be a string"]
With DRY Validation Contract
You can use dry-validation for defining your schema and use it in your model.
class User < ActiveRecord::Base
validates_with_dry
end
class UserSchema < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:email).filled(:string)
end
end
user = User.new(name: '', email: '')
user.valid? # => false
user.errors.full_messages # => ["Name is missing", "Name must be a string", "Email is missing", "Email must be a string"]
Passing options
You can pass options to the schema by passing a hash to the validates_with_dry
method.
Prefix and Suffix
class User < ActiveRecord::Base
# You can pass custom prefix and suffix for validator class
validates_with_dry schema_prefix: 'ApplicationContract::', schema_suffix: 'Contract'
end
class Application
class UserContract < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:email).filled(:string)
end
end
end
user = User.new(name: '', email: '')
user.valid? # => false
user.errors.full_messages # => ["Name is missing", "Name must be a string", "Email is missing", "Email must be a string"]
Custom Schema
class User < ActiveRecord::Base
validates_with_dry schema: UserCustomSchema # custom schema
end
class UserCustomSchema < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:email).filled(:string)
end
end
user = User.new(name: '', email: '')
user.valid? # => false
user.errors.full_messages # => ["Name is missing", "Name must be a string", "Email is missing", "Email must be a string"]
Pass record to contract
We can define it as an external dependency that will be injected to the contract's constructor for Dry Validation Contract. You can pass record to the contract like this:
class User < ActiveRecord::Base
validates_with_dry schema: UserSchema, pass_record_to_contract: true # default key is :record
end
class UserSchema < Dry::Validation::Contract
option :record # this is the record that will be passed to the contract as an option
params do
required(:name).filled(:string)
required(:email).filled(:string)
required(:age).filled(:integer).value(gteq?: 18)
end
rule(:age) do
key.failure('must be greater than 20') if record.role.admin? && value < 20
end
end
user = User.new(name: '', email: '')
user.valid? # => false
user.errors.full_messages # => ["Name is missing", "Name must be a string", "Email is missing", "Email must be a string"]
Custom key for record
class User < ActiveRecord::Base
validates_with_dry schema: UserSchema, pass_record_to_contract: { as: :user }
end
class UserSchema < Dry::Validation::Contract
option :user
params do
required(:name).filled(:string)
required(:email).filled(:string)
required(:age).filled(:integer).value(gteq?: 18)
end
rule(:age) do
key.failure('must be greater than 20') if user.role.admin? && value < 20
end
end
user = User.new(name: '', email: '')
user.valid? # => false
user.errors.full_messages # => ["Name is missing", "Name must be a string", "Email is missing", "Email must be a string"]
Run validation on update or create
Default is :all
which means it will run on both create and update. You can pass :create
or :update
to run validation only on create or update.
class User < ActiveRecord::Base
validates_with_dry schema: UserSchema, on: :create
end
class UserSchema < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:email).filled(:string)
end
end
user = User.first
user.name = ''
user.valid? # => true
user.errors.full_messages # => []
Run validation with if or unless
You can pass if
or unless
option as a symbol or a proc to run validation only if the condition is true.
if
class User < ActiveRecord::Base
validates_with_dry schema: AdminSchema, if: Proc.new { |user| user.admin? }
validates_with_dry schema: UserSchema, if: :normal_user?
def admin?
role.admin?
end
def normal_user?
!admin?
end
end
unless
class User < ActiveRecord::Base
validates_with_dry schema: AdminSchema, unless: :normal_user?
validates_with_dry schema: UserSchema, unless: Proc.new { |user| user.admin? }
def admin?
role.admin?
end
def normal_user?
!admin?
end
end
JSON / JSONB Attributes
You can validate json/jsonb columns with like this:
class User < ActiveRecord::Base
validates_with_dry schema: UserSchema
end
class UserSchema < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:email).filled(:string)
required(:preferences).hash do
required(:color).filled(:string)
required(:font).filled(:string)
end
end
end
user = User.new(name: '', email: '', preferences: { color: '', font: '' })
user.valid? # => false
user.errors.full_messages # => ["Name is missing", "Name must be a string", "Email is missing", "Email must be a string", "Preferences is missing", "Preferences must be a hash", "Preferences.color is missing", "Preferences.color must be a string", "Preferences.font is missing", "Preferences.font must be a string"]
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake test
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 the created tag, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/nejdetkadir/dry-validation-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
License
The gem is available as open source under the terms of the MIT License.
Code of Conduct
Everyone interacting in the Dry::Validation::Rails project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.