Using extensions_loader to cleanly monkeypatch Ruby
One of the greatest features that Ruby has to offer is its excellent support for metaprogramming. This concept is certainly not unique to Ruby, but Ruby is unique in the sheer amount support that it has for metaprogramming. And the Ruby community has definitely adopted it. You would be hard pressed to find a major Ruby library that does not make use of metaprogramming to some extent. Whether its a custom DSL, extensions upon core Ruby classes, or dynamically created methods, metaprogramming is everywhere in Ruby code.
While it is powerful, metaprogramming can get messy. Keeping track of all your extensions, where they apply, and when they’re loaded can introduce unnecessary complexity to an application. That’s where extensions_loader comes into play.
extensions_loader is a lightweight Ruby gem that takes the complexity out of creating Ruby extensions and applying them to existing classes.
Lets try an example. Lets say that I want to add the #average
method to the Array
class. Without using extensions_loader
, I might do the following:
class Array
def average
inject { |sum, num| sum + num } / count
end
end
This would work as expected, but its not exactly clear what’s happening here. A non-Rubyist would have no idea that this code actually extends upon the core Array
class, rather than defining a new class.
Why don’t we try that with extensions_loader
?
module ArrayAverage
def average
inject { |sum, num| sum + num } / count
end
end
ExtensionsLoader.load!(Array => ArrayAverage)
Now, instead of directly reopening the Array class, we are defining a module with the clear purpose of averaging an Array. Then, with extensions_loader
, we’re cleanly providing that functionality to the Array
core class.
What if we wanted to add another method to Array, say #to_sentence
?
module ArrayAverage
def average
inject { |sum, num| sum + num } / count
end
end
module ArraySentence
def to_sentence
join(', ')
end
end
ExtensionsLoader.load!(
Array => [ArrayAverage, ArraySentence]
)
By providing an Array of Modules rather than a single Module, we can specify a number of extensions to load onto the Array
class. It’s very clear what’s happening here, and its super easy to remove specific pieces of functionality. Just remove the element!
Hopefully extensions_loader
can clean up your Ruby monkeypatching and make it easier to understand your Ruby metaprogramming code!