ActiveModel Interdependence

Dependency Status Circle CI Code Quality Code Coverage Inline Documentation

Specify that validations depend on the validation of other fields

Lets classes that implement ActiveModel::Model or ActiveModel::Validator specify that they are dependent on other fields being valid. These specifications are translated into a dependency graph, sorted, and applied in order to a model. As a result, fields are only validated once the fields that they depend on are validated.

Install

Simply run

gem install activemodel-interdependence

or add activemodel-interdependence to your gemfile

gem 'activemodel-interdependence'

and run bundle install from your shell.

Usage

class DayValidator < ActiveModel::EachValidator
  validates :month_field, inclusion: 1..12
  validates :year_field, inclusion: 0..2015

  def validate_each(record, attribute, value)
    month = dependency(record, :month_field)
    year = dependency(record, :year_field)

    return if (1..Time.days_in_month(month, year)).cover?(value)
    record.errors[attribute] << "is not valid for the month #{Date::MONTHNAMES[month]}"
  end

  def dependency(record, proxy_name)
    name = options.fetch(:dependencies, {}).fetch(proxy_name)

    record.send(name)
  end
end

class Birthday
  include ActiveModel::Model
  attr_accessor :day, :month, :year

  validates :day, day: {
    dependencies: {
      month_field: :month,
      year_field: :year
    }
  }
end

leap_day = Birthday.new(day: 29, month: 2, year: 2000)
leap_day.valid? # => true

not_a_leap_day = Birthday.new(day: 29, month: 2, year: 1999)
not_a_leap_day.valid? # => false
not_a_leap_day.errors.full_messages # => ["Day is not valid for the month February"]

bad_month = Birthday.new(day: 29, month: 0, year: 1999)
bad_month.valid? # => false
bad_month.errors.full_messages # => ["Month is not included in the list"]

License

activemodel-interdependence is released under the MIT license. See LICENSE for details.