Where you and Bill learn to avoid another common method_missing trap.
Once you get back from lunch, you find an unexpected problem waiting for you at the office. The developer who wrote the reporting application stumbled upon what he thinks is “the strangest bug ever”: the Computer class can’t retrieve information about the workstations’ displays. All the other methods work fine, but Computer#display doesn’t.
You try the display method in irb, and sure enough it fails:
| my_computer = Computer.new(42, DS.new) |
| my_computer.display # => nil |
Why does Computer#display return nil? You triple-check the code and the back-end data source, but everything seems to be fine. Bill has a sudden insight, and he lists the instance methods of Object that begin with a d:
| Object.instance_methods.grep /^d/ # => [:dup, :display, :define_singleton_method] |
It seems that Object defines a method named display (a seldom-used method that prints an object on a port and always returns nil). Computer inherits from Object, so it gets the display method. The call to Computer#display finds a real method by that name, so it never lands on method_missing. You’re calling a real, live method instead of a Ghost Method ().
This problem crops up with Dynamic Proxies (). When the name of a Ghost Method clashes with the name of a real, inherited method, the latter wins.
If you don’t need the inherited method, you can fix the problem by removing it. While you’re at it, you might want to remove most methods from the class, preventing such name clashes from ever happening again. A skinny class with a minimal number of methods is called a Spell: . As it turns out, Ruby has a ready-made Blank Slate for you to use.
The root of Ruby’s class hierarchy, BasicObject, has only a handful of instance methods:
| im = BasicObject.instance_methods |
| im # => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__] |
If you don’t specify a superclass, your classes inherit by default from Object, which is itself a subclass of BasicObject. If you want a Blank Slate (), you can inherit directly from BasicObject instead. For example, if Computer inherited directly from BasicObject, then it wouldn’t have a problematic display method.
Inheriting from BasicObject is the quicker way to define a Blank Slate in Ruby. In some cases, however, you might want to control exactly which methods to keep and which methods to remove from your class. Let’s see how you can remove a specific method from a class.
You can remove a method from a class by using either Module#undef_method or Module#remove_method. The drastic undef_method removes any method, including the inherited ones. The kinder remove_method removes the method from the receiver, but it leaves inherited methods alone. Let’s look at a real-life library that uses undef_method to create a Blank Slate.
The Builder gem is an XML generator with a twist. You can generate XML tags by calling methods on Builder::XmlMarkup:
| require 'builder' |
| xml = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2) |
| |
| xml.coder { |
| xml.name 'Matsumoto', :nickname => 'Matz' |
| xml.language 'Ruby' |
| } |
This code produces the following snippet of XML:
<= | <coder> |
| <name nickname="Matz">Matsumoto</name> |
| <language>Ruby</language> |
| </coder> |
Builder cleverly bends the syntax of Ruby to support nested tags, attributes, and other niceties. The core idea of Builder is simple: calls such as name and language are processed by XmlMarkup#method_missing, which generates an XML tag for every call.
Now pretend you have to generate a piece of XML describing a university course. It might look like this:
<= | <semester> |
| <class>Egyptology</class> |
| <class>Ornithology</class> |
| </semester> |
So, you’d have to write code like this:
| xml.semester { |
| xml.class 'Egyptology' |
| xml.class 'Ornithology' |
| } |
If XmlMarkup were a subclass of Object, then the calls to class would clash with Object’s class. To avoid that clash, XmlMarkup inherits from a Blank Slate () that removes class and most other methods from Object. When Builder was written, BasicObject didn’t exist yet. (It was introduced in Ruby 1.9.) So Builder defines its own Blank Slate class:
| class BlankSlate |
| # Hide the method named +name+ in the BlankSlate class. Don't |
| # hide +instance_eval+ or any method beginning with "__". |
| def self.hide(name) |
| # ... |
| if instance_methods.include?(name._blankslate_as_name) && |
| name !~ /^(__|instance_eval$)/ |
| undef_method name |
| end |
| end |
| # ... |
| |
| instance_methods.each { |m| hide(m) } |
| end |
Builder doesn’t go as far as removing each and every method from BlankSlate. It keeps instance_eval (a method that you’ll get to know in the next chapter) and all the “reserved methods”—methods that are used internally by Ruby, whose names conventionally begin with a double underscore. One example of a reserved method is BasicObject#__send__, which behaves the same as send but gives you a scary warning when you try to remove it. The case of instance_eval is more of a judgement call: you could choose to remove it, but Builder decided not to.
Now that you know about Blank Slates, you can finally fix the bug in the Computer class.
To turn Computer into a Blank Slate () and fix the display method bug, you and Bill make it a subclass of BasicObject:
* | class Computer < BasicObject |
| # ... |
There is one last improvement you can make to this class. BasicObject doesn’t have a respond_to? method. (respond_to? is a method of BasicObject’s subclass Object.) Because you don’t have respond_to?, you can delete the now pointless respond_to_missing? method that you and Bill added back in . Once you do that, you’re finally done with the method_missing-based implementation of Computer.