Rails: Create association if none is found to avoid nil errors

Mattias picture Mattias · Sep 28, 2010 · Viewed 9k times · Source

I have an application where my users can have a set of preferences. Both are stored as ActiveRecord-models as follows:

class User < AR::Base
   has_one :preference_set
end

class PreferenceSet < AR::Base
   belongs_to :user
end

I can now access the preferences of a user:

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

But this fails if a preference set is not already created, since @u.preference_set will be returning nil, and I'll be calling play_sounds on nil.

What I want to archive is that User.preference_set always returns a PreferenceSet instance. I've tried defining it like this:

class User < ..
   has_one :preference_set

   def preference_set
     preference_set || build_preference_set
   end
end

This is causing a 'Stack level too deep', since it is calling itself recursively.

My question is this:

How can I ensure that @user.preference_set returns either the corresponding preference_set-record or, if none exists, builds a new one?

I know I could just rename my association (eg. preference_set_real) and avoid recursive calls this way, but for the sake of simplicity in my app, I'd like to keep the naming.

Thanks!

Answer

inopinatus picture inopinatus · Jul 24, 2013

There's an elegantly simple form:

class User < ApplicationRecord
  has_one :preference_set
  
  def preference_set
    super || build_preference_set
  end
end

ActiveRecord intentionally enables such use of super to override the behaviour of association methods.