This is an article I played with Refinements. As a concrete content, it is a memorandum when I tried to use Refinements at the top level.
Reprinted from Blog
In Ruby, you can freely add methods to the class later.
class Integer
def hoge
puts :hoge
end
end
42.hoge
# => :hoge
This is convenient, but it's not very good because it changes globally and can cause unintended behavior such as inheritance.
That's where Refinements come in.
module Refine
refine Integer do
def hoge
p :hoge
end
end
end
# 42.hoge
# => Error!
using Refine
42.hoge
# => :hoge
In Refinements, you can use the ones redefined after using
.
Also, since it is limited to the file scope of the using
part, the range of influence can be small.
I wanted to use refine
at the top level to create a method in a specific class as shown below (because it was troublesome to write using
every time ...)
refine Integer do
def hoge
puts :hoge
end
end
42.hoge
However, this code causes an error because the method called refine
is not defined.
So, when I tried to see if it could be done, it worked with the following code.
module Kernel
module_function
def refine klass, &block
Module.new do
refine klass do
yield
end
end
end
end
refine Integer do
public
def hoge
p :hoge
end
end
42.hoge
# => :hoge
I think people who use Refinements often use anonymous modules with the following code.
using Module.new {
refine Integer do
def hoge
p :hoge
end
end
}
42.hoge
# => :hoge
Based on this code, create a refine
method in the Kernel
module.
module Kernel
module_function
def refine klass, &block
Module.new do
refine klass do
yield
end
end
end
end
The refine
method receives the class etc. that you want to redefine as the first argument, and passes the method redefinition etc. as a block argument.
This allows you to add the hoge
method as follows:
refine Integer do
public
def hoge
puts :hoge
end
end
42.hoge
# => :hoge
However, this code can use the redefined contents even though it is not using
...
module Kernel
module_function
def refine klass, &block
Module.new do
refine klass do
yield
end
end
end
end
#I don't use using here, but I can use the hoge method I created ...
refine Integer do
public
def hoge
p :hoge
end
end
42.hoge
# => :hoge
I wonder why it works because it is not clear whether this is the movement according to the specifications of Refinements.
And in this code, the method wasn't called without adding public
.
module Kernel
module_function
def refine klass, &block
Module.new do
refine klass do
yield
end
end
end
end
refine Integer do
def hoge
p :hoge
end
end
42.hoge
# => private method `hoge' called for 42:Integer (NoMethodError)
From the error, I found that it was defined as a private
method, so I added public
and it worked ...
I wonder if this area also moves according to the specifications ...?
For the time being, I was able to do what I wanted to do. I'm not sure if this works as specified, so I'll read about the implementation of Refinements.
[Ruby Advent Calendar 2018] The world of Refinements you don't know [Day 3]
Dynamic definition of refinement in Ruby
Recommended Posts