instance_eval vs模块中的class_eval

时间:2010-08-19 15:43:58

标签: ruby

class Foo
    include Module.new { class_eval "def lab; puts 'm' end" }

    def lab
      super 
      puts 'c'
    end
end

Foo.new.lab #=> m c

=============================================== =========================

class Foo
    include Module.new { instance_eval "def lab; puts 'm' end" }

    def lab
      super 
      puts 'c'
    end
end

请注意,我将class_eval更改为instance_eval

Foo.new.lab rescue nil#=> no super class method lab
Foo.lab #=> undefined method lab for Foo class

所以似乎包含模块既没有定义实例方法也没有定义类方法。

有什么解释在这里发生了什么?

此代码在mac上的ruby 1.8.7上进行了测试。

2 个答案:

答案 0 :(得分:9)

首先,想一想include的作用。它使模块的实例方法包含在包含类的实例方法中。即,除了您的工作示例使用匿名模块之外,它等同于:

module M1
  def lab
    puts 'm'
  end
end

class Foo
    include M1

    def lab
      super 
      puts 'c'
    end
end

接下来,想一想class_eval的作用。它在类或模块的上下文中评估给定的代码。即它就像你重新打开模块并输入传递给class_eval的代码一样。所以MyModule = Module.new { class_eval "def lab; puts 'm' end" }相当于

module MyModule
  def lab
    puts 'm'
  end
end

希望这可以解释有效的案例。

使用instance_eval时,您正在评估接收对象上下文中的代码(在本例中为模块实例),因此MyMod2 = Module.new { instance_eval "def lab; puts 'm' end" }等同于

module MyMod2
  def MyMod2.lab
    puts 'm'
  end
end

即。它会创建一个模块方法,您可以通过MyMod2.lab调用该方法,并且include不会将此类方法作为实例方法添加。


请注意:这个答案从我写给previous question asking about instance_eval vs. class_eval的答案中借用了一些解释,该答案与 Ruby Programming Language 书中的一个例子有关。您可能会发现这个答案也很有帮助。

答案 1 :(得分:3)

包括一个模块只需要实例方法 - 你正在寻找扩展。幸运的是,为了获得两全其美,你可以简单地做到:

module Something
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def blah
      puts "lol"
    end
  end
end

class Test
  include Something
end 

IRB:

>> Test.blah
lol
=> nil