Respuestas:
El bloque que pasa a define_method puede incluir algunos parámetros. Así es como su método definido acepta argumentos. Cuando define un método, en realidad solo está apodando el bloque y manteniendo una referencia a él en la clase. Los parámetros vienen con el bloque. Entonces:
define_method(:say_hi) { |other| puts "Hi, " + other }
... y si quieres parámetros opcionales
class Bar
define_method(:foo) do |arg=nil|
arg
end
end
a = Bar.new
a.foo
#=> nil
a.foo 1
# => 1
... tantos argumentos como quieras
class Bar
define_method(:foo) do |*arg|
arg
end
end
a = Bar.new
a.foo
#=> []
a.foo 1
# => [1]
a.foo 1, 2 , 'AAA'
# => [1, 2, 'AAA']
...combinación de
class Bar
define_method(:foo) do |bubla,*arg|
p bubla
p arg
end
end
a = Bar.new
a.foo
#=> wrong number of arguments (0 for 1)
a.foo 1
# 1
# []
a.foo 1, 2 ,3 ,4
# 1
# [2,3,4]
... todos ellos
class Bar
define_method(:foo) do |variable1, variable2,*arg, &block|
p variable1
p variable2
p arg
p block.inspect
end
end
a = Bar.new
a.foo :one, 'two', :three, 4, 5 do
'six'
end
Actualizar
Ruby 2.0 introdujo doble splat **
(dos estrellas) que ( cito ) hace:
Ruby 2.0 introdujo argumentos de palabras clave, y ** actúa como *, pero para argumentos de palabras clave. Devuelve un hash con pares clave / valor.
... y, por supuesto, también puedes usarlo en el método de definición :)
class Bar
define_method(:foo) do |variable1, variable2,*arg,**options, &block|
p variable1
p variable2
p arg
p options
p block.inspect
end
end
a = Bar.new
a.foo :one, 'two', :three, 4, 5, ruby: 'is awesome', foo: :bar do
'six'
end
# :one
# "two"
# [:three, 4, 5]
# {:ruby=>"is awesome", :foo=>:bar}
Ejemplo de atributos con nombre:
class Bar
define_method(:foo) do |variable1, color: 'blue', **other_options, &block|
p variable1
p color
p other_options
p block.inspect
end
end
a = Bar.new
a.foo :one, color: 'red', ruby: 'is awesome', foo: :bar do
'six'
end
# :one
# "red"
# {:ruby=>"is awesome", :foo=>:bar}
Intenté crear un ejemplo con argumento de palabra clave, splat y splat doble, todo en uno:
define_method(:foo) do |variable1, variable2,*arg, i_will_not: 'work', **options, &block|
# ...
o
define_method(:foo) do |variable1, variable2, i_will_not: 'work', *arg, **options, &block|
# ...
... pero esto no funcionará, parece que hay una limitación. Cuando lo piensa, tiene sentido ya que el operador splat está "capturando todos los argumentos restantes" y el splat doble está "capturando todos los argumentos de palabras clave restantes", por lo tanto, mezclarlos rompería la lógica esperada. (¡No tengo ninguna referencia para probar este punto, doh!)
actualización 2018 agosto:
Artículo resumen: https://blog.eq8.eu/til/metaprogramming-ruby-examples.html
a.foo 1
lugar de foo 1
). ¡Gracias!
Además de la respuesta de Kevin Conner: los argumentos de bloque no admiten la misma semántica que los argumentos de método. No puede definir argumentos predeterminados o argumentos de bloque.
Esto solo se soluciona en Ruby 1.9 con la nueva sintaxis alternativa "stabby lambda" que admite la semántica de argumentos de método completo.
Ejemplo:
# Works
def meth(default = :foo, *splat, &block) puts 'Bar'; end
# Doesn't work
define_method :meth { |default = :foo, *splat, &block| puts 'Bar' }
# This works in Ruby 1.9 (modulo typos, I don't actually have it installed)
define_method :meth, ->(default = :foo, *splat, &block) { puts 'Bar' }
Con 2.2 ahora puede usar argumentos de palabras clave: https://robots.thoughtbot.com/ruby-2-keyword-arguments
define_method(:method) do |refresh: false|
..........
end