【问题标题】:define_method: How to dynamically create methods with argumentsdefine_method:如何动态创建带参数的方法
【发布时间】:2016-11-09 00:56:54
【问题描述】:

我想为 find_by 功能创建一堆方法。我不想一遍又一遍地写同样的东西,所以我想使用元编程。

假设我想创建一个按名称查找的方法,接受名称作为参数。我该怎么做?我过去使用过 define_method,但我没有任何参数可以采用该方法。 这是我的(坏)方法

["name", "brand"].each do |attribute|
    define_method("self.find_by_#{attribute}") do |attr_|
      all.each do |prod|
        return prod if prod.attr_ == attr_
      end
    end
  end

有什么想法吗?提前致谢。

【问题讨论】:

  • 请注意,如果所有数据集都是大型数据集,这可能会导致性能问题。我也真诚地希望这不在 Rails 上下文中,因为 rails 已经为每个属性实现了find_by_XXX
  • 注意:这将定义两个名为self.find_by_nameself.find_by_brand的方法。虽然可以创建这样的方法,但使用普通的方法调用语法来调用它们是不可能的,因为. 不是标识符中的合法字符。你有什么特别的原因要定义一个非法名称的方法吗?
  • @engineersmnky 这不是轨道! all 只为玩具商店的库存系统返回一个 Products 数组。这是 Udacity Ruby 纳米学位的最终项目。
  • @JörgWMittag 我对此一无所知,我想知道为什么会出现 NoMethodError,因为我有一个 self.something 方法。

标签: ruby metaprogramming


【解决方案1】:

如果我正确理解你的问题,你想要这样的东西:

class Product
  class << self
    [:name, :brand].each do |attribute|
      define_method :"find_by_#{attribute}" do |value|
        all.find {|prod| prod.public_send(attribute) == value }
      end
    end
  end
end

(我假设 all 方法返回一个 Enumerable。)

以上内容或多或少等同于定义两个类方法,如下所示:

class Product
  def self.find_by_name(value)
    all.find {|prod| prod.name == value }
  end

  def self.find_by_brand(value)
    all.find {|prod| prod.brand == value }
  end
end

【讨论】:

  • 这对我有用,但是......你能解释一下你是怎么到这里的吗?我搜索了class &lt;&lt; self,发现它打开了一个类并改变了它的行为,但是,为什么我需要在类本身内部这样做?
  • 你需要这样做,因为你想定义一个类方法。没有它,define_method 将定义一个实例方法,而您的示例中的define_method("self.foo") 将不起作用。
  • 非常感谢!我会更深入地研究它以便更适应元编程,但现在,你帮了我很多! :)
  • 更惯用的方法是使用define_singleton_method
  • 为了性能,定义类方法和实例方法的区别非常重要。感谢您提及@Jordan
【解决方案2】:

如果你阅读这里的例子http://apidock.com/ruby/Module/define_method你会发现这个:

define_method(:my_method) do |foo, bar| # or even |*args|
  # do something
end

相同
def my_method(foo, bar)
   # do something
end

【讨论】:

  • 这一直给我一个 NoMethodError :/
【解决方案3】:

当你这样做时:define_method("self.find_by_#{attribute}")

这是不正确的。 define_method 的参数是一个带有单个单词的符号。

让我给你看一些正确的代码,希望这会很清楚:

class MyClass < ActiveRecord::Base
  ["name", "brand"].each do |attribute|
    define_method(:"find_by_#{attribute}") do |attr_|
      first(attribute.to_sym => attr_)
    end
  end
end

这将为find_by_brandfind_by_name 生成类方法。

请注意,如果您正在研究元编程,这是 method_missing 的一个很好的用例。 here's a tutorial 使用 method_missing 来实现您想要的相同功能 (find_by_&lt;x&gt;)

【讨论】:

  • 如果这是 Rails,那么这是一个无用的练习,因为 find_by_XXX 已经定义。 define_method 也可以毫无问题地接受字符串。
  • method_missing 是我讨厌的东西。没有简单的方法可以找到以这种方式定义的方法。尽可能避免。
  • @akostadinov 同意
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2010-10-19
  • 2011-01-14
  • 1970-01-01
  • 2013-03-29
  • 1970-01-01
  • 2021-12-05
相关资源
最近更新 更多