web-dev-qa-db-fra.com

Quelle est la bonne façon de remplacer une méthode de définition dans Ruby sur Rails?

J'utilise Ruby sur Rails 3.2.2 et je voudrais savoir si ce qui suit est un moyen "correct"/"correct"/"sûr" pour remplacer une méthode de définition pour un attribut de ma classe.

attr_accessible :attribute_name

def attribute_name=(value)
  ... # Some custom operation.

  self[:attribute_name] = value
end

Le code ci-dessus semble fonctionner comme prévu. Cependant, j'aimerais savoir si, en utilisant le code ci-dessus, j'aurai à l'avenir des problèmes ou, du moins, quels problèmes "devrais-je m'attendre"/"pourrait-il se produire" avec Ruby sur Rails Si ce n'est pas la bonne façon de remplacer une méthode de définition, quelle est la bonne?


Note: Si j'utilise le code

attr_accessible :attribute_name

def attribute_name=(value)
  ... # Some custom operation.

  self.attribute_name = value
end

Je reçois l'erreur suivante:

SystemStackError (stack level too deep):
  actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70
171
Backo

======================================= ================================ Mise à jour: 19 juillet 2017

Maintenant, le documentation Rails suggère également d'utiliser super comme ceci:

class Model < ActiveRecord::Base

  def attribute_name=(value)
    # custom actions
    ###
    super(value)
  end

end

======================================= =================================

Réponse originale

Si vous souhaitez remplacer les méthodes de définition pour les colonnes d'une table lors de l'accès via des modèles, procédez comme suit.

class Model < ActiveRecord::Base
  attr_accessible :attribute_name

  def attribute_name=(value)
    # custom actions
    ###
    write_attribute(:attribute_name, value)
    # this is same as self[:attribute_name] = value
  end

end

Voir Remplacement des accesseurs par défaut dans la documentation Rails.

Ainsi, votre première méthode est la bonne façon de remplacer les colonnes dans les modèles de Ruby sur Rails. Ces accesseurs sont déjà fournis par Rails pour accéder aux colonnes de la table en tant qu’attributs du modèle, c’est ce que nous appelons le mappage ActiveRecord ORM.

Rappelez-vous également que le attr_accessible au sommet du modèle n'a rien à voir avec les accesseurs. Il a une fonctionnalité complètement différente (voir cette question )

Mais en pur Ruby, si vous avez défini des accesseurs pour une classe et souhaitez remplacer le séparateur, vous devez utiliser une variable d'instance comme celle-ci:

class Person
  attr_accessor :name
end

class NewPerson < Person
  def name=(value)
    # do something
    @name = value
  end
end

Ce sera plus facile à comprendre une fois que vous saurez ce que attr_accessor Est-ce que. Le code attr_accessor :name est équivalent à ces deux méthodes (getter et setter)

def name # getter
  @name
end

def name=(value) #  setter
  @name = value
end

De plus, votre deuxième méthode échoue car elle provoquera une boucle infinie puisque vous appelez la même méthode attribute_name= à l'intérieur de cette méthode.

276
rubyprince

Utilisez le mot-clé super:

def attribute_name=(value)
  super(value.some_custom_encode)
end

Inversement, pour remplacer le lecteur:

def attribute_name
  super.some_custom_decode
end
42
Robert Kajic

In Rails 4

disons que vous avez l'attribut age dans votre table

def age=(dob)   
    now = Time.now.utc.to_date
    age = now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
    super(age) #must add this otherwise you need to add this thing and place the value which you want to save. 
  end

Remarque: pour les nouveaux venus dans Rails 4, vous n'avez pas besoin de spécifier attr_accessible dans le modèle. Vous devez plutôt répertorier vos attributs au niveau du contrôleur à l'aide de - permis méthode.

16
Taimoor Changaiz

J'ai constaté que (au moins pour les collections de relations ActiveRecord), le modèle suivant fonctionne:

has_many :specialties

def specialty_ids=(values)
  super values.uniq.first(3)
end

(Ceci saisit les 3 premières entrées non dupliquées du tableau passé.)

3
Robin Daugherty

En utilisant attr_writer pour écraser le setter attr_writer: nom_attribut

  def attribute_name=(value)
    # manipulate value
    # then send result to the default setter
    super(result)
  end
0
bananaappletw