Comment passez-vous les arguments à define_method?
-
01-07-2019 - |
Question
Je voudrais passer un argument à une méthode définie avec define_method, comment ferais-je cela?
La solution
Le bloc que vous passez à 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. Donc:
define_method(:say_hi) { |other| puts "Hi, " + other }
Autres conseils
... et si vous souhaitez 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 le souhaitez
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 a introduit le double éclatement **
(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 vous pouvez bien sûr l'utiliser aussi 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 double splat "capture tous les arguments de mot clé restants". donc les mélanger briserait 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
En plus de 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 d’arguments par défaut ni d’arguments de blocage.
Ceci n’est corrigé que dans Ruby 1.9 avec la nouvelle alternative "stabby lambda". syntaxe qui prend en charge la sémantique des arguments 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' }
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