Ruby Singleton Methods with (Class_Eval, Define_Method) VS (Instance_Eval, Define_Method)

Ruby singleton methods with (class_eval, define_method) vs (instance_eval, define_method)

No difference for define_method. But there is a difference when you use def.

o = Object.new  

# class_eval example
class << o; self; end.class_eval { def test1; :test1; end }
o.test1 #=> test1

# instance_eval example
class << o; self; end.instance_eval { def test2; :test2; end }
o.test2 #=> NoMethodError

Why the difference in behaviour between def and define_method ?
define_method is a method call and so operates on the self in the eval context. The self in both instance_eval and class_eval is the same - it is the receiver (the eigenclass of o).

However def behaves differently, it does not operate on the self but instead on the default define. In the case of class_eval the default definee is the same as self but for instance_eval it is instead the metaclass of self.

How do we access the test2 method defined above? test2 must be an instance method defined on the metaclass of the eigenclass of o.

It is a class method on the eigenclass of o:

class << o; test2; end #=> :test2

define_method inside instance_eval

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>

a confusing case in Ruby MetaProgramming

In addition to all other comments :

[from the Pickaxe] The method Object#instance_eval lets you set self to be some arbitrary object, evaluates the code in a block with [self], and then resets self.

And Module#define_method : Defines an instance method in the receiver [self, which must be a (anonymous) Class or Module].

singleton_class_of_object_a = aa = class << a; self; end
aa.instance_eval { def foo3; puts "foo3 from singleton class of a, self=#{self}"; end }
aa.foo3 # => foo3 from singleton class of a, self=#<Class:#<A:0x007fc2e4049e68>>
aa.instance_eval do
puts "about to define_method :bar3 in self=#{self}"
define_method :bar3 do; puts "bar3 from singleton class of a, self=#{self}"; end
end # => about to define_method :bar3 in self=#<Class:#<A:0x007fc2e4049e68>>
a.bar3 # => bar3 from singleton class of a, self=#<A:0x007fc2e4049e68>

define_method :bar3 is executed in the context of singleton_class_of_object_a (an anonymous class, see below), thus defines an instance method of that class, hence bar3 becomes a singleton method of a. As already said in my previous answer, it is equivalent to defining directly on the object :

def a.bar4; puts 'bar4 from singleton class of a' end
a.bar4 # => bar4 from singleton class of a

p a.singleton_methods.sort # => [:bar3, :bar4, :foo2]
p a.methods(false).sort # => [:bar3, :bar4, :foo2]



After a = A.new, the field class of instance a points to class A.

With class << a or def a.bar4, Ruby creates an anonymous class, the field class of instance a now points to this anonymous class, and from there to A.

Methods defined in this context with def or define_method go into the methods table of the anonymous class.

class_eval vs instance_eval

Long story short:

  • Object.instance_eval &block sets:

    • self to Object
    • The "current class" to Object.singleton_class
  • Object.class_eval &block sets:

    • self to Object
    • The "current class" to Object

The "current class" is used for def, undef and alias, as well as constant and class variable lookups.


Now, let's have a look at the implementation details.

Here's how module_eval and instance_eval are implemented in C:

VALUE rb_mod_module_eval(int argc, VALUE *argv, VALUE mod) {
return specific_eval(argc, argv, mod, mod);
}

VALUE rb_obj_instance_eval(int argc, VALUE *argv, VALUE self) {
VALUE klass;
if (SPECIAL_CONST_P(self)) { klass = Qnil; }
else { klass = rb_singleton_class(self); }
return specific_eval(argc, argv, klass, self);
}

Both call specific_eval, which takes the following arguments: int argc, VALUE *argv, VALUE klass and VALUE self.

Note that:

  • module_eval passes the Module or Class instance as both klass and self
  • instance_eval passes the object's singleton class as klass

If given a block, specific_eval will call yield_under, which takes the following arguments: VALUE under, VALUE self and VALUE values.

if (rb_block_given_p()) {
rb_check_arity(argc, 0, 0);
return yield_under(klass, self, Qundef);
}

