Piotr Kruczek Piotr Kruczek - 27 days ago 10
Ruby Question

Safely extending functionality of a standard library class

Is there a simple way to extend a functionality of a standard library class like

String
inside a module without affecting anything outside of it? Example (won't work):

module Foo
class String
def hello
"hello #{self}!"
end
end

class Bar
def greet name
name.hello
end
end
end


Result I'm looking for:

Foo::Bar.new.greet 'Tom' #=> hello Tom!
'Tom'.hello #=> NoMethodError


I'm aware of solutions like creating
MyString < String
with desired functionality, but I would rather not call
MyString.new('foo')
every time I want to use a string inside the module.

I realise this may not be considered good practice, I'm just looking to expand my understanding of the language.

Answer

What you're looking for is Refinement:

Refinements are designed to reduce the impact of monkey patching on other users of the monkey-patched class. Refinements provide a way to extend a class locally.

module Foo
  refine String do
    def hello
      "hello #{self}!"
    end
  end
end

puts 'Tom'.hello
#=> undefined method `hello' for "Tom":String
using Foo
puts 'Tom'.hello
#=> hello Tom!

Using refinement inside Bar class:

# Without refinement
class Bar
  def greet(name)
    name.hello
  end
end

puts Bar.new.greet('Tom')
#=> undefined method `hello' for "Tom":String

# With refinement
class Bar
  using Foo
  def greet(name)
    name.hello
  end
end

puts Bar.new.greet('Tom')
#=> hello Tom!

For the complete info about scoping, method lookup and stuff look into docs link I've provided :)

P.S. Be aware, that Rubinius developers are philosophically opposed to Refinements and thus will never implement them (c) Jörg W Mittag.

Comments