2010-09-28 9 views
11

Ho un'applicazione in cui i miei utenti possono avere una serie di preferenze. Entrambi vengono memorizzati come ActiveRecord-modelli come segue:Rails: crea un'associazione se non viene trovata alcuna corrispondenza per evitare errori nil

class User < AR::Base 
    has_one :preference_set 
end 

class PreferenceSet < AR::Base 
    belongs_to :user 
end 

ora posso accedere alle preferenze di un utente:

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

Ma questo non riesce se un insieme preferenza non è già stato creato, dal momento che @u. preferenza_set restituirà nil, e chiamerò play_sounds su nil.

Quello che voglio archiviare è che User.preference_set restituisce sempre un'istanza PreferenceSet. Ho provato a definire in questo modo:

class User < .. 
    has_one :preference_set 

    def preference_set 
    preference_set || build_preference_set 
    end 
end 

Questo sta causando un 'Stack level too deep', dal momento che sta chiamando se stessa in modo ricorsivo.

mia domanda è questa:

Come posso garantire che @user.preference_set rendimenti sia corrispondente preference_set-record o, se non esiste, costruisce uno nuovo?

So che potrei semplicemente rinominare la mia associazione (ad esempio preference_set_real) ed evitare le chiamate ricorsive in questo modo, ma per motivi di semplicità nella mia app, mi piacerebbe mantenere la denominazione.

Grazie!

risposta

27

Bene il modo migliore per farlo è quello di creare il record associato quando si crea quello primario:

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

Che vi impostarlo in modo ogni volta che viene creato un User, quindi è un PreferenceSet. Se è necessario inizializzare il record associato con argomenti, chiamare un metodo diverso in before_create che chiama build_preference_set(:my_options => "here") e quindi restituisce true.

È quindi possibile normalizzare tutti i record esistenti iterando su uno qualsiasi che non ha uno PreferenceSet e costruendolo uno chiamando #create_preference_set.

Se si desidera creare solo il PreferenceSet quando è assolutamente necessario, allora si può fare qualcosa di simile:

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, non si era imbattuto alias_method_chain. Questo è figo. – Chowlett

+0

Tieni presente che 'alias_method_chain' è stato deprecato a favore di' Modulo # anteporre'. Maggiori informazioni qui: https://github.com/rails/rails/pull/19434 –

38

o semplicemente

class User < ApplicationRecord 
    has_one :preference_set 

    def preference_set 
    super || build_preference_set 
    end 
end 
Problemi correlati