2.2 Extending Object

Don't think you need not clean up your "nots"!

If you've read the original Ruby Primer, you'll remember the "hiring programmers" problem. It was one of the first places the Primer really pushed the reader to think about different pieces of logic in a single statement. Often when working with this sort of code, you'll run into negations using Ruby's bang operator (!). Experienced programmers are trained to hunt for these wispy characters in code they read but even a master programmer can miss one during a debugging session after a long night of hacking.

An alternative to code like this: !customer.has_receipt? || !clerk.handles?(customer) would be this: customer.not.has_receipt? || clerk.not.handles?(customer). The English word "not" helps us read this code as "this condition passes if the customer is missing her receipt or if the clerk cannot handle the customer." It's worth noting here that Ruby does have a "not" keyword, used as a prefix. If you don't consider this method more readable than the built-in keyword, just treat it as an interesting thought exercise.

Fill out the Not class below, and use it to return an object capable of inverting calls to Object#not. We'll use calls to smith? as examples and in the tests. You'll see why in the next exercise.

Hint

Remember the object can pass itself to the `Not` using `self`. Use `send` to proxy the method call from `method_missing` to the original object.

Output Window

Minor magic! It's almost as if we've added a new feature to the language. Ostensibly, we have -- we've changed every object in our program. Of course, one should be wary of wielding such power loosely. These sorts of sweeping changes need to be carefully considered. Speaking of consideration, let's use the implementation from the above solution to try something a little different:

Example Code:

Output Window

Oh no! What's happened? true is most certainly not equivalent to false... at least not in Ruby. Take a look at the solution again. Can you see something we missed? If not, try running this:

Example Code:

Output Window

Ooooohhhh, snap. See it now? Not responds to nil?. That's going to be a problem for us. No instance of Not will ever be nil, by definition. That means we need to trap messages sent to Not objects for methods defined on Object itself so they're handled by our inversion logic

Hint

Making an object's instance methods private will allow `method_missing` to catch them.

Output Window

The world's tiniest debugger

Often when you've been working on a Ruby project for an extended period of time, you'll find you need to quickly debug values of any old random thing quickly and easily. Sometimes this is in the full execution of your program. Sometimes within the context of a unit test. Whatever the environment, wouldn't it be convenient to have a one-liner which replaced this?

Example Code:

Output Window

Try writing one. Again, we'll reopen Object so our dbg (short for debug) method is available everywhere.

Hint

Don't forget you probably want to inspect the object you're debugging!

Output Window

Congratulations, guest!


% of the book completed

or

This lesson is Copyright © 2011-2024 by Sidu Ponnappa and Jasim A Basheer