mongoid/mongoid

View on GitHub
lib/mongoid/criteria/modifiable.rb

Summary

Maintainability
A
25 mins
Test Coverage
# encoding: utf-8
module Mongoid
  class Criteria
    module Modifiable

      # Build a document given the selector and return it.
      # Complex criteria, such as $in and $or operations will get ignored.
      #
      # @example build the document.
      #   Person.where(:title => "Sir").build
      #
      # @example Build with selectors getting ignored.
      #   Person.where(:age.gt => 5).build
      #
      # @return [ Document ] A non-persisted document.
      #
      # @since 2.0.0
      def build(attrs = {}, &block)
        create_document(:new, attrs, &block)
      end
      alias :new :build

      # Create a document in the database given the selector and return it.
      # Complex criteria, such as $in and $or operations will get ignored.
      #
      # @example Create the document.
      #   Person.where(:title => "Sir").create
      #
      # @example Create with selectors getting ignored.
      #   Person.where(:age.gt => 5).create
      #
      # @return [ Document ] A newly created document.
      #
      # @since 2.0.0.rc.1
      def create(attrs = {}, &block)
        create_document(:create, attrs, &block)
      end

      # Create a document in the database given the selector and return it.
      # Complex criteria, such as $in and $or operations will get ignored.
      # If validation fails, an error will be raised.
      #
      # @example Create the document.
      #   Person.where(:title => "Sir").create
      #
      # @example Create with selectors getting ignored.
      #   Person.where(:age.gt => 5).create
      #
      # @raise [ Errors::Validations ] on a validation error.
      #
      # @return [ Document ] A newly created document.
      #
      # @since 3.0.0
      def create!(attrs = {}, &block)
        create_document(:create!, attrs, &block)
      end

      # Find the first +Document+ given the conditions, or creates a new document
      # with the conditions that were supplied.
      #
      # @example Find or create the document.
      #   Person.find_or_create_by(:attribute => "value")
      #
      # @param [ Hash ] attrs The attributes to check.
      #
      # @return [ Document ] A matching or newly created document.
      def find_or_create_by(attrs = {}, &block)
        find_or(:create, attrs, &block)
      end

      # Find the first +Document+ given the conditions, or creates a new document
      # with the conditions that were supplied. If validation fails an
      # exception will be raised.
      #
      # @example Find or create the document.
      #   Person.find_or_create_by!(:attribute => "value")
      #
      # @param [ Hash ] attrs The attributes to check.
      #
      # @raise [ Errors::Validations ] on validation error.
      #
      # @return [ Document ] A matching or newly created document.
      def find_or_create_by!(attrs = {}, &block)
        find_or(:create!, attrs, &block)
      end

      # Find the first +Document+ given the conditions, or initializes a new document
      # with the conditions that were supplied.
      #
      # @example Find or initialize the document.
      #   Person.find_or_initialize_by(:attribute => "value")
      #
      # @param [ Hash ] attrs The attributes to check.
      #
      # @return [ Document ] A matching or newly initialized document.
      def find_or_initialize_by(attrs = {}, &block)
        find_or(:new, attrs, &block)
      end

      # Find the first +Document+, or creates a new document
      # with the conditions that were supplied plus attributes.
      #
      # @example First or create the document.
      #   Person.where(name: "Jon").first_or_create(attribute: "value")
      #
      # @param [ Hash ] attrs The additional attributes to add.
      #
      # @return [ Document ] A matching or newly created document.
      #
      # @since 3.1.0
      def first_or_create(attrs = nil, &block)
        first_or(:create, attrs, &block)
      end

      # Find the first +Document+, or creates a new document
      # with the conditions that were supplied plus attributes and will
      # raise an error if validation fails.
      #
      # @example First or create the document.
      #   Person.where(name: "Jon").first_or_create!(attribute: "value")
      #
      # @param [ Hash ] attrs The additional attributes to add.
      #
      # @return [ Document ] A matching or newly created document.
      #
      # @since 3.1.0
      def first_or_create!(attrs = nil, &block)
        first_or(:create!, attrs, &block)
      end

      # Find the first +Document+, or initializes a new document
      # with the conditions that were supplied plus attributes.
      #
      # @example First or initialize the document.
      #   Person.where(name: "Jon").first_or_initialize(attribute: "value")
      #
      # @param [ Hash ] attrs The additional attributes to add.
      #
      # @return [ Document ] A matching or newly initialized document.
      #
      # @since 3.1.0
      def first_or_initialize(attrs = nil, &block)
        first_or(:new, attrs, &block)
      end

      private

      # Create a document given the provided method and attributes from the
      # existing selector.
      #
      # @api private
      #
      # @example Create a new document.
      #   criteria.create_document(:new, {})
      #
      # @param [ Symbol ] method Either :new or :create.
      # @param [ Hash ] attrs Additional attributes to use.
      #
      # @return [ Document ] The new or saved document.
      #
      # @since 3.0.0
      def create_document(method, attrs = nil, &block)
        attributes = selector.reduce(attrs || {}) do |hash, (key, value)|
          unless key.to_s =~ /\$/ || value.is_a?(Hash)
            hash[key] = value
          end
          hash
        end
        if embedded?
          attributes[:_parent] = parent_document
          attributes[:__metadata] = metadata
        end
        klass.__send__(method, attributes, &block)
      end

      # Find the first object or create/initialize it.
      #
      # @api private
      #
      # @example Find or perform an action.
      #   Person.find_or(:create, :name => "Dev")
      #
      # @param [ Symbol ] method The method to invoke.
      # @param [ Hash ] attrs The attributes to query or set.
      #
      # @return [ Document ] The first or new document.
      def find_or(method, attrs = {}, &block)
        where(attrs).first || send(method, attrs, &block)
      end

      # Find the first document or create/initialize it.
      #
      # @api private
      #
      # @example First or perform an action.
      #   Person.first_or(:create, :name => "Dev")
      #
      # @param [ Symbol ] method The method to invoke.
      # @param [ Hash ] attrs The attributes to query or set.
      #
      # @return [ Document ] The first or new document.
      #
      # @since 3.1.0
      def first_or(method, attrs = {}, &block)
        first || create_document(method, attrs, &block)
      end
    end
  end
end