Robert Ingrum Robert Ingrum - 2 months ago 14
Ruby Question

How to lazy-set Mongoid association

I have two models that are associated by a

has_one
relationship. We also have a callback that initializes the association. The classes look (roughly) like this:

class User
has_one :relevance, class_name: 'User::Relevance', inverse_of: :user, dependent: :destroy
after_create :initialize_relevance

def initialize_relevance
self[:relevance] = User::Relevance.new
end

# other garbage that *should* be irrelevant
end

class User::Relevance
belongs_to :user, inverse_of: :relevance, index: true

# more other garbage that *should* be irrelevant
end


Sometimes the relevance association gets into a wonked state where it is nil. When this happens, we want to re-initialize the relationship when it is called and return it instead of nil. So in the class
User
, we would have this:

def relevance
self[:relevance] = User::Relevance.new if self[:relevance].nil?

self[:relevance]
end


Except this doesn't work and
nil
is still returned. I've also tried the same with
update_attribute(User::Relevance.new)
and
self.create_relevance
but
nil
seems to always be returned. Not really sure where to go from here and would love any ideas. I can also provide more code or examples if it would be helpful.

Additional Details:


  • We're using mongoid for our database.

  • We don't have any other callbacks that would affect
    relevance
    .


Answer

Mongoid supports autobuilding one-to-one relations. This should help (no hooks or getter overrides needed).

has_one :relevance, 
        class_name: 'User::Relevance', 
        inverse_of: :user, 
        dependent: :destroy, 
        autobuild: true

As the option name hints, relevance will spring to life upon access (if it was nil before access).

Also, you are aware that this way relevance is not persisted, right?

  after_create :initialize_relevance

  def initialize_relevance
    self[:relevance] = User::Relevance.new
  end

So no wonder it returns nil later on.