Ruby refinements have a second good use case.
According to Bradley Refinements have one good use case: Conversion Wrappers. I humbly present the second one.
Monkey patching in Ruby is a simple as:
class String
def red
"\e[#31m#{self}\e[0m"
end
end
But what if you don’t to pollute the String
-class for users of your Gem? In the worst case you might actually overwrite a monkey patch provided by the developer. We faced this when we wanted to provide coloring of strings for our logger in StimulusReflex. We wanted our users to be able to write a configuration proc like this:
config.logging = proc { "#{timestamp.cyan} #{reflex_id.red}" }
Enter refine
With refinements we were able to monkey patch only where explicitly called for it:
module Colorize
refine String do
def red
"\e[#31m#{self}\e[0m"
end
end
end
Now you can activate this monkey patch in whatever class you need it:
class Logger
using Colorize
def error
"Ooops".red
end
end
A complication
Procs are a convenient solution, however they get the binding/context of where they are defined, not of where they are called. Just calling the proc in our Logger
class will not make use of the colorize
-refinement, actually no method of the Logger
class is available to the proc, because it was defined in a completely different context (in an initializer of the gem user).
So we made use of instance_eval
class Logger
using Colorize
def log
puts instance_eval(&config.logging)
end
end
See the commit for adding colorize and using instance_eval
Originally posted on dev.to