Piotr Kruczek
Piotr Kruczek

Reputation: 2390

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.

Upvotes: 2

Views: 617

Answers (2)

Julien Lamarche
Julien Lamarche

Reputation: 1050

Write a class of the same name then use require_relative (or similar method) to include it.

class Date
    def itis
        puts "It is " + self.to_s
    end

Example:

[1] pry(main)> require_relative 'date.rb'
=> true
[2] pry(main)> require 'date'
=> true
[3] pry(main)> Date.new(2018, 10, 9).itis
It is 2018-10-09
=> nil

Upvotes: 0

Andrey Deineko
Andrey Deineko

Reputation: 52357

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.

Upvotes: 3

Related Questions