Robert Ingrum
Robert Ingrum

Reputation: 234

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:

Upvotes: 0

Views: 100

Answers (2)

mrzasa
mrzasa

Reputation: 23347

Instead of setting the association in the callback, you may try to build relevance in the getter method:

class User
  def relevance
    self[:relevance] || User::Relevance.new
  end
end

This pattern is nicely described here: http://blog.arkency.com/2016/07/always-present-association/

Upvotes: 0

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230551

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.

Upvotes: 1

Related Questions