2009-09-19 11 views
2

J'ai été mordu par cela récemment, et il serait utile de savoir précisément ce qui se passe pour que cela se produise, afin que les autres évitent cette erreur.Pourquoi attr_accessor clobber les variables existantes dans ce modèle dans Ruby on Rails?

J'ai un modèle utilisateur, avec un schéma comme ceci:

create_table "users", :force => true do |t| 
    t.string "user_name" 
    t.string "first_name" 
    t.string "last_name" 
    t.string "email" 
    t.string "location" 
    t.string "town" 
    t.string "country" 
    t.string "postcode" 
    t.boolean "newsletter" 

Dans la user.rb de classe, j'ai un attr_accessor pour trois méthodes:

class User < ActiveRecord::Base 

# lots of code 

    attr_protected :admin, :active 

# relevant accessor methods 

    attr_accessor :town, :postcode, :country 

end 

Maintenant, dans mon contrôleur utilisateur , si j'ai la méthode suivante:

def create 
    @user = User.new params[:user] 
end 

lorsque lorsque je tente de créer un nouvel utilisateur avec le contenu de cette hachage params:

--- !map:HashWithIndifferentAccess 
    # other values 
    country: United Kingdom 
    dob(1i): "1985" 
    dob(2i): "9" 
    dob(3i): "19" 
    town: london 

L'objet retourné a des chaînes vides pour les country, town et code postal postcode valeurs, comme ainsi.

(rdb:53) y user1 
--- !ruby/object:User 
attributes: 
    # lots of attributes that aren't relevant for this example, and are filled in okay 
    postcode: 
    country: 
    town: 

Je peux dire que les méthodes de attr_accessor sont clobbering de Active Record méthodes accesseurs existants, parce que quand je les sors tout fonctionne très bien, donc la solution est assez simple - il suffit de les sortir.

Mais exactement ce qui se passe quand ici?

Je regarde ici dans le Rails API docs for Active Record, et ici dans Ruby's own docs about attr_accessor, mais je suis encore un peu flou sur la façon dont attr_accessor casse les choses ici.

Est-il possible de jeter de la lumière pour empêcher une autre mauvaise âme de tomber dans ce piège?

+0

+1 Plus de questions nécessitent le mot clobber. –

Répondre

8

Lorsque vous ajoutez un attr_accessor à une classe, il définit deux méthodes, par ex. Code postal de l'utilisateur # et code postal de l'utilisateur #.

Si le nom de l'accesseur est égal au nom d'un attribut de modèle, les choses se brisent (si vous ne faites pas attention). Lorsque vous attribuez des attributs au modèle, l'utilisateur # code postal = est appelé et dans votre cas, il ne fait rien sauf

@postcode = value 

Ainsi, la valeur se juste stockée dans une variable d'instance et ne figure pas dans le hachage des attributs.

Alors que dans un scénario normal (sans accesseur) cela irait à method_missing et éventuellement déclencher quelque chose comme

write_attribute(:postcode, value) 

Et puis il apparaît dans les attributs de votre modèle. Espérons que cela a du sens.

0

Pourquoi à la première place vous utilisez attr_accessor :town, :postcode, :country? Active Record a des méthodes setter/getter pour vous. Il suffit de laisser tomber cette ligne, les choses devraient fonctionner.

+1

Khell, - Je ne demandais pas comment résoudre ce problème; c'est pris en charge. J'essayais de comprendre ce qui se passe sous le capot. –

0

Vous pouvez utiliser attr_accessible sur les modèles ActiveRecord pour activer l'attribution en masse d'attributs. Vous n'avez pas besoin de attr_accessor étant donné que les getters/setters sont déjà définis pour les attributs de modèle.