Instance_eval: why a subclass is a 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
        end
      end
    end

    class Developer < Human
    end

    Human.lab # class is Human
    Developer.lab # class is Human ; oops

      

The 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 does Developer.lab say it's Human? And what can be done to process proc Developer reports when Developer.lab is called.

+2


a source to share


3 answers


It's subtle, but it boils down to just calling the block (in this case, it acts like a normal closure, but self

matches where it was defined, i.e. in Human

), or uses it (directly) as a method definition block, 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

      

+4


a source


A closure captures self in the context where it is defined, just like closures are supposed to do. So when it is called, it will use references to the context it has captured. Closure is not an ideal tool for defining intended functionality. Instead of proc.call, the content of the "define_method" call should be "puts" proc says my class is "+ name.to_s"



+4


a source


I need to think a little about why this works, but for now this works:

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

      

+1


a source







All Articles