web-dev-qa-db-fra.com

Comment passez-vous les arguments à define_method?

Je voudrais passer un argument à une méthode définie avec define_method, comment pourrais-je le faire?

150
Sixty4Bit

Le bloc que vous transmettez à define_method peut inclure certains paramètres. C'est comme ça que votre méthode définie accepte les arguments. Lorsque vous définissez une méthode, vous ne faites que nommer le bloc et conserver une référence à celui-ci dans la classe. Les paramètres viennent avec le bloc. Alors:

define_method(:say_hi) { |other| puts "Hi, " + other }
188
Kevin Conner

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

 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

Mettre à jour

Ruby 2.0 introduit la double éclaboussure ** (deux étoiles) qui ( je cite ) fait:

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

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

 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}

Exemple d'attributs nommés:

 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}

J'essayais de créer un exemple avec un mot clé argument, 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 y avoir une limitation. Quand vous y réfléchissez, cela a du sens, car l'opérateur splat "capture tous les arguments restants" et le double splat "capture tous les arguments de mot clé restants", ce qui aurait pour effet de briser la logique attendue. (Je n'ai aucune référence pour prouver ce point, doh!)

mise à jour 2018 août:

Article de synthèse: https://blog.eq8.eu/til/metaprogramming-Ruby-examples.html

84
equivalent8

Outre la réponse de Kevin Conner: les arguments de bloc ne prennent pas en charge la même sémantique que les arguments de méthode. Vous ne pouvez pas définir des arguments par défaut ou des 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 complète des arguments de la méthode.

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' }
59
Jörg W Mittag

Avec 2.2, vous pouvez maintenant utiliser des arguments de mots clés: https://robots.thoughtbot.com/Ruby-2-keyword-arguments

define_method(:method) do |refresh: false|
  ..........
end
7
akostadinov