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
toObject
- The "current class" to
Object.singleton_class
Object.class_eval &block
sets:self
toObject
- 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 theModule
orClass
instance as bothklass
andself
instance_eval
passes the object's singleton class asklass
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
:
block.self = self;
This sets the
self
of the block to the receiver.cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);
The
cref
is a linked list
which specifies the "current class", which is used fordef
,undef
andalias
, as well
as constant and class variable lookups.That line basically sets the
cref
tounder
.Finally:
When called from
module_eval
,under
will be theClass
orModule
instance.When called from
instance_eval
,under
will be the singleton class ofself
.
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
How to Check If a Number Is Included in a Range (In One Statement)
How to Update Gems on Production Server
How to Specify Post Params in a Rails Test
Object.Valid? Returns False But Object.Errors.Full_Messages Is Empty
How Does Rails Implement Before_Filter
Gem File with Git Remote Failing on Heroku Push
Sort an Array of Strings by Their Integer Values
Error Installing Any Ruby Version with Rvm on Osx
Best Practice About Empty Belongs_To Association
Ruby Methods That Either Yield or Return Enumerator
Redirect the "Puts" Command Output to a Log File
Cucumber Not Showing Coloured Output in Windows