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?
The Null object pattern is a design pattern that simplifies the use of dependencies that can be undefined. This is achieved by using instances of a concrete class that implements a known interface, instead of null references.
You can check if an object is nil (null) by calling present? or blank? . @object. present? this will return false if the project is an empty string or nil .
Well, nil is a special Ruby object used to represent an “empty” or “default” value. It's also a “falsy” value, meaning that it behaves like false when used in a conditional statement.
In Null Object pattern, we create an abstract class specifying various operations to be done, concrete classes extending this class and a null object class providing do nothing implemention of this class and will be used seemlessly where we need to check null value.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With