2016-07-06 104 views
13

Voglio creare un sacco di metodi per una funzione find_by. Non voglio scrivere la stessa cosa più e più volte, quindi voglio usare metaprogramming.define_method: Come creare dinamicamente metodi con argomenti

Dire che voglio creare un metodo per trovare per nome, accettando il nome come argomento. Come lo farei? Ho usato define_method in passato ma non avevo argomenti per il metodo da eseguire. Ecco la mia (cattivo) approccio

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

Qualche idea? Grazie in anticipo.

+0

Tenere presente che se tutto è un set di dati di grandi dimensioni, ciò potrebbe causare problemi di prestazioni. Inoltre spero sinceramente che questo sia al di fuori del contesto delle rotaie dato che rails implementa già 'find_by_XXX' per ogni attributo. – engineersmnky

+1

Nota: questo definirà due metodi denominati 'self.find_by_name' e' self.find_by_brand'. Sebbene sia possibile creare tali metodi, è impossibile chiamarli usando la sintassi di chiamata del metodo normale, poiché '.' non è un carattere legale in un identificatore. C'è qualche ragione particolare per cui vuoi definire un metodo con un nome illegale? –

+0

@engineersmnky Non è rotaie! tutto restituisce semplicemente una serie di 'Prodotti' per il sistema di inventario di un negozio di giocattoli. È per il progetto finale per il Ruby Nanodegree di Udacity. –

risposta

19

Se ho capito bene la tua domanda, si desidera qualcosa di simile:

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 

(sto supponendo che il metodo restituisce un all Enu . Susseguiti)

Quanto sopra è più o meno equivalente a definire due metodi di classe in questo modo:

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

Questo funziona per me ma ... Puoi spiegare come sei arrivato qui? Ho cercato 'class << self' e ho visto che apre una classe e cambia il suo comportamento ma, perché dovrei farlo all'interno della classe stessa? –

+1

È necessario farlo perché si desidera definire un metodo di classe. Senza di ciò, 'define_method' definirà un metodo di istanza e' define_method ("self.foo ")' come nel tuo esempio non funziona. –

+0

Grazie mille! Lo guarderò più per essere più a mio agio con Metaprogramming ma per ora mi hai aiutato molto :) :) –

1

E se leggete gli esempi qui http://apidock.com/ruby/Module/define_method troverete questo uno:

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

è lo stesso di

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

Questo continua a darmi un NoMethodError:/ –

1

Quando si esegue questa operazione: define_method("self.find_by_#{attribute}")

che non è corretto. L'argomento per define_method è un simbolo con una singola parola.

Mi permetta di mostrare un po 'di codice corretto, speriamo che questo sarà chiaro:

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

Questo produrrà metodi della classe per find_by_brand e find_by_name.

Nota che se stai cercando metaprogramming, questo è un buon caso d'uso per method_missing. here's a tutorial utilizzare method_missing per implementare la stessa funzionalità desiderata (find_by_<x>)

+2

Se questo è binari di questo è un esercizio inutile poiché' find_by_XXX' è anche definito 'define_method' può accettare una stringa senza problema – engineersmnky

+0

' method_missing' è qualcosa che odio Non esiste un modo semplice per trovare metodi definiti in questo modo .Evitare se possibile – akostadinov

+0

@akostadinov concordato –