Skip to content

Instantly share code, notes, and snippets.

@richardking
Created August 24, 2013 02:53
Show Gist options
  • Select an option

  • Save richardking/6325780 to your computer and use it in GitHub Desktop.

Select an option

Save richardking/6325780 to your computer and use it in GitHub Desktop.
instance_eval, class_eval
Ruby: instance_eval and class_eval method definitions
In yesterday's entry on Cleaning up Controller Tests you can find the following code.
# example 1
...
test_class.class_eval do
define_method :setup do
...
end
end
test_class.class_eval &block
...
An earlier version of this code only used one call to the class_eval method.
# example 2
...
test_class.class_eval do
define_method :setup do
...
end
instance_eval &block
end
...
Example 2 works when your controller tests contain only test methods.
# example 3
controller_tests do
test "should add the params" do
assert_equal 4, Math.add(2,2)
end
test "should multiply the params" do
assert_equal 9, Math.multiply(3,3)
end
end
However, Example 2 stops working if your test class contains any helper methods.
# example 4
controller_tests do
test "should not save if first name is nil" do
person = valid_person
person.first_name = nil
assert_equal false, person.save
end
def valid_person
Person.new(...)
end
end
So, what's the difference between example 1 and example 2? The context in which the block is evaluated.
The above examples are from actual code, but the essence of the issue can be captured in a much simpler example.
# example 5
Foo = Class.new
Foo.class_eval do
def bar
"bar"
end
end
Foo.instance_eval do
def baz
"baz"
end
end
Foo.bar #=> undefined method ‘bar’ for Foo:Class
Foo.new.bar #=> "bar"
Foo.baz #=> "baz"
Foo.new.baz #=> undefined method ‘baz’ for #<Foo:0x7dce8>
As you can see in example 5, using def in an instance_eval is different from using def in a class_eval method call.
Example 5 shows that using def in a class_eval defines an instance method on the receiver (Foo in our example). However, using def in an instance_eval defines an instance method on the singleton class of the receiver (the singleton class of Foo in our example).
This explains why example 3 does not work: the valid_person method was being defined on the singleton class of the controller test class and not as an instance method of the controller test class.
As I previously stated, using def in an instance_eval defines an instance method on the singleton class of the receiver. This also explains how using def in an instance_eval when the receiver is an instance of a class defines a method on that instance.
# example 6
Foo = Class.new
foo = Foo.new
foo.instance_eval do
def instance_bar
"instance_bar"
end
end
foo.instance_bar #=> "instance_bar"
I've already explained where methods are stored in the various examples; however, the following example serves to prove my assertion.
# example 7
class Object
def singleton_class
class << self; self; end
end
end
Foo = Class.new
Foo.class_eval do
def instance_method_of_Foo; end
end
Foo.instance_eval do
def instance_method_of_Foos_singleton_class; end
end
Foo.instance_methods(false).inspect
#=> ["instance_method_of_Foo"]
Foo.singleton_class.instance_methods(false).inspect
#=> ["instance_method_of_Foos_singleton_class", "new", "superclass", "allocate"]
foo = Foo.new
foo.instance_eval do
def instance_method_of_foos_singleton_class; end
end
foo.singleton_class.instance_methods(false).inspect
#=> ["instance_method_of_Foo", "instance_method_of_foos_singleton_class"]
How does define_method fit into this picture?
# example 8
Foo = Class.new
Foo.class_eval do
define_method :instance_method_of_Foo do end
end
Foo.instance_eval do
define_method :another_instance_method_of_Foo do end
end
Foo.instance_methods(false).inspect
#=> ["another_instance_method_of_Foo", "instance_method_of_Foo"]
As you can see from example 8 define_method always defines an instance method on the target (Foo in our example). This makes sense because define_method is being call on the implicit self, which evaluates to the receiver (Foo) in our example.
# example 9
Foo.class_eval do
self #=> Foo
end
Foo.instance_eval do
self #=> Foo
end
By combining examples 5 and 8 I can create the following code that should no longer be surprising.
class Object
def singleton_class
class << self; self; end
end
end
Foo = Class.new
Foo.instance_eval do
def an_instance_method_of_the_singleton_class
end
define_method :an_instance_method do
end
end
Foo.instance_methods(false).inspect
#=> ["an_instance_method"]
Foo.singleton_class.instance_methods(false).inspect
#=> ["an_instance_method_of_the_singleton_class", "new", "superclass", "allocate"]
http://blog.jayfields.com/2007/03/ruby-instanceeval-and-classeval-method.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment