Ruby:类C包含模块M;在M中包含模块N不会影响C. [英] Ruby: class C includes module M; including module N in M does not affect C. What gives?

查看:58
本文介绍了Ruby:类C包含模块M;在M中包含模块N不会影响C.的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更详细地说,我有一个模块Narf,它为一系列类提供了基本功能.具体来说,我想影响所有继承Enumerable的类.所以我在Enumerable中是include Narf.

More verbosely, I have a module Narf, which provides essential features to a range of classes. Specifically, I want to affect all classes that inherit Enumerable. So I include Narf in Enumerable.

Array是默认情况下包含Enumerable的类.但是,它不受模块中后来加入Narf的影响.

Array is a class that includes Enumerable by default. Yet, it is not affected by the late inclusion of Narf in the module.

有趣的是,包含之后定义的类从Enumerable中获取Narf.

Interestingly, classes defined after the inclusion get Narf from 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

推荐答案

在写我的问题时,我不可避免地遇到一个答案.这是我想出的.让我知道是否错过了一个显而易见的,简单得多的解决方案.

In writing my question, inevitably, I came across an answer. Here's what I came up with. Let me know if I missed an obvious, much simpler solution.

问题似乎在于,模块包含会弄平所包含模块的祖先,并包含 that .因此,方法查找不是完全动态的,所包含模块的祖先链也不会被检查.

The problem seems to be that a module inclusion flattens the ancestors of the included module, and includes that. Thus, method lookup is not fully dynamic, the ancestor chain of included modules is never inspected.

在实践中,Array知道Enumerable是祖先,但是它并不关心Enumerable中当前包含的内容.

In practice, Array knows Enumerable is an ancestor, but it doesn't care about what's currently included in Enumerable.

好处是您可以再次include模块,它将重新计算模块祖先链,并包括整个内容.因此,在定义并包含Narf之后,您可以重新打开Array并再次包含Enumerable,它也会得到Narf.

The good thing is that you can include modules again, and it'll recompute the module ancestor chain, and include the entire thing. So, after defining and including Narf, you can reopen Array and include Enumerable again, and it'll get Narf too.

class Array
  include Enumerable
end
p Array.ancestors
# => [Array, Enumerable, Narf, Object, Kernel]

现在让我们概括一下:

# 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!

现在在 GitHub Gemcutter 带来额外的乐趣.

Now on GitHub and Gemcutter for extra fun.

这篇关于Ruby:类C包含模块M;在M中包含模块N不会影响C.的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