How can I create an Object in ruby that will be evaluated to false in logical expressions similar to nil?
My intention is to enable nested calls on other Objects where somewhere half way down the chain a value would normally be nil
, but allow all the calls to continue - returning my nil-like object instead of nil
itself. The object will return itself in response to any received messages that it does not know how to handle and I anticipate that I will need to implement some override methods such as nil?
.
For example:
fizz.buzz.foo.bar
If the buzz
property of fizz
was not available I would return my nil-like object, which would accept calls all the way down to bar
returning itself. Ultimately, the statement above should evaluate to false.
Edit:
Based on all the great answers below I have come up with the following:
class NilClass
attr_accessor :forgiving
def method_missing(name, *args, &block)
return self if @forgiving
super
end
def forgive
@forgiving = true
yield if block_given?
@forgiving = false
end
end
This allows for some dastardly tricks like so:
nil.forgiving {
hash = {}
value = hash[:key].i.dont.care.that.you.dont.exist
if value.nil?
# great, we found out without checking all its parents too
else
# got the value without checking its parents, yaldi
end
}
Obviously you could wrap this block up transparently inside of some function call/class/module/wherever.
There is a principle called the Law of Demeter [1] which suggests that what you're trying to do is not good practice, as your objects shouldn't necessarily know so much about the relationships of other objects.
However, we all do it :-)
In simple cases I tend to delegate the chaining of attributes to a method that checks for existence:
So I can now call fizz.buzz_foo_bar knowing I won't get an exception.
But I've also got a snippet of code (at work, and I can't grab it until next week) that handles method missing and looks for underscores and tests reflected associations to see if they respond to the remainder of the chain. This means I don't now have to write the delegate methods and more - just include the method_missing patch:
[1] http://en.wikipedia.org/wiki/Law_of_Demeter
As far as I'm aware there's no really easy way to do this. Some work has been done in the Ruby community that implements the functionality you're talking about; you may want to take a look at:
try
methodThe andand gem is used like this:
You can modify the
NilClass
class to usemethod_missing()
to respond to any not-yet-defined methods.This is a pretty long answer with a bunch of ideas and code samples of how to approach the problem.
try
Rails has a try method that let's you program like that. This is kind of how it's implemented:
You can program with it like this:
You could conceivably modify this to work a little differently to support a more elegant API:
Then you could do:
andand
andand uses a more nefarious technique, hacking the fact that you can't directly instantiate NilClass subclasses:
This allows you to program this way:
Combine with some fancy rewriting
Again you could expand on this technique:
To explain what's going on: when you call an underscored method you trigger mock mode, the result of
_meth
is wrapped automatically in aMock
object. Anytime you call a method on that mock it checks whether its not holding anil
and then forwards your method to that object (here stored in the@me
variable). The mock then replaces the original object with the result of your function call. When you callmeth_
it ends mock mode and returns the actual return value ofmeth
.This allows for an api like this (I used underscores, but you could use really anything):
Brutal monkey-patching approach
This is really quite nasty, but it allows for an elegant API and doesn't necessarily screw up error reporting in your whole app:
Use like this: