Je voudrais passer un argument à une méthode définie avec define_method, comment pourrais-je le faire?
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 }
... 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
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' }
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