Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 174 lines (159 sloc) 5.051 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
# encoding: utf-8
module Mongoid #:nodoc:
  module Finders #:nodoc:

    # Delegate to the criteria methods that are natural for creating a new
    # criteria.
    [ :all_in, :any_in, :any_of, :asc, :ascending, :avg, :desc, :descending,
      :excludes, :includes, :limit, :max, :min, :not_in, :only, :order_by,
      :skip, :sum, :where, :update, :update_all, :near ].each do |name|
      define_method(name) do |*args|
        criteria.send(name, *args)
      end
    end

    # Find +Documents+ given the conditions.
    #
    # Options:
    #
    # args: A +Hash+ with a conditions key and other options
    #
    # <tt>Person.all(:conditions => { :attribute => "value" })</tt>
    def all(*args)
      find(:all, *args)
    end

    # Returns a count of matching records in the database based on the
    # provided arguments.
    #
    # <tt>Person.count(:conditions => { :attribute => "value" })</tt>
    def count(*args)
      Criteria.translate(self, false, *args).count
    end

    # Returns true if there are on document in database based on the
    # provided arguments.
    #
    # <tt>Person.exists?(:conditions => { :attribute => "value" })</tt>
    def exists?(*args)
      Criteria.translate(self, false, *args).limit(1).count == 1
    end

    # Helper to initialize a new +Criteria+ object for this class, or return
    # the currently scoped +Criteria+ object.
    #
    # Example:
    #
    # <tt>Person.criteria</tt>
    def criteria(embedded = false)
      scope_stack.last || Criteria.new(self, embedded)
    end

    # Find a +Document+ in several different ways.
    #
    # If a +String+ is provided, it will be assumed that it is a
    # representation of a Mongo::ObjectID and will attempt to find a single
    # +Document+ based on that id. If a +Symbol+ and +Hash+ is provided then
    # it will attempt to find either a single +Document+ or multiples based
    # on the conditions provided and the first parameter.
    #
    # Example:
    #
    # <tt>Person.find(:first, :conditions => { :attribute => "value" })</tt>
    # <tt>Person.find(:all, :conditions => { :attribute => "value" })</tt>
    # <tt>Person.find(BSON::ObjectId)</tt>
    #
    # Options:
    #
    # args: An assortment of finder options.
    #
    # Returns:
    #
    # A document or criteria.
    def find(*args)
      raise Errors::InvalidOptions.new(
        :calling_document_find_with_nil_is_invalid, {}
      ) if args[0].nil?
      type, criteria = Criteria.parse!(self, false, *args)
      case type
      when :first then return criteria.one
      when :last then return criteria.last
      else
        return criteria
      end
    end

    # Find the first +Document+ given the conditions, or creates a new document
    # with the conditions that were supplied
    #
    # Options:
    #
    # args: A +Hash+ of attributes
    #
    # <tt>Person.find_or_create_by(:attribute => "value")</tt>
    def find_or_create_by(attrs = {})
      find_or(:create, attrs)
    end

    # Find the first +Document+ given the conditions, or instantiates a new document
    # with the conditions that were supplied
    #
    # Options:
    #
    # args: A +Hash+ of attributes
    #
    # <tt>Person.find_or_initialize_by(:attribute => "value")</tt>
    def find_or_initialize_by(attrs = {})
      find_or(:new, attrs)
    end

    # Find the first +Document+ given the conditions.
    #
    # Options:
    #
    # args: A +Hash+ with a conditions key and other options
    #
    # <tt>Person.first(:conditions => { :attribute => "value" })</tt>
    def first(*args)
      find(:first, *args)
    end

    # Find the last +Document+ given the conditions.
    #
    # Options:
    #
    # args: A +Hash+ with a conditions key and other options
    #
    # <tt>Person.last(:conditions => { :attribute => "value" })</tt>
    def last(*args)
      find(:last, *args)
    end

    # Find all documents in paginated fashion given the supplied arguments.
    # If no parameters are passed just default to offset 0 and limit 20.
    #
    # Options:
    #
    # params: A +Hash+ of params to pass to the Criteria API.
    #
    # Example:
    #
    # <tt>Person.paginate(:conditions => { :field => "Test" }, :page => 1,
    # :per_page => 20)</tt>
    #
    # Returns paginated array of docs.
    def paginate(params = {})
      Criteria.translate(self, false, params).paginate
    end

    protected
    # Find the first object or create/initialize it.
    def find_or(method, attrs = {})
      first(:conditions => attrs) || send(method, attrs)
    end

    # Initializes and returns the current scope stack.
    def scope_stack
      scope_stack_for = Thread.current[:mongoid_scope_stack] ||= {}
      scope_stack_for[object_id] ||= []
    end

    # Pushes the provided criteria onto the scope stack, and removes it after the
    # provided block is yielded.
    def with_scope(criteria)
      scope_stack = self.scope_stack
      scope_stack << criteria
      begin
        yield criteria
      ensure
        scope_stack.pop
      end
    end
  end
end
Something went wrong with that request. Please try again.