[英]module extending other modules in Ruby
在这方面,我已经阅读了很多答案,但是我仍然无法弄清楚以下原因为何不起作用
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:...
我也尝试过:
module B
def self.included(recipient)
recipient.extend A
end
end
希望C
将得到扩展(但是我想层次结构是错误的)
重要提示 :问题在于我有一个需要extended
memoist模块,并且我想为其添加一些功能。
我该如何实现?为什么第一个示例不起作用?
extends
将作为参数传递的模块中的方法添加为“类方法”。 你期待看到的东西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
首先,您的示例并不完全有效。 默认情况下,扩展的模块不会添加实例方法。 您的A
模块应如下所示:
module Memoist
def self.extended(mod)
mod.include InstanceMethods
end
module InstanceMethods
def a
puts 'hi'
end
end
end
您可以在extended
, included
或excluded
回调中extend Memoist
。 只要您要添加到界面中,它就可以工作。 在扩展,包含或添加模块之后,将触发回调。 这意味着,如果要使用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, ...]
如果您确实想使用super
关键字,事情将会变得更加困难。 由于Memoist
模块需要首先扩展。 一种实现方法是将模块包含在自己的模块中,以便您可以覆盖它们。 到目前为止非常简单。 但是, Memoist
可能定义了extended
回调(以添加实例方法),当我们将模块包括在自己的模块中时不会触发该回调。 因此,我们需要手动调用它。
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, ...]
回想一下,如果一个模块M1
被included
或extended
(或prepended
由模块) M2
, M1
的实例方法被带入M2
,无论是作为实例方法(如果包括)或作为模块的方法(如果延伸)。 如果M1
包含模块方法(在M1
调用的方法),则它们都将被include
和extend
跳过。 由于类是模块,因此在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]
#=> []
我们看到M2
和M3
都不包含方法:m
。 现在考虑以下内容。
module A
def a
puts 'hi'
end
end
module B
extend A
end
B.instance_methods
#=> []
B.methods & [:a]
#=> [:a]
不出所料, B
包含任何实例方法,其模块方法包括:a
。 从前面的讨论可以看出,将B
包含或扩展到另一个模块(或类) C
不会将实例方法或方法带入C
。
为了增加A
的功能, B
必须include
(或在prepend
) A
。
module B
include A
def b
puts 'ho'
end
end
B.instance_methods
#=> [:b, :a]
B.methods & [:a, :b]
#=> []
然后, C
可以根据需要include
或extend
B
有人可能会问,当模块被另一个模块包含或扩展时,如果忽略它们,是否有任何理由来定义模块方法。 答案是它们只是辅助方法,在非OOP语言中充当函数。 一个示例是Math模块,该模块仅包含模块方法。 因此,它们在模块上被调用; 例如,
Math.sqrt(2)
#=> 1.4142135623730951
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.