在Ruby中,如何从正在扩展的模块中应用attr_accessor?

时间:2015-10-02 05:42:43

标签: ruby

我试图通过将方法组织到单独的模块中来模块化一些Ruby代码。最初我有这样的事情:

class Joe
  attr_accessor :name

  def self.arms
    2
  end

  def self.legs
    2
  end
end

我尝试过这样的事情:

class Joe
  extend Person
end

module Person
  include Name
  include Arms
  include Legs
end

module Name
  attr_accessor :name
end

module Arms
  def arms
    2
  end
end

module Legs
  def legs
    2
  end
end

但是,无效的部分是attr_accessor。我尝试了include / extenddef self.included(base); base.extend的所有不同组合,但我似乎无法找到合适的组合来使所有内容协同工作。我怎么能这样做?

更新:我认为我遗漏的部分是每个模块都可能有实例方法和类方法。所以我现在有这样的事情:

class Joe
  include Person
end

module Person
  include Name::InstanceMethods
  include Arms::InstanceMethods
  include Legs::InstanceMethods

  def self.included(base)
    base.extend Name::ClassMethods
    base.extend Arms::ClassMethods
    base.extend Legs::ClassMethods
  end
end

module Name
  module ClassMethods; end

  module InstanceMethods
    attr_accessor :name
  end
end

module Arms
  module ClassMethods
    def arms
      2
    end
  end

  module InstanceMethods; end
end

module Legs
  module ClassMethods
    def legs
      2
    end
  end

  module InstanceMethods; end
end

虽然这有效,但感觉很乱。感觉就像Person模块对实例方法和类方法了解得太多。如果我要修改Name模块以删除空/未使用的ClassMethods模块,我还必须修改Person类。

2 个答案:

答案 0 :(得分:10)

includeModule中定义,因此只能在模块和类(模块)上调用。它通过调用append_features将给定模块中的常量,(实例)方法和(模块)变量添加到接收器。

另一方面,

extendObject中定义,即它不限于模块和类。它将给定模块中的实例方法添加到接收器,或者更确切地说,添加到接收器的单例类。

这是一个带有实例方法hello的示例模块:

module Mod
  def hello
    "Hello from #{self.class} '#{self}'"
  end
end

如果我们extend一个实例(而不是一个类),那么hello将成为实例方法

str = 'abc'
str.extend(Mod)
str.hello
#=> "Hello from String 'abc'"

如果我们扩展一个类,那么hello将成为类方法

String.extend(Mod)
String.hello
#=> "Hello from Class 'String'"

这是因为类方法实际上只是在类的单例类中定义的实例方法。

也就是说,有几个选项可以通过调用extend和/或include来定义类和实例方法:

1。 extendinclude

这是最基本的一个,您可以将include Name从人员转移到Joe

module Person
  include Arms, Legs
end

class Joe
  extend Person
  include Name
end

2。超类

中的extendinclude

或者你可以让Person成为extendinclude其他模块的类,并将其用作Joe的超类:

class Person
  extend Arms, Legs
  include Name
end

class Joe < Person
end

接下来的选项涉及一些Ruby魔术 - 他们使用回调在include上调用extend,反之亦然:

3。来自include

extended

您可以在include Name内使用Person回调至module Person include Arms, Legs def self.extended(mod) mod.include(Name) end end class Joe extend Person end

extend

4。来自included

include Person

或者您可以Joeextend使用extended回调来致电module Person include Name def self.included(mod) mod.extend Arms, Legs end end class Joe include Person end

Joe

3和4从Person内部看起来很干净但是包含或扩展PHANTOMJS_BIN可能并不明显(甚至可能令人困惑?)也定义了类或实例方法。

答案 1 :(得分:0)

为了正确获得您想要的功能,您必须将extend更改为include并在模块中提供included块。这样你就可以在一个模块中定义实例和类方法,只需include模块并适当地组织你的方法。

可能需要一些代码重新组织,但为了帮助解释更多,您可以read this article about how modules in Ruby work.

如果仍不清楚,您可能需要查看class_eval