4

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?

Andy
  • 285
  • 3
  • 12

3 Answers3

1

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!
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • So there is no way to make `MyUniverse::DSL.new` method call self-contained? I.e. to not require users of this method to add `using ...` line to their code. – EugZol Nov 22 '19 at 14:13
1

In some cases you can achieve it by using ArrayExtras on the binding.

module MyUniverse
  using ArrayExtras
  class DSL
    def initialize(&block)
      block.binding.eval("using ArrayExtras")
      instance_eval(&block)
    end
  end
end

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

This will however only work if you are not using your class in an instance, it only works if the binding is a module or class context, otherwise the eval will fail because main.using is permitted only at toplevel .

Roland Studer
  • 4,315
  • 3
  • 27
  • 43
-1

If you want to refine the ruby core BaseObject, you need to modify it as below.

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

It will be found in top level class.

sawa
  • 165,429
  • 45
  • 277
  • 381
Auli
  • 74
  • 7