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
何かご意見は?前もって感謝します。
私があなたの質問を正しく理解しているなら、あなたはこのようなものが欲しいです:
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
ここの例を読むと 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
これを行うとき: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>
)