Foo = Class.new
Foo.instance_eval do
def instance_bar
"instance_bar"
end
end
puts Foo.instance_bar #=> "instance_bar"
puts Foo.new.instance_bar #=> undefined method ‘instance_bar’
My understanding is that calling instance_eval on an object is supposed to allow you to define an instance variable or method for that object.
But in the example above, when you call it on class Foo to define the instance_bar method, instance_bar becomes a class method that can be invoked with "Foo.instance_bar". It is clear that this code has not created an instance method because Foo.new.instance_bar results in "undefined method ‘instance_bar’".
Why does instance_eval define a class method rather than an instance method in this context?
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
The purpose of 'instance_eval' is to extend objects, but the purpose of 'class_eval' is to extend classes. And because classes are also objects, you can apply instance_eval on classes.
I guess that extending of classes is just more understandable in classic OOP. Dynamic languages allow us to easily specify behaviour of particular objects. Fact that each object can have own behaviour adds a lot of flexibility designing an application. Not only data can vary for objects of the same class. Two humans differ not only because they were born in different years, not only because they have different parents but they can think different and thus behave different.
Ability to change the behaviour of each object is fundamental. It exists in many languages.
Thinking about instance_eval think about objects first. Then you'll realize that classes are also objects - objects which additional purpose to create new objects, to hold description of common behaviour (methods). You can not only use definition of a class, but can also assign class to a variable, pass class as an argument, call method on a class, program a class!
I would recommend articles written by Yehuda Katz and Yugui to dive deeper into it:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With