Implementing a NullObject pattern in Rails
Asked Answered
W

2

6

I an application where a Post belongs_to :user I want to retain posts for deleted users. This can cause errors in the view when viewing a post whose author was deleted. I tried to do this:

class Post < ActiveRecord::Base
  belongs_to :author, class_name: 'User', foreign_key: 'user_id'

  def author
    author || NullUser.new
    super
  end
end

This causes a 'stack level to deep` error. Why? I could do this:

class Post < ActiveRecord::Base
  belongs_to :user

  def author
    user || NullUser.new
  end

  def author=(user)
    self.user = user
  end
end

But it doesn't seem right to mess with my associations this way. What's the best way to go about this?

Wont answered 15/6, 2013 at 19:54 Comment(1)
Sidenote: Related to NullObjects and just came out, Naught by Avdi Grimm.Scar
M
14

To answer your question,

1. def author
2.   author || NullUser.new
3.   super
4. end

In line 1, you're defining an author method. Then in line 2, you're again calling that author method! This keeps on happening, and you get the stack level too deep error. The proper way to do that is,

def author
  super || NullUser.new
end

So you're not again calling the author method inside itself. You're just calling the super class or returning NullUser. In case you get a nil error when you're calling super, then add an additional nil check:

def author
  (super || NullUser.new) rescue NullUser.new
end

The rescue statement will catch all errors and then return NullUser.new, so you don't have to worry about super throwing an error as well.

EDIT:

Another way of handling super throwing exception which looks nicer:

def author
  (super rescue nil) || NullUser.new
end
Multimillionaire answered 15/6, 2013 at 20:28 Comment(0)
L
3

If you want to retain posts for deleted users, it's better not to really "delete" them.

An alternative is "soft delete". Just add a boolean column say "deleted" or "inactive".

So, when you are going to delete an user, check if he has posts. If nothing, hard delete him. If having, soft delete.

This way things would be much simpler and clean.

Another way is to "steal" the posts. When deleting a user, move all his posts under a special user account, then delete him.

Either way you won't break the association.

Leitmotiv answered 15/6, 2013 at 20:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.