dry-validation
History
Rails 3
mass-assignment protection
# == Schema Information
#
# Table name: users
#
#  id            :integer          not null, primary key
#  email         :string
#  name          :string
#  admin         :boolean          default(FALSE)
#
class User < ActiveRecor::Base
  attr_accessible :email, :name
end
class UsersController < ApplicationController
  def create
    # { "user" => { "email" => "user@example.com", "name" => "Jon Snow", "admin" => true }
    User.create(params[:user])
  end
end
  # == Schema Information
#
# Table name: users
#
#  id            :integer          not null, primary key
#  email         :string
#  name          :string
#  admin         :boolean          default(FALSE)
#
class User < ActiveRecor::Base
  attr_accessible :email, :name
  attr_accessible :email, :name, :admin, as: :admin
end
class Admin::UsersController < ApplicationController
  def create
    # { "user" => { "email" => "user@example.com", "name" => "Jon Snow", "admin" => true }
    User.create(params[:user], as: :admin)
  end
end
  irb(main):001:0> user = User.create(email: "user@example.com", name: "Jon Snow", admin: true)
irb(main):002:0> user.admin
falseFormObject for conditional changes
2011 - Virtus
Rails 4
strong parameters
class PeopleController < ActionController::Base
  # This will raise an ActiveModel::ForbiddenAttributes exception
  # because it's using mass assignment without an explicit permit step.
  def create
    Person.create(params[:person])
  end
  # This will pass with flying colors as long as there's a person key
  # in the parameters, otherwise it'll raise an ActionController::ParameterMissing
  # exception, which will get caught by ActionController::Base and turned 
  # into that 400 Bad Request reply.
  def update
    person = current_account.people.find(params[:id])
    person.update_attributes!(person_params)
    redirect_to person
  end
  private
    # Using a private method to encapsulate the permissible parameters
    # is just a good pattern since you'll be able to reuse the same permit 
    # list between create and update. Also, you can specialize this method
    # with per-user checking of permissible attributes.
    def person_params
      params.require(:person).permit(:name, :age)
    end
end# nested attributes
params.permit(:name, {emails: []}, friends: [:name, { family: [:name], hobbies: []}])
# outside controller
raw_parameters = { :email => "john@example.com", :name => "John", :admin => true }
parameters = ActionController::Parameters.new(raw_parameters)
user = User.create(parameters.permit(:name, :email))Issues
- Data type validation
- Hard or impossible to define complex rules
- Coupled with ActiveRecord
- ...
dry-validation
class MyContract < Dry::Validation::Contract
  params do
    required(:id).filled(:integer)
    required(:name).filled(:string)
  end
end
MyContract.new.call("id" => 1, "name" => "Jon Snow")class MyContract < Dry::Validation::Contract
  params do
    required(:id).filled(:integer)
    required(:name).filled(:string)
  end
end
MyContract.new.call("id" => 1, "name" => "Jon Snow")module Types
  include Dry.Types
  Multiline = Types::Array.of(Types::String).constructor { |v|
    v.split(/\r?\n/)
  }
end
class MyContract < Dry::Validation::Contract
  params do
    required(:textarea).filled(Types::Multiline)
  end
end
# ideas: UUID, ID, CountryCode, PostCodeDry::Validation.Contract do
  register_macro(:qux) do
    if values[:bar]
      key.failure("do not repeat bar") if value == values[:bar]
    end
  end
  params do
    optional(:foo).hash do
      required(:bar).value(:integer)
      required(:baz).maybe(:string)
    end
  end
  rule("foo.bar") do
    key.failed("invalid") if value > 3
  end
  rule("foo.baz").validate(:qux)
endDry::Validation.Contract do
  params do
    required(:foo).optional(:string)
    required(:bar).optional(:string)
  end
  rule do
    if values[:foo].blank? && values[:bar].blank?
      base.failure("provide at least foo or bar")
    end
  end
endRails + dry-validation
gem "gate", "~> 1.0"
class MyController < ApplicationController
  include Gate::Rails
  before_action :verify_contract
  contract(handler: :method_to_handle_error) do
    params do
      required(:name).filled(:string)
    end
  end
  def create
    claimed_params[:name]
    # ...
  end
endQuestions?
dry-validation
By Jan Dudulski
dry-validation
- 138
 
   
   
  