Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make a NullObject evaluate to falsy in Ruby

After implementing Null Object Pattern in a Rails application (also described in RubyTapas episode 112) I refactored out some code, but there's a syntax construct that seems to not work anymore.

I used to write statements like current_user || redirect_out, where, if current_user was set it would return it, and if it was nil it redirects out, but now current_user may be an instance of Null::User and thus "truthy", and that snippet would never redirect out.

I tried defining the || operator, but didn't work. Is there any way this syntax can still be used with null (but "truthy") objects?

like image 259
TuteC Avatar asked Aug 01 '13 08:08

TuteC


2 Answers

I think you have only halfway adopted that pattern, and have not correctly adopted its spirit. My understanding is that the very purpose of the pattern is to avoid ||.

You should have some purpose for calling current_user || redirect_out, and that could be doing something with it, or getting some attribute of it. For example, suppose your code has:

(current_user || redirect_out).foo

When current_user is not an instance of Null::User, you wanted to call foo on it. In that case, what you should do is define Null::User#foo to be redirect_out (possibly followed by some more operations like foo on other classes).

class Null::User
  def foo; redirect_out ... end
end

and in place of (current_user || redirect_out).foo, you should just do

current_user.foo

When current_user is not a Null::User instance, it will call foo. When it is such instance, then the redirect_out ... routine will be called on it.

like image 128
sawa Avatar answered Oct 12 '22 22:10

sawa


I once wrote an article about how it's not possible to define "falsy" objects in Ruby, and why attempts to make a Null Object falsy are generally misguided.

Basically, the best you can do is come up with a confusingly inconsistent object using #!, nil?, etc.

As others have noted, usually when you want to make a Null Object "falsy" it's because you're not full leveraging polymorphism. The whole point of a Null Object is to avoid type checks, and checking for NilClass in the form of an if statement is just as much a type check as any other.

That said, sometimes it's unavoidable. That's why in my Naught library I generate a helper conversion function called Actual() (among several other conversions). Actual() converts Null Objects back to nil values, but leaves all other objects alone. So for the cases where you need to switch on an object's truthiness, you can do it like this:

if Actual(obj_that_might_be_null)
  # ...do stuff...
end
like image 33
Avdi Avatar answered Oct 12 '22 23:10

Avdi