Skip to content

Instantly share code, notes, and snippets.

@krisleech
Last active February 10, 2021 08:13
Show Gist options
  • Save krisleech/078ffea1d5233cf3ef2d2698cf14d93c to your computer and use it in GitHub Desktop.
Save krisleech/078ffea1d5233cf3ef2d2698cf14d93c to your computer and use it in GitHub Desktop.
ActiveModel comforming Form object based on dry-type / dry-validation
require 'dry-validation'
require 'dry-struct'
require 'active_model/errors'

require_relative '../create_study'

module MyApp
  module Types
    include Dry::Types.module
  end

  class CreateStudy
    class Form < Dry::Struct
      transform_keys(&:to_sym)

      attribute :title, Types::String.meta(omittable: true)
      attribute :oid, Types::String.meta(omittable: true)
      
      # ActiveModel
      def persisted?
        false
      end
      
      # ActiveModel
      def model_name
        Struct.new(:param_key, :name).new('form', 'form')
      end
      
      # ActiveModel
      def to_key
        nil
      end
      
      def errors
        @errors ||= ActiveModel::Errors.new(self)
      end

      def valid?
        validate
        @validation.success?
      end
      
      private
      
      def schema
        Dry::Validation.Schema do
          required(:name).filled
        end
      end     
      
      def validate
        unless defined?(@validation)
          @validation = schema.call(attributes)
          @validation.messages.each do |field, field_errors|
            field_errors.each do |field_error|
              errors.add(field, field_error)
            end
          end
        end

        self
      end
    end
  end
end

form = MyApp::CreateStudy::Form.new
form.valid? # => false

form = MyApp::CreateStudy::Form.new(title: 'Yellow')
form.valid? # => true

We could remove any dependency on ActiveModel by implementing our own errors object which ducktypes as ActiveModel::Errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment