Is 'yield self' the same as instance_eval?
Your two pieces of code do very different things. By using instance_eval you're evaluating the block in the context of your object. This means that using def will define methods on that object. It also means that calling a method without a receiver inside the block will call it on your object.
When yielding self you're passing self as an argument to the block, but since your block doesn't take any arguments, it is simply ignored. So in this case yielding self does the same thing as yielding nothing. The def
here behaves exactly like a def
outside the block would, yielding self does not actually change what you define the method on. What you could do is:
class Foo
def initialize
yield self if block_given?
end
end
x = Foo.new {|obj| def obj.foo() 'foo' end}
x.foo
The difference to instance_eval being that you have to specify the receiver explicitly.
Edit to clarify:
In the version with yield, obj in the block will be the object that is yielded, which in this case is is the newly created Foo instance. While self will have the same value it had outside the block. With the instance_eval version self
inside the block will be the newly created Foo instance.
Difference between instance_eval and class self?
class << obj
setsself
to whateverobj
is in the given block, which in my case is the instance ofClass
that isExample
(this is where I'm probably wrong);
No, class << obj
opens up the singleton class of obj
. As you correctly pointed out, inside of a class declaration, self
refers to the class itself, so, in this case, the "inner" self
(i.e. the one being passed to puts
) refers to the singleton class of Example
.
`instance_eval` and scopes
Your scope
method is basically a no-op. When you pass a block to a method that yields, the block is evaluated in the current scope. Observe:
class A
def self.scope
yield
end
end
A.scope { p self }
# main
Since nothing is yielded to the block, and nothing is done with the return value of yield
, any code run in the block will have the same effect run outside the scope
block.
This isn't the case with instance_eval
, however. When instance_eval
runs a block, self
in the block is set to the receiver (rather than whatever self
is in the block's scope). Like this:
class A
end
A.instance_eval { p self }
# A
But note that this means that self.instance_eval { ... }
is also a fancy no-op, because you're changing the block's self
to the same self
outside the block.
So your code is equivalent to this:
class A
def self.method_added method
# do something involving the added method
end
end
class B < A
def foo
end
end
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
.
Instance Eval Within Block
class Builder
def initialize
@lines = []
end
def lines(&block)
block_given? ? instance_eval(&block) : @lines
end
def add_line( text )
@lines << text
end
end
my_builder = Builder.new
my_builder.lines {
add_line "foo"
add_line "bar"
}
p my_builder.lines # => ["foo", "bar"]
Ruby Block Scope with instance_eval
When you define @value
in the lexical scope (your main source file), you're defining an instance variable in the global interpreter. For example:
self #=> main
# "self" here refers to the main interpreter, which is of class Object
self.instance_variable_get(:@value) #=> 1
# "example" is your instance above
example.instance_variable_get(:@value) #=> "a"
# "self" has been changed to refer to "example" using instance_eval
example.instance_eval { self.instance_variable_get(:@value) } #=> "a"
# this syntax is just a shortcut for the line above
example.instance_eval { @value } #=> "a"
With instance_eval
, all you're doing is replacing the main interpreter self
with the object you've called instance_eval
on.
How to change self in a block like instance_eval method do?
You can write a method that accepts a proc argument, and then pass that as a proc argument to instance_eval.
class Foo
def bar(&b)
# Do something here first.
instance_eval &b
# Do something else here afterward, call it again, etc.
end
end
Foo.new.bar { puts self }
Yields
#<Foo:0x100329f00>
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.
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.
Related Topics
Purpose of & (Ampersand) in Ruby for Procs and Calling Methods
Ruby Koans: Explicit Scoping on a Class Definition Part 2
Rails 4.0.1 on Heroku, Can't Create Database
Getting Ruby 1.8.7 Installed on Mountain Lion (10.8)
Should I Test Private Methods Using Rspec
Openid Support for Ruby on Rails Application
How to List Included Modules in a Ruby Class
Find File Path of a Given Class
How to Use a Rack Middleware Only for Certain Paths
Can You Use Semicolons in Ruby
How to Get All Class Names in a Namespace in Ruby
Is There an Add_Days in Ruby Datetime
How to Reference Global Variables and Class Variables
Passing Parameters to Erb View