I'm going through the Facets API and picking some methods to include in my refinement-compatible patch library.
I've hit a snag trying to patch Kernel. It's a module, whereas the other stuff I've patched has been classes (String, Array, etc.)
Here's proof that can't can't be refined using my standard approach for core classes:
module Patch
refine Kernel do
def patched?
true
end
end
end
# TypeError: wrong argument type Module (expected Class)
# from (pry):16:in `refine'
I've also tried wrapping the Kernel module in a class, and changing the global reference to Kernel to that class.
class MyKernel
include Kernel
extend Kernel
end
# not sure if Object::Kernel is really the global reference
Object::Kernel = MyKernel
module Patch
refine MyKernel do
def patched?
true
end
end
end
class Test
using Patch
patched?
end
# NoMethodError: undefined method `patched?' for Test:Class
# from (pry):15:in `<class:Test>'
In this case I could successfully get the same functionality by replacing Kernel with Object:
module Patch
refine Object do
def patched?
true
end
end
end
class Test
using Patch
patched?
end
But I'm not sure if I could get this equivalency with other core modules such as Enumerable.
Modules can be refined as of ruby 2.4:
Module#refine
accepts a module as the argument now. [Feature #12534]
The old caveat ("Refinements only modify classes, not modules so the argument must be a class") no longer applies (although it wasn't removed from the documentation until ruby 2.6).
Example:
module ModuleRefinement
refine Enumerable do
def tally(&block)
block ||= ->(value) { value }
counter = Hash.new(0)
each { |value| counter[block[value]] += 1 }
counter
end
end
end
using ModuleRefinement
p 'banana'.chars.tally # => {"b"=>1, "a"=>3, "n"=>2}