Почему / Как метод класса доступен массиву записей только через ассоциацию в 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")

Тем не менее, я не уверен, что это вернет результат, так как это может просто вернуть

Другие вопросы по тегам