2008-09-18 24 views

Respuesta

168

El bloque que pase a definir_metodo puede incluir algunos parámetros. Así es como su método definido acepta argumentos. Cuando defines un método, en realidad estás poniendo el sobrenombre al bloque y manteniendo una referencia a él en la clase. Los parámetros vienen con el bloque. Por lo tanto:

+0

Bueno, eso es sólo una cosa de Beaty no adulterada pura. Buen trabajo, Kevin Costner. – Fuser97381

58

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 argumento 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' } 
+3

En realidad, creo que los argumentos de bloque en definir_metodo son compatibles con splat, que también proporciona una forma de redondear una ronda para definir los argumentos predeterminados. – Chinasaur

+1

Chinasaur tiene razón sobre los argumentos de bloque que permiten splats. Lo he confirmado tanto en Ruby 1.8.7 como en 1.9.1. –

+0

Gracias, me olvidé de esto. Corregido ahora. –

74

... 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 que desea

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 

actualización

Rubí 2.0 introdujo doble splat ** (dos estrellas), que (I quote) hace:

Rubí 2.0 introdujo argumentos de palabra clave y ** actúa como *, pero para argumentos de palabra clave. Devuelve un Hash con pares clave/valor.

... y por supuesto que se puede utilizar en el método de definir tambié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} 

atributos con nombre ejemplo:

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} 

yo estaba tratando de crear ejemplo, con argumento de palabra clave, splat y doble splat 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 piense, tiene sentido ya que el operador de splat está "capturando todos los argumentos restantes" y double splat está "capturando todos los argumentos de palabra clave restantes", por lo tanto, mezclarlos rompería la lógica esperada. (No tengo ninguna referencia para probar este punto doh!)

+0

Interesante - especialmente el 4 ° bloque: ¡funcionó en 1.8.7! El primer bloque no funcionó en 1.8.7, y el segundo bloque tiene un error tipográfico (debe ser 'a.foo 1' en lugar de' foo 1'). ¡Gracias! –

+1

gracias por sus comentarios, el error fue corregido, ... En ruby ​​1.9.3 y 1.9.2 todos los ejemplos funcionan y estoy seguro de que en 1.9.1 también (pero no lo intenté) – equivalent8

+0

Combiné esta respuesta con la respuesta aceptada en http://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i para averiguar cómo sobrescribir (no anular) un método en tiempo de ejecución que toma args opcionales y un bloque y aún así puede llamar al método original con args y block. Ah, ruby. Específicamente, necesitaba sobreescribir Savon :: Client.request en mi env env para una única llamada API a un host al que solo puedo acceder en producción. ¡Aclamaciones! – pduey

Cuestiones relacionadas