2008-09-18 7 views

Répondre

168

Le bloc que vous passez à define_method peut inclure certains paramètres. C'est ainsi que votre méthode définie accepte les arguments. Lorsque vous définissez une méthode, vous ne faites que nickname le bloc et gardez une référence dans la classe. Les paramètres viennent avec le bloc. Alors:

define_method(:say_hi) { |other| puts "Hi, " + other } 
+0

Eh bien, c'est juste une chose de pure Beaty pure. Beau travail, Kevin Costner. – Fuser97381

58

En plus de réponse de Kevin Conner: les arguments de bloc ne supportent pas la même sémantique que les arguments de méthode. Vous ne pouvez pas définir les arguments par défaut ou les arguments de blocage.

Ceci n'est corrigé que dans Ruby 1.9 avec la nouvelle syntaxe alternative "stabby lambda" qui prend en charge la sémantique d'argument de méthode complète.

Exemple:

# 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 fait, je crois que les arguments de bloc sur define_method supportent splat, ce qui peut fournir un moyen de définir des arguments par défaut aussi. – Chinasaur

+1

Chinasaur est correct sur les arguments de bloc permettant des splats. J'ai confirmé cela à la fois dans Ruby 1.8.7 et 1.9.1. –

+0

Merci, j'ai oublié à ce sujet. Fixé maintenant –

74

... et si vous voulez des paramètres facultatifs

class Bar 
    define_method(:foo) do |arg=nil|     
    arg                       
    end 
end 

a = Bar.new 
a.foo 
#=> nil 
a.foo 1 
# => 1 

... autant d'arguments que vous voulez

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'] 

... combinaison 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] 

... tous

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 

Mise à jour

Ruby 2.0 introduit deux floc ** (deux étoiles) qui (I quote) fait:

Ruby 2.0 introduit des arguments clés , et ** agit comme *, mais pour les arguments de mots-clés. Il renvoie un hachage avec des paires clé/valeur.

... et bien sûr, vous pouvez l'utiliser à définir dans la méthode aussi :)

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} 

nommé attributs par exemple:

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} 

je tentais de créer par exemple avec l'argument de mot-clé, splat et double splat tout en un:

define_method(:foo) do |variable1, variable2,*arg, i_will_not: 'work', **options, &block| 
    # ... 

ou

define_method(:foo) do |variable1, variable2, i_will_not: 'work', *arg, **options, &block| 
    # ... 

... mais cela ne fonctionnera pas, il semble qu'il y ait une limitation. Quand vous y réfléchissez, cela prend tout son sens car l'opérateur splat "capture tous les arguments restants" et double splat "capture tous les arguments de mots-clés restants". Par conséquent, les mélanger briserait la logique attendue. (Je n'ai aucune référence pour prouver ce point doh!)

+0

Intéressant - Spécialement le 4ème bloc: il a fonctionné sur 1.8.7! Le premier bloc n'a pas fonctionné en 1.8.7, et le deuxième bloc a une faute de frappe (devrait être 'a.foo 1' au lieu de' foo 1'). Merci! –

+1

merci pour les commentaires, typo a été corrigé, ... Sur ruby ​​1.9.3 et 1.9.2 tous les exemples fonctionne et je suis positif que sur 1.9.1 aussi (mais n'a pas essayé) – equivalent8

+0

J'ai combiné cette réponse avec le réponse acceptée à http://stackoverflow.com/questions/4470108/when-monkey-patching-a-method-can-you-call-the-overridden-method-from-the-new-i pour comprendre comment écraser (pas override) une méthode à l'exécution qui prend des arguments optionnels et un bloc et qui peut toujours appeler la méthode d'origine avec les arguments et le bloc. Ah, rubis. Plus précisément, j'avais besoin de remplacer Savon :: Client.request dans mon dev env pour un seul appel API vers un hôte auquel je ne peux accéder qu'en production. À votre santé! – pduey

Questions connexes