更详细地说,我有一个模块Narf,它为一系列类提供基本功能.具体来说,我想影响所有继承的类Enumerable.所以我include Narf在Enumerable.
Array是一个Enumerable默认包含的类.然而,它并没有受到Narf模块中后期包含的影响.
有趣的是,包含后定义的类Narf来自Enumerable.
# This module provides essential features
module Narf
def narf?
puts "(from #{self.class}) ZORT!"
end
end
# I want all Enumerables to be able to Narf
module Enumerable
include Narf
end
# Fjord is an Enumerable defined *after* including Narf in Enumerable
class Fjord
include Enumerable
end
p Enumerable.ancestors # Notice that Narf *is* there
p Fjord.ancestors # Notice that Narf *is* here too
p Array.ancestors # But, grr, not here
# => [Enumerable, Narf]
# => [Fjord, Enumerable, Narf, Object, Kernel]
# => [Array, Enumerable, Object, Kernel]
Fjord.new.narf? # And this will print fine
Array.new.narf? # And this one will raise
# => (from Fjord) ZORT!
# => NoMethodError: undefined method `narf?' for []:Array
Run Code Online (Sandbox Code Playgroud)
在写下我的问题时,我不可避免地找到了答案。这就是我的想法。如果我错过了一个明显的、更简单的解决方案,请告诉我。
问题似乎是模块包含压平了所包含模块的祖先,并包含了该. 因此,方法查找不是完全动态的,所包含模块的祖先链永远不会被检查。
实际上,ArrayknowEnumerable是祖先,但它不关心当前包含的内容Enumerable。
好处是您可以include再次模块,它会重新计算模块祖先链,并包含整个内容。因此,在定义并包含之后Narf,您可以重新打开Array并Enumerable再次包含,它也会得到Narf。
class Array
include Enumerable
end
p Array.ancestors
# => [Array, Enumerable, Narf, Object, Kernel]
Run Code Online (Sandbox Code Playgroud)
现在让我们概括一下:
# Narf here again just to make this example self-contained
module Narf
def narf?
puts "(from #{self.class}) ZORT!"
end
end
# THIS IS THE IMPORTANT BIT
# Imbue provices the magic we need
class Module
def imbue m
include m
# now that self includes m, find classes that previously
# included self and include it again, so as to cause them
# to also include m
ObjectSpace.each_object(Class) do |k|
k.send :include, self if k.include? self
end
end
end
# imbue will force Narf down on every existing Enumerable
module Enumerable
imbue Narf
end
# Behold!
p Array.ancestors
Array.new.narf?
# => [Array, Enumerable, Narf, Object, Kernel]
# => (from Array) ZORT!
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
629 次 |
| 最近记录: |