2010-09-28 9 views
11

J'ai une application où mes utilisateurs peuvent avoir un ensemble de préférences. Les deux sont stockées sous forme de modèles ActiveRecord-comme suit:Rails: Créer une association si aucun n'est trouvé pour éviter les erreurs nulles

class User < AR::Base 
    has_one :preference_set 
end 

class PreferenceSet < AR::Base 
    belongs_to :user 
end 

Je peux maintenant accéder aux préférences d'un utilisateur:

@u = User.first 
@u.preference_set => #<PreferenceSet...> 
@u.preference_set.play_sounds => true 

Mais échoue si un ensemble de préférence n'est pas déjà créé, depuis @u. preference_set retournera zéro, et je vais appeler play_sounds sur nil. Ce que je veux archiver, c'est que User.preference_set renvoie toujours une instance de PreferenceSet. Je l'ai essayé de le définir comme ceci:

class User < .. 
    has_one :preference_set 

    def preference_set 
    preference_set || build_preference_set 
    end 
end 

Cela provoque une 'Stack level too deep', car il appelle lui-même récursive.

Ma question est la suivante:

Comment puis-je faire en sorte que ce soit le @user.preference_set retours preference_set-enregistrement ou, si aucune existe correspondante, construit un nouveau?

Je sais que je pourrais simplement renommer mon association (par exemple preference_set_real) et éviter les appels récursifs de cette façon, mais par souci de simplicité dans mon application, je voudrais garder le nom.

Merci!

Répondre

27

Eh bien la meilleure façon de le faire est de créer l'enregistrement associé lorsque vous créez le principal:

class User < ActiveRecord::Base 
    has_one  :preference_set, :autosave => true 
    before_create :build_preference_set 
end 

Ce redresserai donc chaque fois qu'un User est créé, est donc un PreferenceSet. Si vous devez initialiser l'enregistrement associé avec des arguments, appelez une méthode différente dans before_create qui appelle build_preference_set(:my_options => "here") et renvoie true.

Vous pouvez ensuite normaliser tous les enregistrements existants en itérant sur ceux qui n'ont pas de PreferenceSet et en créant un en appelant le #create_preference_set.

Si vous voulez seulement créer le PreferenceSet quand il est absolument nécessaire, alors vous pouvez faire quelque chose comme:

class User < ActiveRecord::Base 
    has_one :preference_set 

    def preference_set_with_initialize 
    preference_set_without_initialize || build_preference_set 
    end 

    alias_method_chain :preference_set, :initialize 
end 
+3

Wow, n'a pas rencontré alias_method_chain. C'est super. – Chowlett

+0

Soyez avisé que 'alias_method_chain' a été abandonné en faveur de' Module # prepend'. Plus d'infos ici: https://github.com/rails/rails/pull/19434 –

38

ou simplement

class User < ApplicationRecord 
    has_one :preference_set 

    def preference_set 
    super || build_preference_set 
    end 
end