How often do you find yourself checking to see whether an object in array? Or a string or an integer? And when you do, you’re using is_a? or kind_of?, right?
I ran into a little project on Github called Simple. Simple is a Ruby script that provides some basic functionality developers may find useful. In this script I saw that the author had defined three methods:
1 2 3 4 5 6 7 8 9 10 11
def is_array(simple_data_type) return simple_data_type.kind_of?(Array) end def is_int(simple_data_type) return simple_data_type.kind_of?(Integer) end def is_string(simple_data_type) return simple_data_type.kind_of?(String) end
Not a big deal, but using them is clunky:
if is_string("Hello world") # Do something end
My first reaction was just to apply some basic Rubyism and submit it as a pull request. Basically, I would’ve just dropped the returns and appended a question mark to the method names. But I realized very quickly that this could be a great opportunity to employ some metaprogramming.
Enter the code above.
What I’ve done is I’ve overwritten the method_missing method of Object. This way I catch any and all method calls that shouldn’t exist (as our new methods shouldn’t). And I check to see if it’s a method we’re looking for (is_array?, is_string? or is_int?).
If the method lines up then we perform a tap. I’ve opted to use tap so that I can more easily capture the caller of the method. Tap will, by its nature, return the caller. This isn’t what we want, so I toggle a boolean variable to match the data type check and return that at the end of the method.
Notice lines 35 and 36. They may be the most important. It’s imperative that our code not have side effects outside of its own scope. If we did not include lines 35 and 36 (which are just saying “if it’s not a method we want to capture then do your normal routine) then errors would be suppressed when they really shouldn’t be.
Metaprogramming is great in what tools it provides for us. But always remember to be responsible!