Почему ассоциация модели Rails не является естественной? ActiveRecord:: Relations?

Я использую Rails 3.2.0

Скажем, у меня есть:

class Comment < ActiveRecord::Base
  has_many :articles
end

c1 = Comment.last

затем

c1.articles.class
# => Array

c1.articles.where('id NOT IN (999999)').class
# => ActiveRecord::Relation    

Почему результат ассоциации не типа ActiveRecord::Relation?

Очевидно, что/был в какой-то момент:

c1.articles.to_orig
# undefined method `to_orig' for #<ActiveRecord::Relation:0x007fd820cc80a8>

c1.articles.class
# => Array

Некоторые оценки действуют на объект ActiveRecord:: Relation, но проверка класса дает другой тип.


В частности, это прерывает создание запросов с ленивым загрузчиком при использовании merge для выполнения нескольких запросов.

Ответ 1

Это ActiveRecord::Relation, но Rails намеренно лжет вам. Вы можете видеть это уже в вызовах метода и продолжать видеть его, вызывая ancestors, который включает в себя множество классов ActiveRecord:

c1.articles.ancestors.select { |c| c.to_s =~ /ActiveRecord/ }.size  #=> 35

который показывает, что он не очень сильно отличается от Array.

Это происходит потому, что вы возвращаетесь при вызове c1.articles ActiveRecord::Associations::CollectionProxy *, который undefines class (наряду со многими другими методами). Это означает, что class передается через его method_missing, который отправляет до target. Как мы видим, класс target здесь, фактически, Array:

c1.articles.target.class  #=> Array

Вот откуда происходит c1.articles.class. Тем не менее, это ActiveRecord::Relation.

<суб > * Мы можем проверить, что это действительно ActiveRecord::Associations::CollectionProxy, вызывая метод Rubys original class для рассматриваемого объекта: Object.instance_method(:class).bind(c1.articles).call. Это хороший трюк, чтобы убедиться, что объект не пытается притворяться другим классом. Суб >

Ответ 2

Потому что, когда вы определяете ассоциацию, она помещается в вашу модель:

def #{name}(*args)
  association(:#{name}).reader(*args)
end

.reader() возвращает AssociationProxy, который удаляет метод .class и делегирует неизвестные методы @target через .method_missing.. p >