ruby - define_method: Cómo crear dinámicamente métodos con argumentos
metaprogramming (3)
Quiero crear un montón de métodos para una característica find_by. No quiero escribir lo mismo una y otra vez, así que quiero usar metaprogramación.
Digamos que quiero crear un método para buscar por nombre, aceptando el nombre como un argumento. ¿Cómo lo haría? He usado define_method en el pasado, pero no tenía ningún argumento para tomar el método. Aquí está mi (mal) enfoque
["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
¿Alguna idea? Gracias por adelantado.
Cuando haces esto: define_method("self.find_by_#{attribute}")
Eso es incorrecto. El argumento para define_method es un símbolo con una sola palabra.
Déjame mostrarte un código correcto, espero que esto quede claro:
class MyClass < ActiveRecord::Base
["name", "brand"].each do |attribute|
define_method(:"find_by_#{attribute}") do |attr_|
first(attribute.to_sym => attr_)
end
end
end
Esto producirá métodos de clase para find_by_brand
y find_by_name
.
Tenga en cuenta que si está buscando metaprogramación, este es un buen caso de uso para method_missing. Aquí hay un tutorial para usar method_missing para implementar la misma funcionalidad que busca ( find_by_<x>
)
Si entiendo tu pregunta correctamente, quieres algo como esto:
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
(Supongo que el método all
devuelve un Enumerable).
Lo anterior es más o menos equivalente a definir dos métodos de clase como este:
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 lees los ejemplos aquí http://apidock.com/ruby/Module/define_method , encontrarás este:
define_method(:my_method) do |foo, bar| # or even |*args|
# do something
end
es lo mismo que
def my_method(foo, bar)
# do something
end