123456789_123456789_123456789_123456789_123456789_

Class: Mongoid::Validatable::AssociatedValidator

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, ActiveModel::Validator
Instance Chain:
self, ActiveModel::Validator
Inherits: ActiveModel::Validator
  • Object
Defined in: lib/mongoid/validatable/associated.rb

Overview

Validates whether or not an association is valid or not. Will correctly handle has one and has many associations.

Examples:

::Set up the association validations.

class Person
  include Mongoid::Document
  embeds_one :name
  embeds_many :addresses

  validates_associated :name, :addresses
end

Instance Method Summary

Instance Method Details

#attributes

Required by validates_with so that the validator gets added to the correct attributes.

[ GitHub ]

  
# File 'lib/mongoid/validatable/associated.rb', line 22

def attributes
  options[:attributes]
end

#get_target_documents(target) ⇒ Array<Mongoid::Document> (private)

Examine the given target object and return an array of documents (possibly empty) that the target represents.

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/validatable/associated.rb', line 97

def get_target_documents(target)
  if target.respond_to?(:_loaded?)
    get_target_documents_for_has_many(target)
  else
    get_target_documents_for_other(target)
  end
end

#get_target_documents_for_has_many(target) ⇒ Array<Mongoid::Document> (private)

Returns the list of all currently in-memory values held by the target. The target will not be loaded.

Parameters:

  • target (HasMany::Enumerable)

    the target that will be examined for in-memory documents.

Returns:

[ GitHub ]

  
# File 'lib/mongoid/validatable/associated.rb', line 113

def get_target_documents_for_has_many(target)
  [ *target._loaded.values, *target._added.values ]
end

#get_target_documents_for_other(target) ⇒ Array<Mongoid::Document> (private)

Returns the target as an array. If the target represents a single value, it is wrapped in an array.

Parameters:

Returns:

[ GitHub ]

  
# File 'lib/mongoid/validatable/associated.rb', line 124

def get_target_documents_for_other(target)
  Array.wrap(target)
end

#validate(document)

Checks that the named associations of the given record (‘attributes`) are valid. This does NOT load the associations from the database, and will only validate records that are dirty or unpersisted.

If anything is not valid, appropriate errors will be added to the document parameter.

Parameters:

[ GitHub ]

  
# File 'lib/mongoid/validatable/associated.rb', line 36

def validate(document)
  options[:attributes].each do |attr_name|
    validate_association(document, attr_name)
  end
end

#validate_association(document, attribute) (private)

Validates that the given association provided is either nil, persisted and unchanged, or invalid. Otherwise, the appropriate errors will be added to the parent document.

Parameters:

  • document (Document)

    The document to validate.

  • attribute (Symbol)

    The association to validate.

[ GitHub ]

  
# File 'lib/mongoid/validatable/associated.rb', line 50

def validate_association(document, attribute)
  # grab the proxy from the instance variable directly; we don't want
  # any loading logic to run; we just want to see if it's already
  # been loaded.
  proxy = document.ivar(attribute)
  return unless proxy

  # if the variable exists, now we see if it is a proxy, or an actual
  # document. It might be a literal document instead of a proxy if this
  # document was created with a Document instance as a provided attribute,
  # e.g. "Post.new(message: Message.new)".
  target = proxy.respond_to?(:_target) ? proxy._target : proxy

  # Now, fetch the list of documents from the target. Target may be a
  # single value, or a list of values, and in the case of HasMany,
  # might be a rather complex collection. We need to do this without
  # triggering a load, so it's a bit of a delicate dance.
  list = get_target_documents(target)

  valid = document.validating do
    # Now, treating the target as an array, look at each element
    # and see if it is valid, but only if it has already been
    # persisted, or changed, and hasn't been flagged for destroy.
    #
    # use map.all? instead of just all?, because all? will do short-circuit
    # evaluation and terminate on the first failed validation.
    list.map do |value|
      if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?)
        value.validated? ? true : value.valid?
      else
        true
      end
    end.all?
  end

  document.errors.add(attribute, :invalid) unless valid
end