Массив, возвращенный модельной ассоциацией, не является Массивом? - PullRequest
2 голосов
/ 12 марта 2011

У нас есть модельная ассоциация, которая выглядит примерно так:

class Example < ActiveRecord::Base
  has_many :others, :order => 'others.rank'
end

Столбец ранга является целочисленным типом. Детали этих конкретных моделей не очень важны, поскольку мы обнаружили ту же проблему с другими ассоциациями has_many между другими моделями.

Мы также добавили в модуль Enumerable:

module Enumerable
  def method_missing(name)
    super unless name.to_s[0..7] == 'collect_'
    method = name.to_s[8..-1]
    collect{|element| element.send(method)}
  end
end

Это добавляет метод collect_id, который мы можем использовать для получения массива идентификаторов записей из массива объектов ActiveRecord.

Так что, если мы используем обычную ActiveRecord find: all, мы получаем хороший массив, который мы можем затем использовать collect_id, но если мы используем Example.others.collect_id, мы получим

NoMethodError: undefined method `collect_id' for #<Class:0x2aaaac0060a0>

Example.others.class возвращает «Массив», так что это ложь или путаница?

Наше решение до сих пор заключалось в том, чтобы использовать его следующим образом:

Example.others.to_a.collect_id 

Это работает, но это кажется немного странным. Зачем тебе это делать?

Мы на Ruby 1.8.7 и Rails 2.3.4

Ответы [ 4 ]

5 голосов
/ 12 марта 2011

Ассоциации моделей - это прокси, а не просто массивы.

Вместо example.others.all.collect_id и вашего патча, я предлагаю вам использовать example.others.all.map(&:id), который является стандартным способом Rails и Ruby> = 1.8.7 для сбораодин атрибут.

3 голосов
/ 12 марта 2011

Ассоциации ActiveRecord лениво загружают записи has_many по соображениям производительности.Например, если вы вызываете example.others.count, вам не нужно загружать все записи.Попробуйте добавить это рядом со своим патчем к перечисляемому:

class ActiveRecord::Associations::AssociationCollection
  def method_missing(name)
    super unless name.to_s[0..7] == 'collect_'

    load_target unless loaded?
    method = name.to_s[8..-1]
    @target.collect{|element| element.send(method)}
  end
end
2 голосов
/ 12 марта 2011

Два возможных решения:

1) Расширить конкретную ассоциацию:

class Example < ActiveRecord::Base
  has_many :others, :order => 'others.rank' do
    def method_missing(name)
      super unless name.to_s[0..7] == 'collect_'
      method = name.to_s[8..-1]
      collect{|element| element.send(method)}
    end
  end
end

2) Добавьте расширения в модуль, чтобы получить повторяемое решение.

Rails предоставляет возможность расширить массив ассоциаций.

module Collector
  def method_missing(name)
    super unless name.to_s[0..7] == 'collect_'
    method = name.to_s[8..-1]
    collect{|element| element.send(method)}
  end
end

class Example < ActiveRecord::Base
  has_many :others, :order => 'others.rank', :extend => Collector
end

Прочитайте документацию для получения более подробной информации. Найдите «Расширения ассоциации» на странице, чтобы перейти к соответствующему разделу.

0 голосов
/ 12 марта 2011

Вы должны использовать all

 example.others.all.collect_id
...