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?

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 }

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


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

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.