2010-07-10 10 views
12

Definisco dinamicamente un metodo in un modulo e vorrei verificare che una volta che il metodo è associato a un'istanza di classe che il corpo del metodo sia quello che io ' Mi aspetto Esiste un modo per produrre (come testo) il corpo di un metodo?Meta-programmazione: corpo del metodo di output come testo

Modulo controller_mixins.rb:

module ControllerMixin 

    instance_eval "def search_by_vendor (*args) \n" \ 
    " @#{self.class.name.sub(/Controller/, '').tableize} = #{self.class.name.sub(/Controller/, '')}.find_all_by_vendor_id(params[:vendor_id]) \n"\ 
    "respond_to do |format| \n" \ 
    " format.html { render :template=>'/#{self.class.name.sub(/Controller/, '').tableize}/index', :layout=>'vendor_info'} \n" \ 
    " format.xml { render :xml => @#{self.class.name.sub(/Controller/, '').tableize} } \n" \ 
    "end \n"\ 
    "end \n" 

end 

classe di essere mescolato con:

class VendorOrdersController < ApplicationController 
    # GET /vendor_orders 
    # GET /vendor_orders.xml 
    require 'controller_mixins' 
    include ControllerMixin 
<rest of class> 

Così mi piacerebbe vedere l'attuazione del mixin quando applicata a VendorOrdersController probabilmente attraverso script/console per convenienza.

UPDATE: Per @ ~/Ho salvato la stringa in una variabile e puts 'd it. Ha funzionato perfettamente. Che ha portato alla luce un errore nel mio codice (la ragione per cui volevo vedere il codice in primo luogo). Il codice sotto è molto meglio e funziona come previsto.

module ControllerMixin 

    def self.included(mod) 
    method_body = "def search_by_vendor \n" \ 
     " @#{mod.name.sub(/Controller/, '').tableize} = #{mod.name.sub(/Controller/, '')}.find_all_by_vendor_id(params[:vendor_id]) \n"\ 
     "respond_to do |format| \n" \ 
     " format.html { render :template=>'/#{mod.name.sub(/Controller/, '').tableize}/index', :layout=>'vendor_info'} \n" \ 
     " format.xml { render :xml => @#{mod.name.sub(/Controller/, '').tableize} } \n" \ 
     "end \n"\ 
    "end \n" 

    puts method_body 
    mod.class_eval(method_body) 
    end 

end 
+0

define_method è il tuo amico – shingara

+0

Probabilmente vorrai usare 'here documents' ('CODICE << - ... CODE') invece di stringhe concatenate. Il formato è molto più bello. – Chubas

+0

Perché non ho sentito parlare di questi "documenti qui" Questo è un invio di DIO. Grazie. Buona spiegazione (sul blog di qualcun altro): http://blog.commonthread.com/2007/12/20/tip-ruby-here-document – SooDesuNe

risposta

5

No, non è possibile ottenere il codice sorgente dietro un metodo.

Il meglio che si può fare è ottenere l'oggetto Method che rappresenta il metodo utilizzando Object#method. Per esempio:

m = VendorOrdersController.method(:search_by_vendor) 

Ma troverete che non c'è molto di più di un Method#name, Method#arity, Method#source_location, ecc

Nel tuo caso però, perché non è sufficiente memorizzare la stringa in una variabile, stampa esso, prima di utilizzare instance_eval?

Indipendentemente, il tuo instance_eval verrà eseguito al momento della dichiarazione del modulo. Probabilmente vorrai metterlo in una callback included per farlo eseguire al momento dell'inclusione.

module ControllerMixin 
    def self.included(mod) 
    mod.instance_eval([...]) 
    end 
end 
+0

Scusa Shtééf, ho appena notato che hai fatto lo stesso punto nel bel mezzo della tua risposta . Per il momento lascerò il mio perché a qualcun altro potrebbe mancarlo come ho fatto io. – Joc

+1

Non si può eseguire '.method (: search_by_vendor) .source'? – tekknolagi

2

Il modo migliore per ottenere risultati ottimali è ... scrivere un test.

Inoltre, non approvo l'utilizzo di instance_eval come quello. Se DEVI utilizzare metaprogrammi del genere, usa define_method, oppure potresti andartene senza fare nulla passando un parametro dai percorsi, certo, è un po 'più digitante, ma la metaprogrammazione è solo icky.

+0

Sembra che define_method promuova fondamentalmente un blocco per un metodo. Dato che non conoscerò il nome del mio metodo o le variabili interne, come posso definirlo generalmente come un blocco? – SooDesuNe

+0

@SooDesuNe: Poiché il blocco che si assegna a 'define_method' è una chiusura, si avrà accesso alle variabili locali. Provalo nel tuo caso è banale e sarà ** molto ** più bello. –

2

Non è possibile assegnare la stringa a una variabile prima dell'esecuzione di instance_eval e inviarla alla console?

Poiché la stringa definisce l'intero metodo, in pratica è già presente il codice sorgente.

0

come sopra le risposte dicono, il modo migliore per fare ciò che si sta tentando di fare è con define_method.

se qualcuno è alla ricerca di "Meta-programmazione: corpo metodo di uscita come testo" prova questo:

se si vuole ottenere il codice di una classe o di un metodo di controllare ParseTree e ruby2ruby

ParseTree può prendere l'Abstract Syntax Tree di una classe o un metodo e genera "espressioni simboliche", come la lisp, ha un aspetto simile agli elenchi di liste di simboli ed è molto interessante.

ruby2ruby prende questi s-exp e lo trasforma in un rubino normale.

essere avvisati, ParseTree non funziona attualmente in ruby ​​1.9.

Problemi correlati