1 minute read

Ruby method names can contain a question mark or an exclamation point, but it must be the last character. So foo? and foo! are fine, but foo?bar and foo!bar are not.

This does not seem like a big deal until you run into code that assumes that it can add characters to the end of any method name. For example, Ruby Facets has a method called cache which allows the user to ensure a method is called once and only once per instance. You can see the code and comments in the Facets SVN Repository.

For example, the following code will only print bar once:

def foo
  puts "bar"
end
cache :foo

foo
foo

cache is implemented by aliasing the original method and redefining it. The new version checks a hash to see if it has already been called. The aliasing is done with the line:

alias_method '__#{ m }__', '#{ m }'

So a method called:

foo

will be aliased to:

__foo__

This will fail if the method ends in a ? or ! since it cannot append __ to the end of the method name.

This is a subtle bug to track down. The error is cryptic, since the ruby parser fails when it reaches the trailing underscores. Furthermore, the code doesn’t fail until the method is called, not when the caching is done:

def foo?
  true
end
cache :foo?

>> foo?
NoMethodError: undefined method `__' for #<Object:0xb7cc99f0 @cache={"foo?"=>{}}>
        from (eval):8:in `foo?'
        from (irb):34

A better way to redefine methods is to unbind and rebind them. This technique is explained nicely in Jay’s blog: Ruby: Alias method alternative

Updated: