2 votes

Un tableau retourné par une association de modèle n'est pas un tableau ?

Nous avons un modèle d'association qui ressemble à quelque chose comme ceci :

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

La colonne rang est un type entier. Les détails de ces modèles particuliers ne sont cependant pas vraiment importants car nous avons trouvé le même problème avec d'autres associations has_many entre d'autres modèles.

Nous avons également ajouté au module 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

Cela ajoute une méthode collect_id que nous pouvons utiliser pour obtenir un tableau d'identifiants d'enregistrement à partir d'un tableau d'objets ActiveRecord.

Ainsi, si nous utilisons une recherche normale ActiveRecord find :all, nous obtenons un joli tableau sur lequel nous pouvons ensuite utiliser collect_id, mais si nous utilisons Example.others.collect_id, nous obtenons

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

Example.others.class renvoie "Array", donc il ment ou il est confus ?

Notre solution jusqu'à présent a été de l'utiliser de cette façon :

Example.others.to_a.collect_id 

Cela fonctionne mais cela semble un peu étrange. Pourquoi devez-vous faire cela ?

Nous sommes sous Ruby 1.8.7 et Rails 2.3.4.

6voto

topherx Points 101

Les associations de modèles sont des proxies, et non de simples tableaux.

Au lieu de example.others.all.collect_id et votre patch, je vous suggère d'utiliser example.others.all.map(&:id) qui est la méthode standard de Rails et Ruby >=1.8.7 pour collecter un seul attribut.

3voto

tilleryj Points 5589

Les associations ActiveRecord chargent paresseusement les enregistrements has_many pour des raisons de performance. Par exemple, si vous appelez example.others.count, vous n'avez pas besoin de charger tous les enregistrements. Essayez d'ajouter ceci en même temps que votre patch pour enumerable :

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

2voto

Harish Shetty Points 38877

Deux solutions possibles :

1) Étendre une association spécifique :

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) Ajouter les extensions à un module pour obtenir une solution reproductible.

Rails fournit une option pour étendre le tableau d'association.

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

Lire l'article documentation pour plus de détails. Recherchez "Extensions d'association" dans la page pour accéder à la section correspondante.

0voto

fl00r Points 41855

Vous devez utiliser all

 example.others.all.collect_id

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X