Search code examples
ruby-on-railsvalidationvirtus

How to implement `dry-validation` gem in a Rails form object?


I'm trying to substitute ActiveRecord validations with Dry-validations, but I've been unable to find any in-app implementation examples to follow.

Dry-validation docs: http://dry-rb.org/gems/dry-validation/

I've added below to the form object, but I don't understand how to actually implement it so the validation fails if title is not inputted in the UI's form.

schema = Dry::Validation.Schema do
  required(:title).filled
end

Form Object (setup with Virtus):

class PositionForm
  include Virtus.model
  include ActiveModel::Model
  require 'dry-validation'
  require 'dry/validation/schema/form'

  # ATTRIBUTES
  attribute :id, Integer
  attribute :title, String
  ...

  # OLD ACTIVE RECORD VALIDATIONS
  #validates :title, presence: true

  # NEW DRY VALIDATIONS
  schema = Dry::Validation.Schema do
    required(:title).filled
  end

  def save
    if valid?
      persist!
      true
    else
      false
    end
  end

  def persist!
    @position = Position.create!(title: title...)
  end
end

I've never used dry-validation before - any guidance would be super appreciated!


UPDATE

I've been able to "make it work" but it still doesn't feel like the correct design pattern.

Updated save method

def save
  schema = Dry::Validation.Schema do
    required(:title).filled
  end

  errors = schema.(title: title)

  if valid? && errors.messages.empty?
    persist!
    true
  else
    false
  end
end

If anyone could share guidance on the appropriate design pattern to implement dry-validation into a virtus-style form object it would be super appreciated!


Solution

  • I would try to keep validation at the model level.

    Have a ModelValidations model in your initializers, each method named after the model it validates.

    config/initialize/model_validations.rb

    module ModelValidations
      def position_form
        Dry::Validation.Schema do
          required(:title).filled
        end
      end
    end
    

    In the model, call the dry_validation module for that model.

    app/models/position_form.rb

    class PositionForm
      validates :dry_validation
    
      def dry_validation
        ModelValidations.position_form(attributes).each do |field, message|
          errors.add(field, message)
        end
      end
    
    end