redefining a single ruby method on a single instance with a lambda

26,416

Solution 1

def define_singleton_method_by_proc(obj, name, block)
  metaclass = class << obj; self; end
  metaclass.send(:define_method, name, block)
end
p = proc { "foobar!" }
define_singleton_method_by_proc(y, :bar, p)

or, if you want to monkey-patch Object to make it easy

class Object
  # note that this method is already defined in Ruby 1.9
  def define_singleton_method(name, callable = nil, &block)
    block ||= callable
    metaclass = class << self; self; end
    metaclass.send(:define_method, name, block)
  end
end

p = proc { "foobar!" }
y.define_singleton_method(:bar, p)
#or
y.define_singleton_method(:bar) do
   "foobar!"
end

or, if you want to define your proc inline, this may be more readable

class << y
  define_method(:bar, proc { "foobar!" })
end

or,

class << y
  define_method(:bar) { "foobar!" }
end

this is the most readable, but probably doesn't fit your needs

def y.bar
  "goodbye"
end

This question is highly related

Solution 2

I'm not sure what version of Ruby this was added in (at least 1.8.7), but there seems to be an even simpler way of doing this:

str1 = "Hello"
str2 = "Goodbye"
def str1.to_spanish
  "Hola"
end
puts str1 # => Hello
puts str1.to_spanish # => Hola
puts str2 # => Goodbye
puts str2.to_spanish # => Throws a NoMethodError

Learnt about this whilst reading the Ruby Koans (about_class_methods.rb lesson). I'm still not entirely sure what the purpose of this is since it seems a bit dangerous to me.

Solution 3

You can use the syntax class <<object to get an object's "singleton class" (that's a special parent class belonging only to that object) and define methods only for that instance. For example:

str1 = "Hello"
str2 = "Foo"

class <<str1
  def to_spanish
    'Hola'
  end
end

Now if you do str1.to_spanish, it will return "Hola", but str2.to_spanish will give you a NoMethodFound exception.

Share:
26,416
kmorris511
Author by

kmorris511

Updated on July 09, 2022

Comments

  • kmorris511
    kmorris511 almost 2 years

    In Ruby, is there a way to redefine a method of a particular instance of a class using a proc? For example:

    class Foo
      def bar()
        return "hello"
      end
    end
    
    x = Foo.new
    y = Foo.new
    

    (Something like):

    y.method(:bar) = lambda { return "goodbye" }
    
    x.bar
    y.bar
    

    Producing:

    hello
    goodbye
    

    Thanks.

  • John Douthat
    John Douthat about 15 years
    if he has an externally defined proc in a simple variable, it's difficult to get it into the new scope created by class<<str1, without resorting to global or instance variables
  • Cyril Duchon-Doris
    Cyril Duchon-Doris about 9 years
    Thank you for posting this (more) up-to-date answer !
  • Daniel A. R. Werner
    Daniel A. R. Werner almost 8 years
    Assuming str.to_spanish was already defined and I wanted to “redefine” it, how would I call the “original” str.to_spanish from within the newly defined one?
  • BRPocock
    BRPocock almost 8 years
    If str is your instance object and String is your class, you can call the class's version of the method by String.to_spanish
  • Benjineer
    Benjineer over 5 years
    God I love Ruby
  • Abdul Rahman K
    Abdul Rahman K about 4 years
    wondering why 'probably doesn't fit your needs' for the last solution?
  • Abdul Rahman K
    Abdul Rahman K about 4 years
    Both: ``` class Foo end obj = Foo.new class << obj def method1; end end def obj.method2; end
  • John Douthat
    John Douthat about 4 years
    @AbdulRahmanK because he asked how to do it with a lambda, and that way doesn't use a lambda, but I wanted to include it anyway