TuteC
TuteC

Reputation: 4382

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?

Upvotes: 6

Views: 768

Answers (3)

sawa
sawa

Reputation: 168121

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.

Upvotes: 6

Avdi
Avdi

Reputation: 18418

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

Upvotes: 6

Jörg W Mittag
Jörg W Mittag

Reputation: 369478

There are exactly two objects which are falsy in Ruby: nil and false. Period.

Unfortunately, it is not possible to define your own falsy objects, nor is it possible to override the Boolean operators (except for not/!). This is a shame, really: it is one of the basic pillars of OO that an object can simulate another object, but in Ruby it is not possible to simulate false or nil, therefore breaking one of the fundamental properties of OO in a language with an otherwise pretty good OO model.

Upvotes: 4

Related Questions