There are two important lines in yield_under:

  1. block.self = self;

    This sets the self of the block to the receiver.

  2. cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);

    The cref is a linked list
    which specifies the "current class", which is used for def, undef and alias, as well
    as constant and class variable lookups.

    That line basically sets the cref to under.

    Finally:

    • When called from module_eval, under will be the Class or Module
      instance.

    • When called from instance_eval, under will be the singleton class of
      self.

Using send method inside define_method in class_eval block

The reciever is the current value of self

So you can do:

class MethodLogger
def log_method((klass,method_name)
klass.class_eval do
alias_method "#{method_name}_original" method_name
define_method method_name do
puts "#{Time.now}: Called #{method_name} on #{self.class}"
send "#{method_name}_original"
end
end
end
end

After a while you'll learn to keep track of self in your mind :)

Ruby's def and instance_eval vs. class_eval

I think your confusion comes from the fact that def does not depend on the current self, you might think about it as being a "current class" that has it's own rules.

Following your examples:

class A
# defs here go to A
puts self # => A
class << self
#defs here go to A's eigenclass
end
end

A.class_eval do
#defs here go to A
end

A.instance_eval do
#defs here go to A's eigenclass
end

s = "Hello World"

class << s
#defs here go to s's eigenclass
end

Here's the portion of the chapter that talks about the issue and it's pretty clear about the behaviour

class_eval and instance_eval both set
self for the duration of the block.
However, they differ in the way they
set up the environment for method
definition. class_eval sets things up
as if you were in the body of a class
definition, so method definitions will
define instance methods In contrast,
calling instance_eval on a class acts
as if you were working inside the
singleton class of self. Therefore,
any methods you define will become
class methods.

The only thing I think is worth adding is that you can call instance_eval in any object, not just classes, and the behaviour doesn't change but has different consequences.

Some relevant reading:

Ruby: instance_eval and class_eval method definitions

Chapter 4 of this most excelent series

How to understand the difference between class_eval() and instance_eval()?

As the documentation says, class_eval evaluates the string or block in the context of the Module or Class. So the following pieces of code are equivalent:

class String
def lowercase
self.downcase
end
end

String.class_eval do
def lowercase
self.downcase
end
end

In each case, the String class has been reopened and a new method defined. That method is available across all instances of the class, so:

"This Is Confusing".lowercase 
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
=> "the smiths on charlie's bus"

class_eval has a number of advantages over simply reopening the class. Firstly, you can easily call it on a variable, and it's clear what your intent is. Another advantage is that it will fail if the class doesn't exist. So the example below will fail as Array is spelt incorrectly. If the class was simply reopened, it would succeed (and a new incorrect Aray class would be defined):

Aray.class_eval do
include MyAmazingArrayExtensions
end

Finally class_eval can take a string, which can be useful if you're doing something a little more nefarious...

instance_eval on the other hand evaluates code against a single object instance:

confusing = "This Is Confusing"
confusing.instance_eval do
def lowercase
self.downcase
end
end

confusing.lowercase
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
NoMethodError: undefined method ‘lowercase’ for "The Smiths on Charlie's Bus":String

So with instance_eval, the method is only defined for that single instance of a string.

So why does instance_eval on a Class define class methods?

Just as "This Is Confusing" and "The Smiths on Charlie's Bus" are both String instances, Array, String, Hash and all other classes are themselves instances of Class. You can check this by calling #class on them:

"This Is Confusing".class
=> String

String.class
=> Class

So when we call instance_eval it does the same on a class as it would on any other object. If we use instance_eval to define a method on a class, it will define a method for just that instance of class, not all classes. We might call that method a class method, but it is just an instance method for that particular class.

use define_method to create a method with equal on console

Note that if you have an expression

x.y=z

the Method "y=" is invoked on the object x, with the argument z.

However, if you have an expression

y=z

y is interpreted as the name of a variable, and gets assigned the value z.

Since you want the a= method to be invoked, you need a receiver. While we often can leave out the receiver, if it is 'self', Ruby can't recognize here that you want to do a method invocation. Hence you have to explicitly use 'self'.

Ruby metaprogramming: define_method block not maintaining scope

When you reference the class Base::Test like this, ruby does not take the Base:: as the module context to look up constants. That is the normal behaviour and would also not work, if you would define the moethod directly.

But you could do it in this way:

module Base
Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
end


Related Topics



Leave a reply



Submit