Search code examples
rubyinstance-eval

define_method inside instance_eval


When I define a method inside instance_eval block for class it creates a class method which is fine.

Eg)

class A
end

A.instance_eval do
  def method; end
end

A.method #works

But when I use define_method inside instance_eval it creates instance method instead of class method Eg)

A.instance_eval do
  define_method(:method1) {}
end
A.method1 # NoMethodError: undefined method `method1'
A.new.method1 # Works fine

I am unable to understand above phenomena. Please can somebody help me out.


Solution

  • This quirky behavior makes a little more sense if you look at instance_eval in the context of instances (which is its main purpose).

    class A
    end
    
    a = A.new
    a.instance_eval do
      def foo
      end
    end
    

    Where is foo defined? The only sensible place I can think of is a's singleton class, and indeed that is true

    a.method(:foo).owner == a.singleton_class
    # true
    

    So this demonstrates the rule

    def inside instance_eval defines a method in self's singleton class.

    which is completely consistent with what you saw.

    A.instance_eval do
      # defines method in A's singleton class!
      def method; end
    end
    

    So why does define_method behave differently? Because unlike def it's a method! So this

    A.instance_eval do
      define_method(:foo) {}
    end
    

    is really just

    A.define_method(:foo) {}
    

    which is the metaprogramming way of creating normal instance methods. This inconsistency may seem annoying, but again look at the case for normal instances and you'll see why def and define_method can't be consistent. This

    a.instance_eval do
      define_method(:foo) {}
    end
    

    is really just

    a.define_method(:foo) {}
    

    which is nonsense

    NoMethodError: undefined method `define_method' for #<A:0x00008>