Usando el rendimiento dentro de define_method en Ruby
metaprogramming (2)
¿Es posible hacer que la palabra clave de rendimiento funcione dentro de un bloque dado a define_method? Ejemplo simple:
class Test
define_method :test do |&b|
puts b # => #<Proc:...>
yield
end
end
Test.new.test {
puts "Hi!"
}
Este código produce el siguiente error en Ruby 1.8.7 y 1.9.0:
test.rb: 4: en `test '': no se proporcionó ningún bloque (LocalJumpError) desde test.rb: 8
Lo extraño es la variable del bloque b != nil
block_given?
pero block_given?
devuelve falso. ¿Es el comportamiento de Ruby intencional no reconocer bloques por objetos Proc ?
Edición: Saludos a la respuesta de b.call()
: b.call()
no es lo que estoy buscando. La variable de bloque se usó solo para indicar que el bloque realmente se da y no se detecta dentro de define_method.
Por lo que necesito usar yield
lugar de block.call
Estoy dispuesto a escribir alguna extensión sobre la forma en que se definen las nuevas clases en Ruby, por lo tanto, cualquier código que pueda escribir en Ruby puro debe ser aceptado cuando uso mi extensión.
Por lo tanto, no se puede tener en cuenta una semántica similar, ya que esto obliga a los usuarios de mi biblioteca a usar solo una forma adecuada de pasar un bloque. Esto rompe la regla TIMTOWTDI y no hace que mi biblioteca sea transparente.
Ejemplo de la vida real
El código a continuación se puede simplificar al código anterior ya que my_def
usa define_method
:
require ''my_library''
class Test
# client can write ''my_def'' instead of ''def'' since
# my_library extends Class class
my_def :test, "some parameter" do
yield # oh no, error :(
end
end
Test.new.test {
puts "Hi!"
}
Creo que esto es lo que estás buscando:
class Test
define_method :test do |&b|
b.call
end
end
Test.new.test {
puts "Hi!"
}
Más información en http://coderrr.wordpress.com/2008/10/29/using-define_method-with-blocks-in-ruby-18/
No puedes usar el yield
dentro de un bloque define_method
. Esto se debe a que los bloques son capturados por cierres, observe:
def hello
define_singleton_method(:bye) { yield }
end
hello { puts "hello!" }
bye { puts "bye!" } #=> "hello!"
No creo que a sus usuarios les importe no poder utilizar el ''rendimiento'' de la forma en que usted lo expresa: la sintaxis no se parece en nada a la sintaxis de definición del método Ruby, por lo que es poco probable que haya confusión.
Más información sobre por qué no puede pasar bloques implícitamente a los métodos que se encuentran aquí: http://banisterfiend.wordpress.com/2010/11/06/behavior-of-yield-in-define_method/