2011-09-27 15 views
8

Sé que puedo definir dinámicamente métodos en una clase usando define_method, y que especifico los parámetros que toma este método usando la aridad del bloque.¿Puedo definir dinámicamente un método de Ruby que tome un bloque?

Quiero definir dinámicamente un método que acepte tanto parámetros opcionales como un bloque. En Ruby 1.9, esto es fácil porque ahora se permite pasar un bloque a un bloque.

Desafortunadamente, Ruby 1.8 no permite esto, por lo que el siguiente no funcionará:

#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 

Sustitución de la explícita block.call con yield no soluciona el problema.
Desafortunadamente, la actualización a Ruby 1.9 no es una opción para mí. ¿Es este un problema difícil de resolver, o hay una forma de evitarlo?

Respuesta

6

Esto funciona con Ruby 1.8.7, 1.8.6, pero no:

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

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

da:

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

(1.8.6 con él da syntax error, unexpected tAMPER, expecting '|'.)

Si desea argumentos opcionales, así como bloque, se podría intentar algo como esto:

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 

prueba con:

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

da:

default 
default 
    No arg but block 
4

Lo que podría hacer es usar class_eval con una cadena en lugar de define_method. La desventaja de esto (aparte de no ser tan elegante) es que pierdes el alcance léxico. Pero esto a menudo no es necesario.

+0

¿Es necesario pasar una cadena para class_eval? ¿No puedo pasar un bloque, lo que mantendría el alcance? – andrewdotnich

+2

@andrewdotnich Bien, entonces vuelves al problema de cómo vas a definir el método dinámicamente. –

+1

https://github.com/agrimm/zombie-chaser/blob/master/lib/zombie-chaser/chaser.rb tiene un ejemplo y un enlace a una publicación de blog a la mitad del archivo. –

Cuestiones relacionadas