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

发布于 2024-10-31 07:00:42 字数 1337 浏览 1 评论 0原文

我有以下内容:

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)

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

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

I have the following:

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

In another class which mixes-in the Thing module:

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

When calling has_things within a class, I would like to have the dynamic "foo_for_thing_one" and "foo_for_thing_two" instance methods available. For example:

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

However, I get the following error:

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

I realize that "self" in the problem line pointed out above (first line of the InstanceMethods module) refers to the InstanceMethods module.

How do I reference the "things" class method (which returns [:one, :two] in this example) so I can loop through and create dynamic instance methods for each? Thanks. Or if you have other suggestions for accomplishing this, please let me know.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

三人与歌 2024-11-07 07:00:42

快速回答:

将 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"

仅在需要时添加复杂性(选项提取、输入标准化等)。及时编写代码,而不仅仅是以防万一。

Quick answer:

Put the contents of InstanceMethods inside the has_things method definition and remove the InstanceMethods module.

Better answer:

Your use of the InstanceMethods-ClassMethods anti-pattern is especially unwarranted here and cargo-culting it has added to your confusion about scope and context. Do the simplest thing that could possibly work. Don't copy someone else's code without critical thinking.

The only module you need is ClassMethods, which should be given a useful name and should not be included but rather used to extend the class that you want to grant the has_things functionality. Here's the simplest thing that could possibly work:

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"

Only add complexity (options extraction, input normalization, etc) when you need it. Code just in time, not just in case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文