Instance_eval: why the class of subclass is superclass
def singleton_class
class << self
self
end
end
class Human
proc = lambda { puts 'proc says my class is ' + self.name.to_s }
singleton_class.instance_eval do
define_method(:lab) do
proc.call
开发者_Go百科 end
end
end
class Developer < Human
end
Human.lab # class is Human
Developer.lab # class is Human ; oops
Following solution works.
def singleton_class
class << self
self
end
end
class Human
proc = lambda { puts 'proc says my class is ' + self.name.to_s }
singleton_class.instance_eval do
define_method(:lab) do
self.instance_eval &proc
end
end
end
class Developer < Human
end
Human.lab # class is Human
Developer.lab # class is Human ; oops
Why Developer.lab is reporting that it is Human ? And what can be done so that proc reports Developer when Developer.lab is invoked.
It's subtle, but it boils down to simply calling the block (in which case it acts as a normal closure, and self
corresponds to where it was defined, i.e. in Human
), or using it (directly) as a block for a method definition or instance_eval
:
def singleton_class
class << self
self
end
end
class Human
PROC = proc { puts 'proc says my class is ' + self.name.to_s }
singleton_class.instance_eval do
define_method(:lab) do
PROC.call
end
define_method(:lab2, &PROC.method(:call))
define_method(:lab3) do
instance_eval(&PROC)
end
define_method(:lab4, &PROC)
end
end
class Developer < Human
end
Human::PROC.call # => "class is Human" (original closure)
Developer.lab # Same as previous line, so "class is Human" (original closure)
Developer.lab2 # ditto
Developer.instance_eval(&Human::PROC) # => "class is Developer" (instance_eval changes sets a different scope)
Developer.lab3 # Same as previous line, so "class is Developer"
Developer.lab4 # ditto
The closure is capturing self in the context where it is defined - just like closures should do. So, when it is called, it will use the references to the context it captured. A closure is not the ideal tool to define the intended functionality. Instead of proc.call, the content of the "define_method" invocation should be "puts 'proc says my class is ' + name.to_s"
I have to think a little bit about exactly why this works, but for the moment it does work:
class Human
proc = -> { name }
define_singleton_method(:lab, &proc)
end
class Developer < Human; end
require 'test/unit'
class TestClosures < Test::Unit::TestCase
def test_that_the_human_class_is_named_human
assert_equal 'Human', Human.lab
end
def test_that_the_developer_class_is_named_developer
assert_equal 'Developer', Developer.lab
end
end
精彩评论