Wie übergeben Sie Argumente an define_method?

155

Ich möchte ein Argument an eine Methode übergeben, die mit define_method definiert wird. Wie würde ich das tun?

Sixty4Bit
quelle

Antworten:

198

Der Block, den Sie an define_method übergeben, kann einige Parameter enthalten. So akzeptiert Ihre definierte Methode Argumente. Wenn Sie eine Methode definieren, benennen Sie den Block wirklich nur mit einem Spitznamen und behalten einen Verweis darauf in der Klasse. Die Parameter werden mit dem Block geliefert. So:

define_method(:say_hi) { |other| puts "Hi, " + other }
Kevin Conner
quelle
Nun, das ist nur eine Sache von unverfälschtem Beaty. Gute Arbeit, Kevin Costner.
Darth Egregious
90

... und wenn Sie optionale Parameter wünschen

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

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

... so viele Argumente wie Sie wollen

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

...Kombination von

 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]

... alle von ihnen

 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

Aktualisieren

Ruby 2.0 hat Double Splat **(zwei Sterne) eingeführt, was ( ich zitiere ) Folgendes bewirkt:

Ruby 2.0 führte Schlüsselwortargumente ein und ** verhält sich wie *, jedoch für Schlüsselwortargumente. Es gibt einen Hash mit Schlüssel / Wert-Paaren zurück.

... und natürlich kannst du es auch in der Methode define verwenden :)

 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}

Beispiel für benannte Attribute:

 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}

Ich habe versucht, ein Beispiel mit Schlüsselwortargument, Splat und Double Splat in einem zu erstellen:

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

oder

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

... aber das wird nicht funktionieren, es sieht so aus, als gäbe es eine Einschränkung. Wenn Sie darüber nachdenken, ist dies sinnvoll, da der splat-Operator "alle verbleibenden Argumente erfasst" und double splat "alle verbleibenden Schlüsselwortargumente erfasst", sodass das Mischen dieser Argumente die erwartete Logik brechen würde. (Ich habe keinen Hinweis, um diesen Punkt zu beweisen, doh!)

Update 2018 August:

Zusammenfassender Artikel: https://blog.eq8.eu/til/metaprogramming-ruby-examples.html

Äquivalent8
quelle
Interessant - speziell der 4. Block: es hat am 1.8.7 funktioniert! Der erste Block hat in 1.8.7 nicht funktioniert, und der zweite Block hat einen Tippfehler (sollte a.foo 1anstelle von sein foo 1). Vielen Dank!
Sony Santos
1
danke für das Feedback, Tippfehler wurde behoben, ... Auf Ruby 1.9.3 und 1.9.2 funktionieren alle Beispiele und ich bin mir sicher, dass auch auf 1.9.1 (aber nicht versucht)
Äquivalent8
Ich habe diese Antwort mit der akzeptierten Antwort unter stackoverflow.com/questions/4470108/… kombiniert, um herauszufinden, wie eine Methode zur Laufzeit überschrieben (nicht überschrieben) werden kann, die optionale Argumente und einen Block verwendet und dennoch die ursprüngliche Methode mit den Argumenten aufrufen kann und blockieren. Ah, Rubin. Insbesondere musste ich Savon :: Client.request in meiner Entwicklungsumgebung für einen einzelnen API-Aufruf an einen Host überschreiben, auf den ich nur in der Produktion zugreifen kann. Prost!
pduey
59

Zusätzlich zu Kevin Conners Antwort: Blockargumente unterstützen nicht dieselbe Semantik wie Methodenargumente. Sie können keine Standardargumente definieren oder Argumente blockieren.

Dies wird nur in Ruby 1.9 mit der neuen alternativen Syntax "stabby lambda" behoben, die die vollständige Semantik von Methodenargumenten unterstützt.

Beispiel:

# 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' }
Jörg W Mittag
quelle
3
Eigentlich glaube ich, dass Blockargumente auf define_method Splat unterstützen, was eine Rundum-Möglichkeit bietet, auch Standardargumente zu definieren.
Chinasaurier
1
Chinasaur hat Recht mit Blockargumenten, die Splats zulassen. Ich habe dies sowohl in Ruby 1.8.7 als auch in 1.9.1 bestätigt.
Peter Wagenet
Danke, das habe ich vergessen. Jetzt behoben.
Jörg W Mittag