Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a NullObject pattern in Rails

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?

like image 367
dee Avatar asked Jun 15 '13 19:06

dee


People also ask

What is null object design pattern?

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.

How do you check if the value is null in rails?

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 .

Why does Ruby use 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.

What is null design pattern in C#?

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.


2 Answers

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
like image 90
Subhas Avatar answered Nov 04 '22 10:11

Subhas


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.

like image 3
Billy Chan Avatar answered Nov 04 '22 08:11

Billy Chan