mongoid/mongoid

View on GitHub
lib/mongoid/validatable.rb

Summary

Maintainability
A
0 mins
Test Coverage
# encoding: utf-8
require "mongoid/validatable/macros"
require "mongoid/validatable/localizable"
require "mongoid/validatable/associated"
require "mongoid/validatable/format"
require "mongoid/validatable/length"
require "mongoid/validatable/queryable"
require "mongoid/validatable/presence"
require "mongoid/validatable/uniqueness"

module Mongoid

  # This module provides additional validations that ActiveModel does not
  # provide: validates_associated and validates_uniqueness_of.
  module Validatable
    extend ActiveSupport::Concern

    included do
      extend Macros
      include Macros
    end

    # Begin the associated validation.
    #
    # @example Begin validation.
    #   document.begin_validate
    #
    # @since 2.1.9
    def begin_validate
      Threaded.begin_validate(self)
    end

    # Exit the associated validation.
    #
    # @example Exit validation.
    #   document.exit_validate
    #
    # @since 2.1.9
    def exit_validate
      Threaded.exit_validate(self)
    end

    # Given the provided options, are we performing validations?
    #
    # @example Are we performing validations?
    #   document.performing_validations?(validate: true)
    #
    # @param [ Hash ] options The options to check.
    #
    # @return [ true, false ] If we are validating.
    #
    # @since 4.0.0
    def performing_validations?(options = {})
      options[:validate].nil? ? true : options[:validate]
    end

    # Overrides the default ActiveModel behaviour since we need to handle
    # validations of relations slightly different than just calling the
    # getter.
    #
    # @example Read the value.
    #   person.read_attribute_for_validation(:addresses)
    #
    # @param [ Symbol ] attr The name of the field or relation.
    #
    # @return [ Object ] The value of the field or the relation.
    #
    # @since 2.0.0.rc.1
    def read_attribute_for_validation(attr)
      attribute = database_field_name(attr)
      if relations.key?(attribute)
        begin_validate
        relation = without_autobuild { send(attr) }
        exit_validate
        relation.do_or_do_not(:in_memory) || relation
      elsif fields[attribute].try(:localized?)
        attributes[attribute]
      else
        send(attr)
      end
    end

    # Determine if the document is valid.
    #
    # @example Is the document valid?
    #   person.valid?
    #
    # @example Is the document valid in a context?
    #   person.valid?(:create)
    #
    # @param [ Symbol ] context The optional validation context.
    #
    # @return [ true, false ] True if valid, false if not.
    #
    # @since 2.0.0.rc.6
    def valid?(context = nil)
      super context ? context : (new_record? ? :create : :update)
    end

    # Used to prevent infinite loops in associated validations.
    #
    # @example Is the document validated?
    #   document.validated?
    #
    # @return [ true, false ] Has the document already been validated?
    #
    # @since 2.0.0.rc.2
    def validated?
      Threaded.validated?(self)
    end

    # Are we currently performing a validation that has a query?
    #
    # @example Are we validating with a query?
    #   document.validating_with_query?
    #
    # @return [ true, false ] If we are validating with a query.
    #
    # @since 3.0.2
    def validating_with_query?
      self.class.validating_with_query?
    end

    module ClassMethods

      # Adds an associated validator for the relation if the validate option
      # was not provided or set to true.
      #
      # @example Set up validation.
      #   Person.validates_relation(metadata)
      #
      # @param [ Metadata ] metadata The relation metadata.
      #
      # @since 2.0.0.rc.1
      def validates_relation(metadata)
        if metadata.validate?
          validates_associated(metadata.name)
        end
      end

      # Add validation with the supplied validators forthe provided fields
      # with options.
      #
      # @example Validate with a specific validator.
      #   validates_with MyValidator, on: :create
      #
      # @param [ Class<Array>, Hash ] *args The validator classes and options.
      #
      # @note See ActiveModel::Validations::With for full options. This is
      #   overridden to add autosave functionality when presence validation is
      #   added.
      #
      # @since 3.0.0
      def validates_with(*args, &block)
        if args.first == PresenceValidator
          args.last[:attributes].each do |name|
            metadata = relations[name.to_s]
            if metadata && metadata[:autosave] != false
              autosave(metadata.merge!(autosave: true))
            end
          end
        end
        super
      end

      # Are we currently performing a validation that has a query?
      #
      # @example Are we validating with a query?
      #   Model.validating_with_query?
      #
      # @return [ true, false ] If we are validating with a query.
      #
      # @since 3.0.2
      def validating_with_query?
        Threaded.executing?("#{name}-validate-with-query")
      end
    end
  end
end