2011-09-27 16 views
8

So che posso definire dinamicamente i metodi su una classe usando define_method e che specifichi i parametri che questo metodo impiega usando l'arità del blocco.Posso definire dinamicamente un metodo Ruby che accetta un blocco?

Voglio definire dinamicamente un metodo che accetta sia parametri opzionali che un blocco. In Ruby 1.9, questo è facile perché ora è consentito passare un blocco a un blocco.

Purtroppo, Ruby 1.8 non consente questo, quindi il seguente non funziona:

#Ruby 1.8 
class X 
    define_method :foo do |bar, &baz| 
    puts bar 
    baz.call if block_given? 
    end 
end 

x = X.new 
x.foo("foo") { puts "called!"} #=> LocalJumpError: no block given 

Sostituzione della esplicita block.call con yield non risolve il problema.
L'aggiornamento a Ruby 1.9 non è purtroppo un'opzione per me. È un problema intrattabile o esiste un modo per aggirarlo?

risposta

6

questo funziona con Ruby 1.8.7, ma non 1.8.6:

class X 
    define_method(:foo) do |bar, &baz| 
    puts bar 
    baz.call if baz 
    end 
end 

Test con:

X.new.foo("No block") 
X.new.foo("With block") { puts " In the block!"} 
p = proc {puts " In the proc!"} 
X.new.foo("With proc", &p) 

dà:

No block 
With block 
    In the block! 
With proc 
    In the proc! 

(con 1.8.6 esso dà syntax error, unexpected tAMPER, expecting '|'.)

Se si desidera argomenti opzionali, nonché blocco, si potrebbe provare qualcosa di simile:

class X 
    define_method(:foo) do |*args, &baz| 
    if args[0] 
     bar = args[0] 
    else 
     bar = "default" 
    end 
    puts bar 
    baz.call if baz 
    end 
end 

test con:

X.new.foo 
X.new.foo { puts " No arg but block"} 

dà:

default 
default 
    No arg but block 
4

Quello che si potrebbe fare è utilizzare class_eval con una stringa anziché define_method. Lo svantaggio di questo (a parte non essere elegante) è che perdi lo scoping lessicale. Ma spesso non è necessario.

+0

Ho bisogno di passare una stringa a class_eval? Non posso passare un blocco, che manterrebbe l'ambito? – andrewdotnich

+2

@andrewdotnich Bene, allora sei tornato al problema di come definirai il metodo in modo dinamico. –

+1

https://github.com/agrimm/zombie-chaser/blob/master/lib/zombie-chaser/chaser.rb ha un esempio e un collegamento a un post del blog a metà del file. –

Problemi correlati