Class: ActiveRecord::Validations::DatabaseConstraintsValidator

Inherits:
ActiveModel::EachValidator
  • Object
show all
Defined in:
lib/active_record/validations/database_constraints.rb

Constant Summary collapse

VALID_CONSTRAINTS =
Set[:size, :basic_multilingual_plane, :not_null, :range]
SIZE_VALIDATORS_FOR_TYPE =
{
  characters: ActiveModel::Validations::LengthValidator,
  bytes: ActiveModel::Validations::BytesizeValidator,
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ DatabaseConstraintsValidator

Returns a new instance of DatabaseConstraintsValidator.



17
18
19
20
21
# File 'lib/active_record/validations/database_constraints.rb', line 17

def initialize(options = {})
  @constraints = Set.new(Array.wrap(options[:in]) + Array.wrap(options[:with]))
  @constraint_validators = {}
  super
end

Instance Attribute Details

#constraintsObject (readonly)

Returns the value of attribute constraints.



8
9
10
# File 'lib/active_record/validations/database_constraints.rb', line 8

def constraints
  @constraints
end

Instance Method Details

#attribute_validators(klass, attribute) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/active_record/validations/database_constraints.rb', line 64

def attribute_validators(klass, attribute)
  @constraint_validators[attribute] ||= begin
    column_definition = klass.columns_hash[attribute.to_s]

    unless column_definition
      raise ArgumentError.new("Model #{klass.name} does not have column #{attribute.to_s}!")
    end

    column = ActiveRecord::Validations::TypedColumn.new(column_definition)

    [
      not_null_validator(klass, column),
      size_validator(klass, column),
      basic_multilingual_plane_validator(klass, column),
      range_validator(klass, column),
    ].compact
  end
end

#basic_multilingual_plane_validator(klass, column) ⇒ Object



58
59
60
61
62
# File 'lib/active_record/validations/database_constraints.rb', line 58

def basic_multilingual_plane_validator(klass, column)
  return unless constraints.include?(:basic_multilingual_plane)
  return unless column.text? && column.collation =~ /\Autf8(?:mb3)?_/
  ActiveModel::Validations::BasicMultilingualPlaneValidator.new(attributes: [column.name.to_sym], class: klass)
end

#check_validity!Object

Raises:

  • (ArgumentError)


23
24
25
26
27
28
# File 'lib/active_record/validations/database_constraints.rb', line 23

def check_validity!
  invalid_constraints = constraints - VALID_CONSTRAINTS

  raise ArgumentError, "You have to specify what constraints to validate for." if constraints.empty?
  raise ArgumentError, "#{invalid_constraints.map(&:inspect).join(',')} is not a valid constraint." unless invalid_constraints.empty?
end

#not_null_validator(klass, column) ⇒ Object



30
31
32
33
34
35
# File 'lib/active_record/validations/database_constraints.rb', line 30

def not_null_validator(klass, column)
  return unless constraints.include?(:not_null)
  return if column.null

  ActiveModel::Validations::NotNullValidator.new(attributes: [column.name.to_sym], class: klass)
end

#range_validator(klass, column) ⇒ Object



49
50
51
52
53
54
55
56
# File 'lib/active_record/validations/database_constraints.rb', line 49

def range_validator(klass, column)
  return unless constraints.include?(:range)
  return unless column.number?

  args = { attributes: [column.name.to_sym], class: klass, allow_nil: true }
  args.merge!(ActiveRecord::DatabaseValidations::MySQL.column_range(column))
  ActiveModel::Validations::NumericalityValidator.new(args)
end

#size_validator(klass, column) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/active_record/validations/database_constraints.rb', line 37

def size_validator(klass, column)
  return unless constraints.include?(:size)
  return unless column.text? || column.binary?

  maximum, type, encoding = ActiveRecord::DatabaseValidations::MySQL.column_size_limit(column)
  validator_class = SIZE_VALIDATORS_FOR_TYPE[type]

  if validator_class && maximum
    validator_class.new(attributes: [column.name.to_sym], class: klass, maximum: maximum, encoding: encoding)
  end
end

#validate_each(record, attribute, _value) ⇒ Object



83
84
85
86
87
# File 'lib/active_record/validations/database_constraints.rb', line 83

def validate_each(record, attribute, _value)
  attribute_validators(record.class, attribute).each do |validator|
    validator.validate(record)
  end
end