简体   繁体   English

模块扩展Ruby中的其他模块

[英]module extending other modules in Ruby

I have read quite a few answers here in this regards, but I still cant figure out why the following doesn't work 在这方面,我已经阅读了很多答案,但是我仍然无法弄清楚以下原因为何不起作用

module A
  def a
    puts 'hi'
  end
end

module B
  extend A
end

class C
  extend B
  def b
    a
  end
end
C.new.b # undefined local variable or method `a' for #<C:...

I've also tried with: 我也尝试过:

module B
  def self.included(recipient)
    recipient.extend A
  end
end

Hoping C will get extended (but I guess the hierarchy is then wrong) 希望C将得到扩展(但是我想层次结构是错误的)

Important : The Problem is that I have a module that requires to be extended memoist , and I want to add some functionality to it. 重要提示 :问题在于我有一个需要extended memoist模块,并且我想为其添加一些功能。

How can I achieve that and why is the first example not working? 我该如何实现?为什么第一个示例不起作用?

extends adds the methods from the module passed as argument as 'class methods'. extends将作为参数传递的模块中的方法添加为“类方法”。 What you're looking for is include , which adds methods from (in this case) A module as instance methods, just like you want it to: 你期待看到的东西include ,它增加了从方法(在这种情况下) A模块实例方法,就像你希望它:

module A
  def a
    puts 'hi'
  end
end

module B
  include A
end

class C
  include B
  def b
    a
  end
end
C.new.b
# hi

First of all your example is not entirely valid. 首先,您的示例并不完全有效。 A module that is extended doesn't add instance methods by default. 默认情况下,扩展的模块不会添加实例方法。 Your A module should look something like this: 您的A模块应如下所示:

module Memoist
  def self.extended(mod)
    mod.include InstanceMethods
  end

  module InstanceMethods
    def a
      puts 'hi'
    end
  end
end

You can extend Memoist in an extended , included or excluded callback. 您可以在extendedincludedexcluded回调中extend Memoist This works as long as you're adding to the interface. 只要您要添加到界面中,它就可以工作。 The callbacks are triggered after your module is extended, included or prepended. 在扩展,包含或添加模块之后,将触发回调。 This means that if you want to extend an existing method using super this won't work since the original is called before your version. 这意味着,如果要使用super扩展现有方法,则该方法将无效,因为在您的版本之前会调用原始方法。

module YourMemoist
  # Depending on how you want to incorporate your module into the class
  # you can use one of the following: 
  def self.extended(mod)
    mod.extend Memoist
  end

  # def self.included(mod)
  #   mod.extend Memoist
  # end

  # def self.prepended(mod)
  #   mod.extend Memoist
  # end

  # ...
end

class C
  extend MemoistHelper # or include, prepend
end

C.new.a #=> prints hi
C.singleton_class.ancesotrs #=> [#<Class:C>, Memoist, YourMemoist, ...]

If you do want to use the super keyword things are going to be a bit more difficult. 如果您确实想使用super关键字,事情将会变得更加困难。 Since the Memoist module needs to be extended first. 由于Memoist模块需要首先扩展。 One way of doing this by including the module into your own, so you can override them. 一种实现方法是将模块包含在自己的模块中,以便您可以覆盖它们。 Pretty simple so far. 到目前为止非常简单。 However Memoist might have an extended callback defined (to add instance methods), which is not triggered when we include the module in our own. 但是, Memoist可能定义了extended回调(以添加实例方法),当我们将模块包括在自己的模块中时不会触发该回调。 So we need to call this manually. 因此,我们需要手动调用它。

module YourMemoist
  include Memoist

  def self.extended(mod)
    Memoist.send(:extended, mod) # using #send to call private method
    # If you want to add your own instance methods add them after the
    # above call (as shown in the Memoist module).
  end

  # ...
end

class C
  extend MemoistHelper
end

C.new.a #=> prints hi
C.singleton_class.ancesotrs #=> [#<Class:C>, YourMemoist, Memoist, ...]

Recall that if a module M1 is included or extended (or prepended ) by a module M2 , M1 's instance methods are brought into M2 , either as instance methods (if including) or as module methods (if extending). 回想一下,如果一个模块M1includedextended (或prepended由模块) M2M1的实例方法被带入M2 ,无论是作为实例方法(如果包括)或作为模块的方法(如果延伸)。 If M1 contains module methods (methods invoked on M1 ) they are skipped over by both include and extend . 如果M1包含模块方法(在M1调用的方法),则它们都将被includeextend跳过。 As classes are modules, this applies when M2 is a class. 由于类是模块,因此在M2是类时适用。

module M1
  def i() end
  def self.m() end
end

M1.instance_methods
  #=> [:i] 
M1.methods(false)
  #=> [:m] 

module M2; end

M2.extend M1
M2.instance_methods
  #=> []
M2.methods & [:i, :m]
  #=> [:i]

module M3; end

M3.include M1
M3.instance_methods
  #=> [:i]
M3.methods & [:i, :m]
  #=> []

We see that neither M2 nor M3 contains a method :m . 我们看到M2M3都不包含方法:m Now consider the following. 现在考虑以下内容。

module A
  def a
    puts 'hi'
  end
end

module B
  extend A
end

B.instance_methods
  #=> []
B.methods & [:a]
  #=> [:a]

As expected, B contains no instance methods and its module methods include :a . 不出所料, B包含任何实例方法,其模块方法包括:a From the earlier discussion it follows that including or extending B into another module (or class) C brings no instance methods or methods into C . 从前面的讨论可以看出,将B包含或扩展到另一个模块(或类) C 不会将实例方法或方法带入C

To add to the functionality of A , B must include (or prepend ) A . 为了增加A的功能, B必须include (或在prependA

module B
  include A
  def b
    puts 'ho'
  end
end

B.instance_methods
  #=> [:b, :a] 
B.methods & [:a, :b]
  #=> []

C may then include or extend B , depending on requirements. 然后, C可以根据需要includeextend B

One may ask whether there is any reason to define module methods if they are disregarded when the module is included or extended by another module. 有人可能会问,当模块被另一个模块包含或扩展时,如果忽略它们,是否有任何理由来定义模块方法。 The answer is that they are simply helper methods that behave as functions in non-OOP languages. 答案是它们只是辅助方法,在非OOP语言中充当函数。 An example is the module Math , which contains module methods only. 一个示例是Math模块,该模块仅包含模块方法。 They are therefore invoked on the module; 因此,它们在模块上被调用; for example, 例如,

Math.sqrt(2)
  #=> 1.4142135623730951  

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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