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

какие мысли? Спасибо заранее.

3 ответов


Если я правильно понял ваш вопрос, вы хотите что-то вроде этого:

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

Это, если Вы читаете примеры здесь 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 является символом с одним словом.

позвольте мне показать вам правильный код, надеюсь, это будет ясно:

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>)