web-dev-qa-db-ja.com

define_method:引数を持つメソッドを動的に作成する方法

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

何かご意見は?前もって感謝します。

30

私があなたの質問を正しく理解しているなら、あなたはこのようなものが欲しいです:

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を返すと仮定しています。)

上記は、このような2つのクラスメソッドを定義することとほぼ同等です。

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
40
Jordan Running

ここの例を読むと 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
5
Albin

これを行うとき:define_method("self.find_by_#{attribute}")

それは間違っています。 define_methodの引数は、単一のWordを持つシンボルです。

正しいコードをいくつかお見せしましょう。

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_brandおよびfind_by_nameのクラスメソッドが生成されます。

メタプログラミングを検討している場合、これはmethod_missingの良いユースケースであることに注意してください。 チュートリアルはこちら method_missingを使用して、目的の機能を実装する(find_by_<x>

1
max pleaner