Is there a way to create methods just for the instance of a Ruby class from inside that instance?

Ruby

Ruby Problem Overview


Let there be class Example defined as:

class Example
  def initialize(test='hey')
    self.class.send(:define_method, :say_hello, lambda { test })
  end
end

On calling Example.new; Example.new I get a warning: method redefined; discarding old say_hello. This, I conclude, must be because it defines a method in the actual class (which makes sense, from the syntax). And that, of course, would prove disastrous should there be multiple instances of Example with different values in their methods.

Is there a way to create methods just for the instance of a class from inside that instance?

Ruby Solutions


Solution 1 - Ruby

You need to grab a reference to the instance's singleton class, the class that holds all the instance specific stuff, and define the method on it. In ruby 1.8, it looks a little messy. (if you find a cleaner solution let me know!)

Ruby 1.8

class Example
  def initialize(test='hey')
    singleton = class << self; self end
    singleton.send :define_method, :say_hello, lambda { test }
  end
end

Ruby 1.9 however, provides a much easier way in.

Ruby 1.9

class Example
  def initialize(test='hey')
    define_singleton_method :say_hello, lambda { test }
  end
end

Solution 2 - Ruby

First off, a small style tip:

self.class.send(:define_method, :say_hello, lambda { test })

You can make this look a little bit nicer by using the new proc literal in Ruby 1.9:

self.class.send(:define_method, :say_hello, -> { test })

But you don't need that. Ruby has something called blocks, which are basically a piece of code that you can pass as an argument to a method. In fact, you already used blocks, since lambda is just a method which takes a block as an argument and returns a Proc. However, define_method already takes a block anyway, there is no need to pass a block to lambda which converts it to a Proc which it passes to define_method which then converts it back into a block:

self.class.send(:define_method, :say_hello) { test }

As you already noticed, you are defining the method on the wrong class. You are defining it on the Example class, since inside an instance method like initialize, self is the current object (i.e. ex1 or ex2 in @mikej's example), which means that self.class is ex1's class, which is Example. So, you are overwriting the same method over and over again.

This leads to the following unwanted behavior:

ex1 = Example.new('ex1')
ex2 = Example.new('ex2') # warning: method redefined; discarding old say_hello
ex1.say_hello            # => ex2 # Huh?!?

Instead, if you want a singleton method, you need to define it on the singleton class:

(class << self; self end).send(:define_method, :say_hello) { test }

This works as intended:

ex1 = Example.new('ex1')
ex2 = Example.new('ex2')
ex1.say_hello            # => ex1
ex2.say_hello            # => ex2

In Ruby 1.9, there's a method that does that:

define_singleton_method(:say_hello) { test }

Now, this works the way you want it to, but there's a higher-level problem here: this is not Ruby code. It is Ruby syntax, but it's not Ruby code, it's Scheme.

Now, Scheme is a brilliant language and writing Scheme code in Ruby syntax is certainly not a bad thing to do. It beats the hell out of writing Java or PHP code in Ruby syntax, or, as was the case in a StackOverflow question yesterday, Fortran-57 code in Ruby syntax. But it's not as good as writing Ruby code in Ruby syntax.

Scheme is a functional language. Functional languages use functions (more precisely, function closures) for encapsulation and state. But Ruby is not a functional language, it is an object-oriented language and OO languages use objects for encapsulation and state.

So, function closures become objects and captured variables become instance variables.

We can also come at this from a completely different angle: what you are doing is that you are defining a singleton method, which is a method whose purpose it is to define behavior which is specific to one object. But you are defining that singleton method for every instance of the class, and you are defining the same singleton method for every instance of the class. We already have a mechanism for defining behavior for every instance of a class: instance methods.

Both of these arguments come from completely opposite directions, but they arrive at the same destination:

class Example
  def initialize(test='hey')
    @test = test
  end

  def say_hello
    @test
  end
end

Solution 3 - Ruby

I know it was asked two years back, but I would like to add one more answer. .instance_eval will help to add methods to instance object

    string = "String"
    string.instance_eval do
      def new_method
        self.reverse
      end
    end

Solution 4 - Ruby

Define instance method from outside:

example = Example.new
def example.say_hello
  puts 'hello'
end

From inside:

class Example
  def initialize(word='hey')
    @word = word
    def self.say_hello
      puts "example: #{@word}"
    end
  end
end

Tested on ruby 2.5

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAaron YodaikenView Question on Stackoverflow
Solution 1 - RubythorncpView Answer on Stackoverflow
Solution 2 - RubyJörg W MittagView Answer on Stackoverflow
Solution 3 - RubySelvamaniView Answer on Stackoverflow
Solution 4 - RubyDaniel GarmoshkaView Answer on Stackoverflow