Super Secret Methods
Here is a quirk of the Ruby language that I discovered a few weeks ago.
Method names can not contain a period character.
def secret.squirrel
:shhh
end
#=> undefined local variable or method `secret' (NameError)
This is because the period character is syntax used for defining methods on specific objects.
secret = "hello"
def secret.squirrel
:shhh
end
p secret #=> "hello"
p secret.squirrel #=> :shhh
Even though the syntax does not allow periods in the method name,
surprisingly, the Ruby runtime will allow it.
Using define_method
, the syntax limitations can be bypassed.
# this works
define_method('secret.squirrel') do
:shhh
end
The method above can’t be called using normal Ruby syntax.
secret.squirrel
#=> undefined local variable or method `secret' (NameError)
But again, the syntax limitations can be bypassed, using send
.
p send('secret.squirrel') #=> :shhh
To summarize, it is possible to define and call methods that are impossible to access via normal Ruby syntax.
Abuses Applications
Upon discovering this, my first thought was “this is a bug in Ruby.” My next thought, of course, was “what could I use this for?” The only use that I can think of is avoiding method pollution.
Sometimes, when writing mixins or base classes, you want to define methods for internal use only. You don’t want these internal methods to accidentally override or conflict with other peoples’ methods.
Roda is very concerned about pollution. […] Roda purposely limits the instance variables, methods, and constants available in the route block scope, so that it is unlikely you will run into conflicts. If you’ve ever tried to use an instance variable named @request inside a Sinatra::Base subclass, you’ll appreciate that Roda attempts to avoid polluting the scope.
So this trick can be used to make “secret” methods which don’t pollute the namespace of normal methods – although the same trick does not work for instance variables or constants.
instance_variable_set("@secret.squirrel", :shhh)
#=> NameError: `@secret.squirrel' is not allowed as an instance variable name
String.const_set('SECRET.SQUIRREL', :shhh)
#=> NameError: wrong constant name SECRET.SQUIRREL
Disclaimer
I’m sure someone’s already frothing at the keyboard, writing a mean-spirited comment on Reddit, for daring to suggest that this trick might be useful in any way.
So let me be clear: this is probably a bad idea.
I’m still not convinced that it is intended behaviour. I know that method names are allowed to contain unicode, but allowing the period character seems inconsistent to me, since it’s not allowed for instance variables or constants.
I also haven’t tried it on JRuby, or any other implementations. It might only work on MRI.
It’s just an interesting insight into the internals of Ruby.
Got questions? Comments? Milk?
Shoot an email to [email protected] or hit me up on Twitter (@tom_dalling).