Class: ActiveModel::Validations::DateValidator

Inherits:
EachValidator
  • Object
show all
Defined in:
lib/active_model/validations/date_validator.rb

Overview

Date Validator. Inherits from ActiveModel::EachValidator.

Responds to the regular validator API methods ‘#check_validity` and `#validate_each`.

Constant Summary collapse

CHECKS =

Implemented checks and their associated operators.

{ after: :>,  after_or_equal_to: :>=,
          before: :<, before_or_equal_to: :<=,
          equal_to: :==
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ DateValidator

Call ‘#initialize` on the superclass, adding a default `allow_nil: false` option.



25
26
27
# File 'lib/active_model/validations/date_validator.rb', line 25

def initialize(options)
  super(options.reverse_merge(allow_nil: false))
end

Instance Method Details

#check_validity!Object

Validates the arguments passed to the validator.

They must be either any kind of Time, a Proc, or a Symbol.



32
33
34
35
36
37
38
# File 'lib/active_model/validations/date_validator.rb', line 32

def check_validity!
  keys = CHECKS.keys
  options.slice(*keys).each do |option, value|
    next if is_time?(value) || value.is_a?(Proc) || value.is_a?(Symbol) || (defined?(ActiveSupport::TimeWithZone) and value.is_a? ActiveSupport::TimeWithZone)
    raise ArgumentError, ":#{option} must be a time, a date, a time_with_zone, a symbol or a proc"
  end
end

#validate(record) ⇒ Object

Overridden because standard allow_nil and allow_blank checks don’t work with string expressions that cannot be type cast to dates. We have to validate the pre-type cast values.



43
44
45
46
47
48
# File 'lib/active_model/validations/date_validator.rb', line 43

def validate(record)
  attributes.each do |attribute|
    value = record.read_attribute_for_validation(attribute)
    validate_each(record, attribute, value)
  end
end

#validate_each(record, attr_name, value) ⇒ Object

The actual validator method. It is called when ActiveRecord iterates over all the validators.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/active_model/validations/date_validator.rb', line 52

def validate_each(record, attr_name, value)
  before_type_cast = :"#{attr_name}_before_type_cast"

  value_before_type_cast = if record.respond_to?(before_type_cast)
    record.send(before_type_cast)
  else
    nil
  end

  if value_before_type_cast.present? && value.nil?
    record.errors.add(attr_name, :not_a_date, **options)
    return
  end

  return if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])

  unless value
    record.errors.add(attr_name, :not_a_date, **options)
    return
  end

  unless is_time?(value)
    record.errors.add(attr_name, :not_a_date, **options)
    return
  end

  options.slice(*CHECKS.keys).each do |option, option_value|
    option_value = option_value.call(record) if option_value.is_a?(Proc)
    option_value = record.send(option_value) if option_value.is_a?(Symbol)

    original_value = value
    original_option_value = option_value

    # To enable to_i conversion, these types must be converted to Datetimes
    if defined?(ActiveSupport::TimeWithZone)
      option_value = option_value.to_datetime if option_value.is_a?(ActiveSupport::TimeWithZone)
      value = value.to_datetime if value.is_a?(ActiveSupport::TimeWithZone)
    end

    if defined?(Date)
      option_value = option_value.to_datetime if option_value.is_a?(Date)
      value = value.to_datetime if value.is_a?(Date)
    end

    unless is_time?(option_value) && value.to_i.send(CHECKS[option], option_value.to_i)
      record.errors.add(attr_name, :"date_#{option}", **options.merge(
          value: original_value,
          date:  (I18n.localize(original_option_value) rescue original_option_value)
      ))
    end
  end
end