Je veux créer un tas de méthodes pour une fonctionnalité find_by. Je ne veux pas écrire la même chose encore et encore, alors je veux utiliser la métaprogrammation.
Dites que je veux créer une méthode pour rechercher par nom, en acceptant le nom comme argument. Comment pourrais-je le faire? J'ai utilisé define_method par le passé, mais je n'avais aucun argument à prendre pour la méthode. Voici ma (mauvaise) approche
["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
Des pensées? Merci d'avance.
Si je comprends bien votre question, vous voulez quelque chose comme ça:
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
(Je suppose que la méthode all
renvoie un Enumerable.)
Ce qui précède est plus ou moins équivalent à la définition de deux méthodes de classe comme celle-ci:
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
Si vous lisez les exemples ici http://apidock.com/Ruby/Module/define_method vous trouverez celui-ci:
define_method(:my_method) do |foo, bar| # or even |*args|
# do something
end
est le même que
def my_method(foo, bar)
# do something
end
Quand vous faites ceci: define_method("self.find_by_#{attribute}")
c'est inexact. L'argument de define_method est un symbole avec un seul mot.
Laissez-moi vous montrer un code correct, j'espère que cela sera clair:
class MyClass < ActiveRecord::Base
["name", "brand"].each do |attribute|
define_method(:"find_by_#{attribute}") do |attr_|
first(attribute.to_sym => attr_)
end
end
end
Cela produira des méthodes de classe pour find_by_brand
et find_by_name
.
Notez que si vous cherchez une métaprogrammation, c'est un bon cas d'utilisation pour method_missing. voici un tutoriel pour utiliser method_missing afin d'implémenter la même fonctionnalité que vous (find_by_<x>
)