Почему / Как метод класса доступен массиву записей только через ассоциацию в Rails 3?
Рассмотрим простой пример, где у нас есть 2 модели, статья и категория.
class Article < ActiveRecord::Base
belongs_to :category
def self.search(title)
where(:title => title)
end
end
class Category < ActiveRecord::Base
has_many :articles
end
Теперь о рельсах консоли:
Article.all.search('test article')
Как и ожидалось, возвращает ошибку
NoMethodError: undefined method `search' for #<Array:0x9aa207c>
Но когда я делаю это
Category.first.articles.search('test article')
возвращает набор записей!
Это побуждает к проверке классов
Article.all
а также
Category.first.articles
оба возвращают класс Array.
Очевидно, что метод класса Article "search" вызывается во время выполнения и вызывает "правильный" возврат записей при доступе через его ассоциацию (Category), но ведет себя иначе, когда к нему обращается сам класс (Article).
Итак, что происходит?
2 ответа
Это потому, что когда вы выполняете .all
запрос фактически выполняется, поэтому возвращаемый объект будет базовым массивом. Тем не менее, когда вы выполняете Category.first
, он вернет объект Category, затем articles
на самом деле использует ActiveRecord::Reflection::AssociationReflection
и делает расширение массива. Например, в rails c
пытаться:
Category.first.articles.ancestors
против
Category.first.articles.all.ancestors #throws an error
Второй выдает ошибку, потому что в этот момент объект является простым массивом. Первый, однако, состоит из чего-то вроде этого:
Article(...), ActiveRecord::Base, ActiveRecord::Reflection, Object (and so on)
В качестве другого примера попробуйте это:
a = Category.first.articles; ObjectSpace.each_object(Class).select {|k| a < k }
#=> [Object, BasicObject, ActiveRecord::Base]
Вы можете видеть, что, хотя он выглядит как Array, он наследует эти другие классы.
Article.all
Это возвращает массив, который дает вам ошибку, как вы видели, вы пытаетесь запустить функцию класса Article
на Array
учебный класс.
Я не уверен, что именно вы пытаетесь сделать, но вы могли бы сделать это, я думаю
Article.search("soem content")
Тем не менее, я не уверен, что это вернет результат, так как это может просто вернуть