简体   繁体   中英

instance_eval vs class_eval in module

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

Notice here I changed class_eval to instance_eval

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

So it seems that including the module neither defined an instance method nor a class method.

Any explanation what's going on here?

This code was tested on ruby 1.8.7 on mac.

First, think of what include does. it makes the instance methods of the module being included into instance methods on the including class. ie apart from the fact that your working example uses an anonymous module it is equivalent to:

module M1
  def lab
    puts 'm'
  end
end

class Foo
    include M1

    def lab
      super 
      puts 'c'
    end
end

Next, think of what class_eval does. It evaluates the given code in the context of the class or module. ie it's exactly like you reopened the module and typed the code passed to class_eval . So MyModule = Module.new { class_eval "def lab; puts 'm' end" } is equivalent to

module MyModule
  def lab
    puts 'm'
  end
end

Hopefully this explains the case that works.

When you use instance_eval you are evaluating the code within the context of the receiving object (in this case the instance of module) so MyMod2 = Module.new { instance_eval "def lab; puts 'm' end" } is equivalent to

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

ie it creates a module method which you'd call via MyMod2.lab and such methods are not added as instance methods by include .


Please note : this answer borrows a bit of its explanation from an answer I wrote to a previous question asking about instance_eval vs. class_eval relating to an example from The Ruby Programming Language book. You might find that answer helpful too.

including a module just takes the instance methods - you are looking for extend. luckily to get the best of both worlds, you can simply do:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM