繁体   English   中英

Ruby:Mixin,它添加了动态实例方法,其名称是使用类方法创建的

[英]Ruby: Mixin which adds dynamic instance methods whose names are created using a class method

我有以下内容:

module Thing
  def self.included(base)
    base.send :extend, ClassMethods
  end

  module ClassMethods
    attr_reader :things

    def has_things(*args)
      options = args.extract_options! # Ruby on Rails: pops the last arg if it's a Hash

      # Get a list of the things (Symbols only)
      @things = args.select { |p| p.is_a?(Symbol) }

      include InstanceMethods
    end
  end

  module InstanceMethods
    self.class.things.each do |thing_name| # !!! Problem is here, explaination below
      define_method "foo_for_thing_#{thing_name}" do
        "bar for thing #{thing_name}"
      end
    end
  end
end

在另一个混合了Thing模块的类中:

class Group
  has_things :one, :two, :option => "something"
end

在类中调用has_things ,我希望动态的“foo_for_thing_one”和“foo_for_thing_two”实例方法可用。 例如:

@group = Group.new
@group.foo_for_thing_one # => "bar for thing one"
@group.foo_for_thing_two # => "bar for thing two"

但是,我收到以下错误:

`<module:InstanceMethods>': undefined method `things' for Module:Class (NoMethodError)

我意识到上面指出的问题行中的“self”(InstanceMethods模块的第一行)引用了InstanceMethods模块。

如何引用“things”类方法(在此示例中返回[:one,:two]),以便我可以遍历并为每个方法创建动态实例方法? 谢谢。 或者如果您有其他建议来完成此操作,请告诉我。

快速回答:

将InstanceMethods的内容放在has_things方法定义中,然后删除InstanceMethods模块。

更好的答案:

您对InstanceMethods-ClassMethods反模式的使用在这里尤其没有根据,而且货物结果增加了您对范围和上下文的困惑。 做最简单的事可能有用。 没有批判性思维,不要复制别人的代码。

您需要的唯一模块是ClassMethods,它应该被赋予一个有用的名称,不应该包含在内,而是用于扩展您要授予has_things功能的类。 这是最简单的事情:

module HasThings
  def has_things(*args)
    args.each do |thing|
      define_method "thing_#{thing}" do
        "this is thing #{thing}"
      end
    end
  end
end

class ThingWithThings
  extend HasThings
  has_things :foo
end

ThingWithThings.new.thing_foo # => "this is thing foo"

只在需要时添加复杂性(选项提取,输入规范化等)。 代码及时,不仅仅是以防万一。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM