ActiveModel validations without a model

The other day someone on Stack Overflow asked how to validate inputs without having a model.

I kind of liked my answer, so I am more or less reposting it here — with some extra thoughts.

The Rails Way (tm)

The Rails Way is having validations tied to ActiveRecord classes, and where that doesn’t make sense, construct other classes and include ActiveModel::Validations.

Occasionally this might be too heavy-handed, I guess, or you might have dynamic validations, or you might disagree with having validations in the model layer on sheer principle.

The Laravel Way?

The original question used an example from Laravel, where validations do not live inside models. Rather, you construct a validator that can tell you if a set of inputs is valid given a set of validations.

An action following that pattern might look like this in a Rails application:

validator = DataValidator.make(
  params,
  {
    :email => {:presence => true},
    :name => {:presence => true}
  }
)
if validator.valid?
  # Success
else
  # Error
    @errors = validator.errors
end

Simple enough, huh?

The code for this is fairly simple as well and basically defines an anonymous class on the fly, returning an instance of that class:

class DataValidator
  def self.make(data, validations)
    Class.new do
      include ActiveModel::Validations

      attr_accessor(*validations.keys)

      validations.each do |attribute, options|
        validates attribute, options
      end

      def self.model_name
        ActiveModel::Name.new(self, nil, "DataValidator::Validator")
      end

      def initialize(data)
        data.each do |key, value|
          self.send("#{key.to_sym}=“, value)
        end
      end
    end.new(data)
  end
end

What's up with validating data in models, anyways?

Writing the above code also got me thinking; why exactly do we not always validate inputs like this? Why do we insist on validating user input in our models?