Как ActiveRecord обнаруживает последний вызов метода в цепочке? - PullRequest
5 голосов
/ 01 марта 2012

Позвольте мне представить это для вас.

class Product < ActiveRecord::Base
end

Product.first.title
#=> "My sample product"

Ничего необычного здесь. Просто простой вызов метода. Теперь взглянем на следующий пример.

class Product < ActiveRecord::Base
  def method_missing
  end
end

Product.first.title
#=> nil

Product.first
Product.first.title
#=> "My sample product"

Как это возможно? Каким-то образом они определяют конец цепочки методов и действуют на это? По крайней мере, это моя теория.

Кто-нибудь может объяснить это поведение?

1 Ответ

7 голосов
/ 01 марта 2012

Вы видите артефакт использования irb для расследования вещей.

Когда вы говорите это:

> Product.first.title
#=> nil

Ваш method_missing будет вызван для ленивой загрузкиtitle метод, и вы получите nil.

Когда вы говорите это:

> Product.first

Вы эффективно делаете это:

> p = Product.first; puts p.inspect

Первый продуктэкземпляр будет загружен, а затем irb вызовет на нем inspect, и AR добавит методы доступа по пути.В результате у Product теперь будет метод title.Следовательно, делая это:

> Product.first
> Product.first.title

вообще не будет вызывать ваш method_missing, так как будет существовать настоящий title метод для вызова Product.first.title.

Если вы попытаетесьснова вот так:

> Product.first; nil
> Product.first.title

Вы увидите два nil s.


Что касается цепочки, ActiveRecord на самом деле не определяет конец, это просточто некоторые вызовы методов, естественно, требуют реальных данных из базы данных, а некоторые - нет.

Если вы вызываете where, order или любой другой метод запроса, вы получаете ActiveRecord:: Relation экземпляр обратно, и вы можете связать больше методов и областей запросов для этого объекта отношения.Например, where (который ActiveRecord :: Relation получает путем включения ActiveRecord :: QueryMethods ) выглядит следующим образом:

def where(opts, *rest)
  return self if opts.blank?

  relation = clone
  relation.where_values += build_where(opts, rest)
  relation
end

, поэтому он просто создаеткопия текущего запроса, добавляет к копии несколько вещей и возвращает ее обратно.

Если вы звоните first, last, to_a, all, любой из Перечислимые методы (то есть вы вызываете each), ... тогда вы спрашиваете о конкретных экземплярах, и ActiveRecord должен будет выполнить запрос, чтобы реализовать рассматриваемые экземпляры модели.Например, ActiveRecord::Relation#to_a выглядит так:

def to_a
  logging_query_plan do
    exec_queries
  end
end

и all немного больше, чем обертка вокруг to_a.

ActiveRecord на самом деле не знает, где находится конец цепочки, он просто ничего не загружает из базы данных до тех пор, пока он не должен, чтобы вы сказали ему, где заканчивается цепочка, сказав, иди и принесите мне некоторые данные..

...