Ruby/Rails:如何确定模块是否已被包含?

42

在我的问题上进行扩展(ruby/rails: extending or including other modules), 使用我现有的解决方案,最好的方法是确定我的模块是否已经被包含?

目前我所做的是,在每个模块上定义实例方法,这样当它们被包含时,方法就会可用。然后我只是在父模块中添加了一个捕获器(method_missing()),以便我可以捕获它们是否已被包含。我的解决方案代码如下:

module Features
  FEATURES = [Running, Walking]

  # include Features::Running
  FEATURES.each do |feature|
    include feature
  end

  module ClassMethods
    # include Features::Running::ClassMethods
    FEATURES.each do |feature|
      include feature::ClassMethods
    end
  end

  module InstanceMethods
    def method_missing(meth)
      # Catch feature checks that are not included in models to return false
      if meth[-1] == '?' && meth.to_s =~ /can_(\w+)\z?/
        false
      else
        # You *must* call super if you don't handle the method,
        # otherwise you'll mess up Ruby's method lookup
        super
      end
    end
  end

  def self.included(base)
    base.send :extend, ClassMethods
    base.send :include, InstanceMethods
  end
end

# lib/features/running.rb
module Features::Running
  module ClassMethods
    def can_run
      ...

      # Define a method to have model know a way they have that feature
      define_method(:can_run?) { true }
    end
  end
end

# lib/features/walking.rb
module Features::Walking
  module ClassMethods
    def can_walk
      ...

      # Define a method to have model know a way they have that feature
      define_method(:can_walk?) { true }
    end
  end
end

在我的模型中,我有:

# Sample models
class Man < ActiveRecord::Base
  # Include features modules
  include Features

  # Define what man can do
  can_walk
  can_run
end

class Car < ActiveRecord::Base
  # Include features modules
  include Features

  # Define what man can do
  can_run
end

然后我就可以

Man.new.can_walk?
# => true
Car.new.can_run?
# => true
Car.new.can_walk? # method_missing catches this
# => false

我写的对吗?还有更好的方式吗?


1
问题有点复杂,所以我不确定这是否是您要找的,但是要检查模型是否包含,您可以执行object.class.include? Module - makhan
你可以使用 respond_to? 来检查一个方法是否可用。 - Lesleh
1个回答

68
如果我正确理解你的问题,你可以使用 Module#include? 方法:
Man.include?(Features)

例如:

module M
end

class C
  include M
end

C.include?(M) # => true

其他方法

检查Module#included_modules

这种方法可以实现,但相对来说有些间接,因为它会生成一个中间的included_modules数组。

C.included_modules.include?(M) # => true

由于 C.included_modules 的值为 [M, Kernel]

检查Module#ancestors

C.ancestors.include?(M) #=> true

由于 C.ancestors 的值为 [C, M, Object, Kernel, BasicObject]

使用像 < 这样的操作符

Module 类还声明了几个比较运算符:

例如:

C < M # => true 

3
因为“C < M”语法而点赞。你知道我可以在哪里学习这样的语法糖吗?我还没有找到一个像《Thinking in Java》一样深入介绍Ruby语言的东西。 - makhan
@makhan,这是方法Module#< - Cary Swoveland
2
@makhan Russ Olsen的《Eloquent Ruby》是一本很好(而且相当全面)的Ruby惯用语概述。 - TNT
正确的代码是:Man.included_modules.include?(Features) 而不是 includes?。我尝试编辑答案,但需要更改超过6个字符才能编辑,而且这篇文章的其余部分似乎很好 :-) - rept
1
@CarySwoveland 我刚刚对答案进行了相当大的编辑。我增加了更多的格式,链接到相关的API文档,并将提到的第一种方法更改为使用Man.include?(Feature)而不是Man.included_modules.include?(Feature)的“惯用”方法。如果您认为我越界了,请随时恢复它。让我知道您的想法! - Alexander
2
谢谢你,@Alexander。明显有所改善。 - Cary Swoveland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接