简体   繁体   中英

Include vs extend within another module

Why does the code below return NoMethodFound error:

module Mod1
  def print_string
    puts 'hello ruby'
  end
end
module Mod2
  include Mod1
end
Mod2.print_string 

while the code below runs fine?

module Mod1
  def print_string
    puts 'hello ruby'
  end
end
module Mod2
  extend Mod1
end
Mod2.print_string

extend - adds the specified module's methods and constants to the target's metaclass (ie the singleton class) eg

  • if you call Klazz.extend(Mod) , now Klazz has Mod's methods (as class methods)
  • if you call obj.extend(Mod) , now obj has Mod's methods (as instance methods), but no other instance of of obj.class has those methods.
  • extend is a public method

include - By default, it mixes in the specified module's methods as instance methods in the target module/class. eg

  • if you call class Klazz; include Mod; end; class Klazz; include Mod; end; , now all instances of Klazz have access to Mod's methods (as instance methods)
  • include is a private method, because it's intended to be called from within the container class/module.

However , modules very often override include 's behavior by monkey-patching the included method. This is very prominent in legacy Rails code. more details from Yehuda Katz .

Further details about include , with its default behavior, assuming you've run the following code

class Klazz
  include Mod
end
  • If Mod is already included in Klazz, or one of its ancestors, the include statement has no effect
  • It also includes Mod's constants in Klazz, as long as they don't clash
  • It gives Klazz access to Mod's module variables, eg @@foo or @@bar
  • raises ArgumentError if there are cyclic includes
  • Attaches the module as the caller's immediate ancestor (ie It adds Mod to Klazz.ancestors, but Mod is not added to the chain of Klazz.superclass.superclass.superclass. So, calling super in Klazz#foo will check for Mod#foo before checking to Klazz's real superclass's foo method. See the RubySpec for details.).

Of course, the ruby core documentation is always the best place to go for these things. The RubySpec project is also a fantastic resource, because they document the functionality precisely.

ref: John

Yes, extend adds the module's methods as class methods to the caller, whereas include adds them as instance methods.

An example (using classes instead of modules since modules can't have instances...)

This works...

module Mod1
  def print_string
    puts 'hello ruby'
  end
end
class Mod2
  extend Mod1
end
Mod2.print_string

And this works...

module Mod1
  def print_string
    puts 'hello ruby'
  end
end
class Mod2
  include Mod1
end
mod_instance = Mod2.new
mod_instance.print_string

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