Use Ruby's Refinements Anywhere With an Anonymous Class

For a long time I’ve been looking for a way to use Ruby’s refinements without building a class & method setup and then instantiating an instance of the class just to use it. Today I finally found a solution.

module Moo
  refine Fixnum do
    def to_s
      "moo"
    end
  end
end

# begin/end blocks allow access to local variables
begin # valid Ruby 2.2.3 and NOT Ruby 2.3.0
  using Moo
  1.to_s
end
# => "moo"

# class syntax has no access to outer local variables
class << Class.new # valid Ruby 2.3.0
  using Moo
  1.to_s
end
# => "moo" 

The best work around I’ve found for accessing values outside the class wood be constants (preferable over global vars). If you wanted to take the extra effort your can have a constant be an instance of Binding to grab the local variables.

class Object
  # bv for binding & variable
  def bv(a_binding, local_var)
    a_binding.local_variable_get local_var
  end
end

x = 5
A = binding

class << Class.new
  using Moo
  bv(A, :x).to_s
end
# => "moo" 

I’ve put in a feature request for Ruby’s using method to take a block syntax and allow access to local variables Feature Request #12281. So this would bring back the convenience available in Ruby 2.2.3.

Example of Refinement Use in Just One Method

If you use the Object#bv method defined above you may use kind of a dynamic constant variable to grab the local variable.

class Example
  def number_to_moo(num)
    Example.const_set :B, binding
    result = class << Class.new
      using Moo
      bv(B, :num).to_s
    end
    Example.send :remove_const, :B
    result
  end
end

Example.new.number_to_moo 9
# => "moo"

At this point we’ve gone too far to bend against the design of how a refinement is used (and other Ruby design principles) and it would be simpler to just write the refinement as intended by design.

class Example2
  def number_to_moo(num)
    DoTheThing.new.the_thing(num)
  end

  class DoTheThing
    using Moo
    def the_thing(num)
      num.to_s
    end
  end
  private_constant :DoTheThing
end

Example2.new.number_to_moo 9
# => "moo" 

As you can see this is a lot of class wrapper just to refine one little thing in a method. So I really hope my feature request get’s accepted for Ruby and we can simplify the use of refinements anywhere and still stick to a strict lexical scope.