web-dev-qa-db-fra.com

define_method: Comment créer dynamiquement des méthodes avec des arguments

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.

23
Kostas Andrianos

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

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
4
Albin

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

1
max pleaner