Search code examples
ruby-on-railsrubyactiverecordactivesupport

How ActiveRecord calls 'where'?


Could you let me know how Active Record calls 'where' when I execute User.where(id: 1)?

I'm checking Active Record's functionality to read the source code and stop the process by pry. I can see when I execute User.where(id: 1), the process goes to rails/activerecord/lib/active_record/querying.rb

delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or,
         :where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly,
         :having, :create_with, :distinct, :references, :none, :unscope, :merge, to: :all

'where' is delegated to :all. Thus it goes to rails/activerecord/lib/active_record/scoping/named.rb

def all
  if current_scope
   current_scope.clone
  else
    default_scoped
  end
end

I don't understand this part. Why it goes to all? delegate can be set to class method? I though it can be delegated to class variables, instance variables.

after default_scope in scoping/named.rb all method, the process goes to rails/activerecord/lib/active_record/relation/query_methods.rb

def where(opts = :chain, *rest)
  if :chain == opts
    WhereChain.new(spawn)
  elsif opts.blank?
    self
  else
    spawn.where!(opts, *rest)
  end
end

Why? Isn't where delegated to :all? How does it get back to where(relation/query_methods.rb)?


Solution

  • I don't understand this part. Why it goes to all?

    Because:

    User.where(id: 1)
    

    is equivalent to:

    User.all.where(id: 1)
    

    This delegation is to simplify the API, by adding an implicit call to all when none is explicitly made.

    How does it get back to where (relation/query_methods.rb)?

    Remember that this was a delegation, not an alias. So, since:

    User.all
    

    returns:

    User.default_scoped
    

    This means that, effectively, this is the method chain being constructed:

    User.where
    --> User.all.where
    --> User.default_scoped.where
    

    And since User.default_scoped is a User::ActiveRecord_Relation object, its definition of #where is indeed defined in rails/activerecord/lib/active_record/relation/query_methods.rb, as:

    def where(opts = :chain, *rest)
      if :chain == opts
        WhereChain.new(spawn)
      elsif opts.blank?
        self
      else
        spawn.where!(opts, *rest)
      end
    end
    

    ...But this is only the beginning of the story. We are still quite a way off constructing the actual SQL for the query.

    Here is a series of blog posts that dive deeper, if you're interested - but I think this goes beyond the scope of a single StackOverflow answer.