2016-07-06 2 views
13

Ich möchte eine Reihe von Methoden für eine find_by-Funktion erstellen. Ich möchte nicht immer wieder dasselbe schreiben, also möchte ich Metaprogrammierung verwenden.define_method: So erstellen Sie Methoden dynamisch mit Argumenten

Angenommen, ich möchte eine Methode zum Suchen nach Namen erstellen und den Namen als Argument akzeptieren. Wie würde ich es tun? Ich habe in der Vergangenheit define_method verwendet, aber ich hatte keine Argumente für die Methode. Hier ist mein (sehr schlecht) Ansatz

["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 

Irgendwelche Gedanken? Danke im Voraus.

+0

Beachten Sie, wenn alles ein großer Datensatz ist, kann dies zu Leistungsproblemen führen. Ich hoffe auch, dass dies außerhalb des Rails-Kontexts liegt, da rails bereits 'find_by_XXX' für jedes Attribut implementiert. – engineersmnky

+1

Hinweis: Dies definiert zwei Methoden namens 'self.find_by_name' und' self.find_by_brand'. Obwohl es möglich ist, solche Methoden zu erstellen, ist es unmöglich, sie mit der normalen Methodenaufrufsyntax aufzurufen, da '.' kein gültiges Zeichen in einem Bezeichner ist. Gibt es einen bestimmten Grund, warum Sie eine Methode mit einem ungültigen Namen definieren möchten? –

+0

@engineersmnky Es ist nicht Schienen! Alle geben nur ein Array von 'Produkten' für das Inventarsystem eines Spielzeugladens zurück. Es ist das letzte Projekt für den Ruby Nanodegree in Udacity. –

Antwort

19

Wenn ich Ihre Frage recht verstehe, wollen Sie etwas wie folgt aus:

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 

(Ich gehe davon aus, dass die all Methode eine Enu zurückgibt . Merable)

Die oben ist mehr oder weniger gleich, die zwei Klassenmethoden wie folgt aus:

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 
+0

Das funktioniert für mich aber ... Können Sie erklären, wie Sie hierher gekommen sind? Ich suchte nach 'class << self 'und sah, dass es eine Klasse öffnet und ihr Verhalten ändert, aber warum sollte ich das innerhalb der Klasse selbst tun müssen? –

+1

Sie müssen das tun, weil Sie eine Klassenmethode definieren möchten. Ohne dies definiert 'define_method' eine Instanzmethode und' define_method ("self.foo ")' wie in Ihrem Beispiel funktioniert nicht. –

+0

Vielen Dank! Ich werde mehr in es mit Metaprogramming mehr bequem zu sein, aber für jetzt, Sie haben mir sehr geholfen! :) –

1

Es wenn Sie die Beispiele hier http://apidock.com/ruby/Module/define_method lesen Sie diese ein:

define_method(:my_method) do |foo, bar| # or even |*args| 
    # do something 
end 

ist die gleiche wie

def my_method(foo, bar) 
    # do something 
end 
+1

Das gibt mir einen NoMethodError:/ –

1

Wenn Sie dies tun: define_method("self.find_by_#{attribute}")

, die nicht korrekt ist. Das Argument für define_method ist ein Symbol mit einem einzelnen Wort.

Lassen Sie mich Ihnen ein paar richtigen Code, dies wird hoffentlich klar sein:

class MyClass < ActiveRecord::Base 
    ["name", "brand"].each do |attribute| 
    define_method(:"find_by_#{attribute}") do |attr_| 
     first(attribute.to_sym => attr_) 
    end 
    end 
end 

Diese Klasse Methoden für find_by_brand und find_by_name produzieren wird.

Beachten Sie, dass dies ein guter Anwendungsfall für method_missing ist, wenn Sie sich mit Metaprogrammierung beschäftigen. here's a tutorial Verwenden Sie method_missing, um dieselbe Funktionalität zu implementieren, die Sie anstreben (find_by_<x>)

+2

Wenn dies Schienen ist, ist dies eine nutzlose Übung, da' find_by_XXX' ist "define_method" kann einen String ohne Problem akzeptieren. – engineersmnky

+0

'method_missing' ist etwas, was ich hasse. Es gibt keinen einfachen Weg, Methoden zu finden, die auf diese Weise definiert sind. Vermeide es wenn möglich. – akostadinov

+0

@akostadinov stimmt zu –

Verwandte Themen