Exploring Ruby Metaprogramming Footguns
The last few weeks, I’ve been learning different languages to see what’s popular outside my regular sphere. My journey took me to lots of articles written by Ruby enthusiasts, and the common usage of metaprogramming. Plenty of languages support metaprogramming, but I’ve seen very few communities use and encourage it actively. It’s even used in considerably “Enterprise Projects” such as RSpec and Active Record.
In an effort to have fun with metaprogramming, I’ve created a project of intentional Ruby Footguns. This project is not a point against metaprogramming or meant to showcase potential problems, it is simply meant to make me laugh… or facepalm.
My first entry in this repo is quite basic, a class that implements
method_missing in a rather horrible way.
If you invoke a non-existent method, it will invoke a method with a Levenshtein distance of two or less, and subsequently make it private.
By design, it punishes small typos by causing
NoMethodError to appear from seemingly innocuous lines of code.
class TypoBomb def print_stuff puts "My method is working as expected" end def method_missing(method_name, *args, &block) name = methods.select do |name| levenshtein(name.to_s, method_name.to_s) <= 2 end.first if !name.nil? val = send(name, *args, &block) self.class.undef_method(name) return val else raise NoMethodError end end end b = TypoBomb.new b.pint_stuff # > "My method is working as expected" # more stuff happening until... b.print_stuff # BOOM! raises NoMethodError and confusion ensues
So now this seemingly perfect line is raising an error, when the real culprit is the typo that occurred elsewhere.
As another consequence, the typo doesn’t even have to occur on the same instance.
If an instance
print_stoff, the next time
b or any instance anywhere of
print_stuff, it will break.
Go check out the repo to see this and other footguns complete with unit tests.