rubyinstance-evalrefinements

Ruby refinements with instance_eval


I'd like to provide some refinements to a DSL. I'm able to get refinements working with this example:

module ArrayExtras
  refine Array do
    def speak
      puts 'array!'
    end
  end
end

module MyUniverse
  using ArrayExtras
  class Thing
    def initialize
      [1].speak
    end
  end
end
MyUniverse::Thing.new

This prints out "array!" just fine. But once I introduce instance_eval, the method can't be found:

module MyUniverse
  using ArrayExtras
  class DSL
    def initialize(&block)
      instance_eval(&block)
    end
  end
end

MyUniverse::DSL.new do
  [1].speak
end

I get a undefined methodspeak' for [1]:Array (NoMethodError)`

Is there a way to get refinements working within an instance_eval?


Solution

  • Refinements are lexically scoped. You are activating the Refinement in the wrong lexical context. You need to activate it where you are calling the refined method:

    module ArrayExtras
      refine Array do
        def speak
          puts 'array!'
        end
      end
    end
    
    module MyUniverse
      class DSL
        def initialize(&block)
          instance_eval(&block)
        end
      end
    end
    
    using ArrayExtras
    
    MyUniverse::DSL.new do
      [1].speak
    end
    # array!