2010-09-28 9 views
11

Tengo una aplicación donde mis usuarios pueden tener un conjunto de preferencias. Ambos se almacenan como ActiveRecord-modelos de la siguiente manera:Rieles: Crear asociación si no se encuentra ninguno para evitar errores nulos

class User < AR::Base 
    has_one :preference_set 
end 

class PreferenceSet < AR::Base 
    belongs_to :user 
end 

que ahora pueden acceder a las preferencias de un usuario:

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

Pero esto no funciona si un conjunto de preferencias no se ha creado, ya que @u. preference_set devolverá nil, y llamaré al play_sounds en nil.

Lo que quiero archivar es que User.preference_set siempre devuelve una instancia de PreferenceSet. He intentado definir así:

class User < .. 
    has_one :preference_set 

    def preference_set 
    preference_set || build_preference_set 
    end 
end 

Esto está causando un 'Stack level too deep', ya que está llamando a sí mismo de forma recursiva.

Mi pregunta es la siguiente:

¿Cómo puedo asegurar que @user.preference_set devuelve o el correspondiente preference_set-registro o, si no existe, se construye una nueva?

Sé que podría cambiar el nombre de mi asociación (por ejemplo, preference_set_real) y evitar las llamadas recursivas de esta manera, pero por simplicidad en mi aplicación, me gustaría mantener el nombre.

Gracias!

Respuesta

27

Bueno, la mejor manera de hacer esto es crear el registro asociado al crear el principal:

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

Eso será configurarlo para que cada vez que se crea un User, por lo que es un PreferenceSet. Si necesita inicializar el registro asociado con argumentos, llame a un método diferente en before_create que llama al build_preference_set(:my_options => "here") y luego devuelve true.

Puede simplemente normalizar todos los registros existentes iterando sobre cualquiera que no tenga un PreferenceSet y compilando uno llamando al #create_preference_set.

Si sólo desea crear el PreferenceSet cuando es absolutamente necesario, entonces usted puede hacer algo como:

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

Vaya, no había llegado a través de alias_method_chain. Eso es genial. – Chowlett

+0

Tenga en cuenta que 'alias_method_chain' ha quedado en desuso en favor de' Module prepend de'. Más información aquí: https://github.com/rails/rails/pull/19434 –

38

o simplemente

class User < ApplicationRecord 
    has_one :preference_set 

    def preference_set 
    super || build_preference_set 
    end 
end 
Cuestiones relacionadas