April 21, 2016 by Daniel P. Clark

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. And now I’ve 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 of local variable access 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.

Antipattern
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"

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

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

Summary

Refinements are great but they still have a way to go (see: Ruby Refinements – Not quite ready for production).  At the time of this writing the Ruby core team seams really focused on just patch/bug related updates and I haven’t seen them accept a new feature since before the release of 2.3.0.  But once there next release version is out I’m hopeful for newer and improved features for the language 🙂 .

I’ve been into programming in the Rust language a lot lately and there use of implementations on types works in the same manner as Ruby’s refinements without all the extra hassle.  In Rust where ever you write use my_crate::MyTrait; it adds the functionality to a specific object type just like Ruby’s refinements and it’s available within whatever scope you put it.  I’m hoping Ruby heads more in this direction.  I’m sure I’ll be writing about Rust soon so look forward to it!

As always I hope you found this educational and enjoyable!  Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter@6ftdan!

God Bless!
-Daniel P. Clark

Image by solarisgirl via the Creative Commons Attribution-ShareAlike 2.0 Generic License.

#anonymous#block#class#convenient#refinement#ruby#scope#short

Leave a Reply

Your email address will not be published / Required fields are marked *