Accessing Ruby Class Variables with Class_Eval and Instance_Eval

Accessing Ruby Class Variables with class_eval and instance_eval

I just asked the same question to Matz during the RubyKaigi party. I was half-drunk, but he was perfectly sober, so you can take this as the definitive answer.

Anton is right - the reason why you cannot access class variables through instance_eval() is "just because". Even class_eval() shares the same issue (Matz himself wasn't totally sure about class_eval() until I told him I'd already tried it). More specifically: scope-wise, class variables are more like constants than instance variables, so switching self (as instance_eval() and class_eval() do) is not going to make any difference when it comes to accessing them.

In general, it might be a good idea to avoid class variables altogether.

Why is `instance_eval`/`class_eval` not able to access class variables?

The error thrown is because MySelf.instance_eval('@@name') correctly throws an error. This is not an instance variable, it's a class variable. You'll want to have MySelf.class_eval('@@name') on it's own, and then it'll work.

Check the repl here: https://repl.it/Be0U/0

To set the class variable, use class_variable_set like so:

 MySelf.class_variable_set('@@name', 'graham')

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.

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.

Ruby instance_eval on a class with attr_accessor

At first, your understanding (or intuition) is correct, methods defined inside #instance_eval and #class_eval are not the same

A = Class.new

A.instance_eval { def defined_in_instance_eval; :instance_eval; end }
A.class_eval { def defined_in_class_eval; :class_eval; end }

A.new.defined_in_class_eval # => :class_eval
A.defined_in_instance_eval # => :instance_eval

a side note: while self is the same in both instance_eval and class_eval, the default definee is different, see http://yugui.jp/articles/846

What really does the trick is Module#attr_accessor itself, look at its definition:
http://rxr.whitequark.org/mri/source/vm_method.c#620

it does not use def, it does not read context, self or a default definee. It just "manually" inserts methods into a module. That's why the result is counterintuitive.

Why does instance_eval() define a class method when called on a class?

x.instance_eval changes your context so self evaluates to x.

This allows you to do many things, including defining instance variables and instance methods but only for x.

 x = Object.new
y = Object.new

# define instance variables for x and y
x.instance_eval { @var = 1 }
y.instance_eval { @var = 2 }

# define an instance method for all Objects
class Object
def var
@var
end
end

x.var #=> 1
y.var #=> 2

Ruby lets you define instance methods for an object in a couple places. Normally,
one defines them in a class, and those instance methods are shared among all instances
of that class (like def var above).

However, we can also define an instance method for just a single object:

# here's one way to do it
def x.foo
"foo!"
end
# here's another
x.instance_eval do
# remember, in here self is x, so bar is attached to x.
def bar
"bar!"
end
end

Even though x and y have the same class, they don't share these methods, since they were only defined for x.

x.foo #=> "foo!"
x.bar #=> "bar!"
y.foo #=> raises NoMethodError
y.bar #=> raises NoMethodError

Now in ruby, everything's an object, even classes. Class methods are just instance methods
for that class object.

# we have two ways of creating a class:
class A
end
# the former is just syntatic sugar for the latter
B = Class.new

# we have do ways of defining class methods:

# the first two are the same as for any other object
def A.baz
"baz!"
end
A.instance_eval do
def frog
"frog!"
end
end

# the others are in the class context, which is slightly different
class A
def self.marco
"polo!"
end
# since A == self in here, this is the same as the last one.
def A.red_light
"green light!"
end

# unlike instance_eval, class context is special in that methods that
# aren't attached to a specific object are taken as instance methods for instances
# of the class
def example
"I'm an instance of A, not A itself"
end
end
# class_eval opens up the class context in the same way
A.class_eval do
def self.telegram
"not a land shark"
end
end

Note again, that all these methods are A-specific, B doesn't get access to any of them:

A.baz #=> "baz!"
B.telegram #=> raises NoMethodError

The important thing to take away from here is that
class methods are just instance methods of an object of class Class

Why might you call instance_eval (as opposed to class_eval) inside 'initialize'?

First of all, you can't do something like this:

class Observer
def initialize(&block)
class_eval(&block) if block_given?
end
end

Because class_eval isn't defined for an instance of Observer. It is defined in Module (which Class descends from). We'll come back to class_eval later.

The reason to use the above idiom is often to allow block initialization:

x = Observer.new do
add_event(foo)
some_other_instance_method_on_observer
self.some_attribute = something
end

Plus, you can add methods to a given instance of the class:

foo = Observer.new do
def foo
'foo'
end
end

foo.foo # => "foo"

You can accomplish roughly the same thing without instance_eval:

class Foo
def initialize
yield self if block_given?
end
end

foo = Foo.new do |x|
x.add_event(foo)
x.some_other_instance_method_on_observer
x.self.some_attribute = something
end

But that doesn't give you the ability to add methods. If you were to do this:

foo = Foo.new do
def foo
'foo'
end
end

foo.foo # => "foo"

It seems to work, right? But what you've actually done is to add the foo method to everything, because self is set to the "main" object. It's equivalent to simply defining the method outside of the block. They get added as instance methods to Object, so they work on everything.

Now, as promised, a brief return to class_eval. You could do something like this:

class Observer
def initialize(&block)
class.class_eval(&block) if block_given?
end
end

But then you open up the entire class:

x = Observer.new { def foo; 'foo'; end }
x.foo # => "foo"
y = Observer.new
y.foo # => "foo"

This isn't typically what we want to do. Plus, self will be the class, not the instance. This makes it useless for the block initialization as demonstrated above.

Dose self.class.class_eval equal to instance_eval?

By class_eval you modify the class, by instance_eval the current instance only. Look:

▶ class A
▷ def ceval
▷ self.class.class_eval "def on_class ; puts 'On Class' ; end"
▷ end
▷ def ieval
▷ self.instance_eval "def on_instance ; puts 'On Instance' ; end"
▷ end
▷ end
▶ a1 = A.new
#⇒ #<A:0xcf6a87c>
▶ a1.ceval
#⇒ :on_class
▶ a1.ieval
#⇒ :on_instance
▶ a1.on_class
#⇒ On Class
▶ a1.on_instance
#⇒ On Instance

▶ a2 = A.new
#⇒ #<A:0xd0e9f7c>
▶ a2.on_class
#⇒ On Class !!! IT IS DEFINED ON NEWLY CREATED INSTANCE OF A
▶ a2.on_instance
#⇒ NoMethodError: undefined method `on_instance' for #<A:0xd0e9f7c>

The latter fails because we declare on_instance method on instance a1, and a2 knows nothing about it.

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



Related Topics



Leave a reply



Submit