EDIT | Or another question, on the same object subject. Can I write my own class definition that would cause the following all to work?
o = WeirdObject.new
puts "Object o evaluates as true in boolean expressions" if o # Line never output
puts "Object o is nil?" if o.nil? # Line is output
I can do the nil?
thing easy, since that's just a method. But no idea how to make it evaluate as a non-true value in a boolean expression (including just the most basic expression "o").
Original question follows...
More out of curiosity as to how much fun I can have with Ruby (1.9.2) really...
When a user is not logged in, I'd like to be able to detect it with unless @user
or unless @user.nil?
etc, but at the same time, I'd like to be able to call the methods of User
on this object and have them return the same NilClass
(ala Objective-C), since in most places this would remove the need for boilerplate if @user
code, while still acting like nil
in boolean expressions.
I see that you can't subclass NilClass
, since it doesn't have a #new
method. You can add methods directly to the instance, but it's a singleton, so this would cause issues if the only place I wanted to observe this behaviour was with non-logged-in users.
Is it possible?
What I mean, is something like this:
class CallableNil < NilClass
def method_missing(meth, *args, &block)
self
end
end
user = CallableNil.new # or .singleton, or something
puts "Got here" unless user # Outputs
puts "And here" unless user.nil? # also outputs
puts user.username # no-op, returns nil
This is basically how Objective-C handles nil and it's useful in some circumstances.
I guess I could not subclass NilClass and just implement the methods that would cause it to be false in boolean expressions?
EDIT | Haha, you really break Ruby pretty badly if you redefine the top-level NilClass
... it stops responding to any method call at all.
>> class NilClass
>> def method_missing(meth, *args, &block)
>> self
>> end
>> end
>>
?> nil.foo
>>
?>
?> puts "here"
>> quit
>> # WTF? Close dammit!
If your primary requirement is to be able to call methods on a nil
value without raising an exception (or having to check that it's defined), ActiveSupport
adds Object#try
which gives you the ability to do this:
instance = Class.method_that_returns_nil
unless instance
puts "Yes, we have no instance."
end
instance.try(:arbitrary_method) # => nil
instance.try(:arbitrary_method, "argument") # => nil
As you've seen, screwing around with NilClass
has some interesting side-effects.