[RUBY] I played with Refinements

Introduction

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

What is Refinements

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.

What I wanted to do

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.

I tried it

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

What you are doing

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 ...

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 ...?

in conclusion

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.

reference

How to use Ruby refinements

[Ruby Advent Calendar 2018] The world of Refinements you don't know [Day 3]

Dynamic definition of refinement in Ruby

Recommended Posts

I played with Refinements
I played loosely with Jshell
I implemented Ruby with Ruby (and C) (I played with builtin)
I tried DI with Ruby
I tried UPSERT with PostgreSQL.
I tried BIND with Docker
I tried using JOOQ with Gradle
I tried morphological analysis with MeCab
I made a GUI with Swing
I tried to interact with Java
I tried UDP communication with Java
What I learned with Java Gold
I tried GraphQL with Spring Boot
[Introduction to JSP + Servlet] I played with it for a while ♬
I tried Flyway with Spring Boot
What I learned with Java Silver
I tried customizing slim with Scaffold
[I get] Until deploying with SAM CLI
I just wanted to logrotate with log4j 2
I tried to get started with WebAssembly
I tried using Scalar DL with Docker
I want to use DBViewer with Eclipse 2018-12! !!
I made an eco server with scala
I tried using OnlineConverter with SpringBoot + JODConverter
I tried time-saving management learning with Studyplus.
I tried playing with BottomNavigationView a little ①
I made a risky die with Ruby
I tried using OpenCV with Java + Tomcat
I tried Lazy Initialization with Spring Boot 2.2.0
I made a rock-paper-scissors app with kotlin
I built Step Functions with AWS CDK.
I made a rock-paper-scissors app with android
I tried to implement ModanShogi with Kinx