This post originated from an RSS feed registered with Ruby Buzz
by Jay Fields.
Original Post: Ruby: instance_eval and class_eval method definitions
Feed Title: Jay Fields Thoughts
Feed URL: http://feeds.feedburner.com/jayfields/mjKQ
Feed Description: Blog about Ruby, Agile, Testing and other topics related to software development.
# 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
# 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
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