Add method to an instanced object

In ruby 1.9+, there's a better way of doing this using define_singleton_method, as follows:

obj = SomeObject.new

obj.define_singleton_method(:new_method) do
  "do some things"
end

There are several ways to achieve this, and they are all related to the singleton class:

  1. You can use class << idiom to open the singleton class definition:

     obj = Object.new
     class << obj
       def my_new_method
          ...
       end
     end
    
  2. Or you can use define_singleton_method on the obj:

     obj = Object.new
     obj.define_singleton_method(:my_new_method) do
          ...
     end
    
  3. You can also use define_method from the singleton class:

     obj = Object.new
     obj.singleton_class.define_method(:my_new_method) do
          ...
     end
    
  4. Or you can use def directly:

     obj = Object.new
     def obj.my_new_method
          ...
     end
    

Pay attention to example 3, I think the concept of a singleton class becomes clearer on that one. There is a difference between these two examples:

    a = Object.new
    b = Object.new
    
    # -- defining a new method in the object's "class" --
    a.class.define_method(:abc) do
      puts "hello abc"
    end
    
    a.abc # prints "hello abc"
    b.abc # also prints "hello abc"

    # -- defining a new method in the object's "singleton class" --
    a.singleton_class.define_method(:bcd) do
      puts "hello bcd"
    end
    
    a.bcd # prints "hello bcd"
    b.bcd # error undefined method

This is because every object has its own singleton class:

    a = Object.new
    b = Object.new

    p a.class # prints "Object"
    p a.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84438>>"

    p b.class # also prints "Object"
    p b.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84410>>" (a different reference address)

Just an interesting point to note:

if you had instead gone:

def my_method
    def my_other_method; end
end

Then my_other_method would actually be defined on the CLASS of the object not withstanding that the receiver ofmy_method is an instance.

However if you go (as you did):

def my_method
    def self.my_other_method; end
end

Then my_other_method is defined on the eigenclass of the instance.

Not directly relevant to your question but kind of interesting nonetheless ;)


Use a Mixin.

module AdditionalMethods
  def new_method
    "do some things"
  end
end

obj = SomeObject.new
obj.extend(AdditionalMethods)

puts obj.new_method
> "do some things"

Tags:

Ruby