Methodenaufrufe

Das Aufrufen einer Methode sendet eine Nachricht an ein Objekt, damit dieses eine bestimmte Aufgabe ausführen kann.

In Ruby senden Sie eine Nachricht an ein Objekt wie folgt:

my_method()

Beachten Sie, dass die Klammern optional sind.

my_method

Außer wenn es einen Unterschied zwischen der Verwendung und dem Weglassen von Klammern gibt, verwendet dieses Dokument Klammern, wenn Argumente vorhanden sind, um Verwirrung zu vermeiden.

Dieser Abschnitt behandelt nur das Aufrufen von Methoden. Siehe auch die Syntaxdokumentation zum Definieren von Methoden.

Empfänger

self ist der Standardempfänger. Wenn Sie keinen Empfänger angeben, wird self verwendet. Um einen Empfänger anzugeben, verwenden Sie ..

my_object.my_method

Dies sendet die Nachricht my_method an my_object. Jedes Objekt kann ein Empfänger sein, aber abhängig von der Sichtbarkeit der Methode kann das Senden einer Nachricht einen NoMethodError auslösen.

Sie können auch :: verwenden, um einen Empfänger zu bezeichnen, dies wird jedoch aufgrund der Verwechslungsgefahr mit :: für Namespaces selten verwendet.

Methodenaufrufe verketten

Sie können Methodenaufrufe „verkettet“, indem Sie einen Methodenaufruf sofort mit einem weiteren fortsetzen.

Dieses Beispiel verkettet die Methoden Array#append und Array#compact.

a = [:foo, 'bar', 2]
a1 = [:baz, nil, :bam, nil]
a2 = a.append(*a1).compact
a2 # => [:foo, "bar", 2, :baz, :bam]

Details

Sie können Methoden verketten, die sich in verschiedenen Klassen befinden. Dieses Beispiel verkettet die Methoden Hash#to_a und Array#reverse.

h = {foo: 0, bar: 1, baz: 2}
h.to_a.reverse # => [[:baz, 2], [:bar, 1], [:foo, 0]]

Details

Sicherer Navigationsoperator

&., der „sichere Navigationsoperator“ genannt wird, ermöglicht das Überspringen eines Methodenaufrufs, wenn der Empfänger nil ist. Er gibt nil zurück und wertet die Argumente der Methode nicht aus, wenn der Aufruf übersprungen wird.

REGEX = /(ruby) is (\w+)/i
"Ruby is awesome!".match(REGEX).values_at(1, 2)
# => ["Ruby", "awesome"]
"Python is fascinating!".match(REGEX).values_at(1, 2)
# NoMethodError: undefined method `values_at' for nil:NilClass
"Python is fascinating!".match(REGEX)&.values_at(1, 2)
# => nil

Dies ermöglicht die einfache Verkettung von Methoden, die leere Werte zurückgeben könnten. Beachten Sie, dass &. nur den nächsten Aufruf überspringt. Für eine längere Kette ist es notwendig, den Operator auf jeder Ebene hinzuzufügen.

"Python is fascinating!".match(REGEX)&.values_at(1, 2).join(' - ')
# NoMethodError: undefined method `join' for nil:NilClass
"Python is fascinating!".match(REGEX)&.values_at(1, 2)&.join(' - ')
# => nil

Argumente

Beim Senden einer Nachricht gibt es drei Arten von Argumenten: Positionsargumente, Schlüsselwortargumente (oder benannte Argumente) und das Blockargument. Jede gesendete Nachricht kann eine, zwei oder alle Arten von Argumenten verwenden, aber die Argumente müssen in dieser Reihenfolge angegeben werden.

Alle Argumente in Ruby werden per Referenz übergeben und nicht verzögert ausgewertet.

Jedes Argument wird durch ein , getrennt.

my_method(1, '2', :three)

Argumente können ein Ausdruck, ein Hash-Argument sein

'key' => value

oder ein Schlüsselwortargument.

key: value

Hash und Schlüsselwortargumente müssen zusammenhängend sein und nach allen Positionsargumenten stehen, können aber gemischt werden.

my_method('a' => 1, b: 2, 'c' => 3)

Positionsargumente

Die Positionsargumente für die Nachricht folgen dem Methodennamen.

my_method(argument1, argument2)

In vielen Fällen sind Klammern beim Senden einer Nachricht nicht notwendig.

my_method argument1, argument2

Klammern sind jedoch erforderlich, um Mehrdeutigkeit zu vermeiden. Dies löst einen SyntaxError aus, da Ruby nicht weiß, an welche Methode argument3 gesendet werden soll.

method_one argument1, method_two argument2, argument3

Wenn die Methodendefinition ein *argument hat, werden zusätzliche Positionsargumente in argument als Array in der Methode zugewiesen.

Wenn die Methodendefinition keine Schlüsselwortargumente enthält, werden die Schlüsselwort- oder Hash-ähnlichen Argumente als einzelner Hash dem letzten Argument zugewiesen.

def my_method(options)
  p options
end

my_method('a' => 1, b: 2) # prints: {'a'=>1, :b=>2}

Wenn zu viele Positionsargumente angegeben werden, wird ein ArgumentError ausgelöst.

Standard-Positionsargumente

Wenn die Methode Standardargumente definiert, müssen Sie nicht alle Argumente an die Methode übergeben. Ruby füllt die fehlenden Argumente der Reihe nach auf.

Zuerst behandeln wir den einfachen Fall, bei dem die Standardargumente auf der rechten Seite stehen. Betrachten Sie diese Methode:

def my_method(a, b, c = 3, d = 4)
  p [a, b, c, d]
end

Hier haben c und d Standardwerte, die Ruby für Sie anwendet. Wenn Sie nur zwei Argumente an diese Methode senden:

my_method(1, 2)

Sie sehen, wie Ruby [1, 2, 3, 4] ausgibt.

Wenn Sie drei Argumente senden:

my_method(1, 2, 5)

Sie sehen, wie Ruby [1, 2, 5, 4] ausgibt.

Ruby füllt die fehlenden Argumente von links nach rechts auf.

Ruby erlaubt Standardwerte in der Mitte von Positionsargumenten. Betrachten Sie diese kompliziertere Methode:

def my_method(a, b = 2, c = 3, d)
  p [a, b, c, d]
end

Hier haben b und c Standardwerte. Wenn Sie nur zwei Argumente an diese Methode senden:

my_method(1, 4)

Sie sehen, wie Ruby [1, 2, 3, 4] ausgibt.

Wenn Sie drei Argumente senden:

my_method(1, 5, 6)

Sie sehen, wie Ruby [1, 5, 3, 6] ausgibt.

Dies in Worte zu fassen, wird kompliziert und verwirrend. Ich werde es stattdessen in Variablen und Werten beschreiben.

Zuerst wird 1 an a zugewiesen, dann wird 6 an d zugewiesen. Dadurch bleiben nur die Argumente mit Standardwerten übrig. Da 5 noch keinem Wert zugewiesen wurde, wird es b gegeben und c verwendet seinen Standardwert von 3.

Schlüsselwortargumente

Schlüsselwortargumente folgen allen Positionsargumenten und werden wie Positionsargumente durch Kommas getrennt.

my_method(positional1, keyword1: value1, keyword2: value2)

Alle nicht angegebenen Schlüsselwortargumente verwenden den Standardwert aus der Methodendefinition. Wenn ein Schlüsselwortargument angegeben wird, das die Methode nicht aufgelistet hat, und die Methodendefinition keine beliebigen Schlüsselwortargumente akzeptiert, wird ein ArgumentError ausgelöst.

Der Wert eines Schlüsselwortarguments kann weggelassen werden, was bedeutet, dass der Wert aus dem Kontext anhand des Namens des Schlüssels bezogen wird.

keyword1 = 'some value'
my_method(positional1, keyword1:)
# ...is the same as
my_method(positional1, keyword1: keyword1)

Seien Sie sich bewusst, dass die Parsing-Reihenfolge unerwartet sein kann, wenn auch die Methodenkopfklammern weggelassen werden.

my_method positional1, keyword1:

some_other_expression

# ...is actually parsed as
my_method(positional1, keyword1: some_other_expression)

Blockargument

Das Blockargument sendet eine Closure aus dem aufrufenden Gültigkeitsbereich an die Methode.

Das Blockargument steht beim Senden einer Nachricht an eine Methode immer zuletzt. Ein Block wird mit do ... end oder { ... } an eine Methode gesendet.

my_method do
  # ...
end

oder

my_method {
  # ...
}

do end hat eine niedrigere Priorität als { }, also

method_1 method_2 {
  # ...
}

sendet den Block an method_2, während

method_1 method_2 do
  # ...
end

sendet den Block an method_1. Beachten Sie, dass in beiden Fällen, wenn Klammern verwendet werden, der Block an method_1 gesendet wird.

Ein Block kann Argumente von der Methode akzeptieren, an die er gesendet wurde. Argumente werden ähnlich wie eine Methode Argumente definiert. Die Argumente des Blocks stehen in | ... | nach dem öffnenden do oder {.

my_method do |argument1, argument2|
  # ...
end

Block-lokale Argumente

Sie können auch Block-lokale Argumente für einen Block deklarieren, indem Sie ; in der Liste der Blockargumente verwenden. Die Zuweisung an ein Block-lokales Argument überschreibt keine lokalen Argumente außerhalb des Blocks im Gültigkeitsbereich des Aufrufers.

def my_method
  yield self
end

place = "world"

my_method do |obj; place|
  place = "block"
  puts "hello #{obj} this is #{place}"
end

puts "place is: #{place}"

Dies gibt aus:

hello main this is block
place is: world

Die Variable place im Block ist also nicht dieselbe Variable place wie außerhalb des Blocks. Wenn Sie ; place aus den Blockargumenten entfernen, erhalten Sie dieses Ergebnis:

hello main this is block
place is: block

Positionsargumente entpacken

Angenommen, die folgende Methode:

def my_method(argument1, argument2, argument3)
end

Sie können ein Array mit dem *-Operator (oder Splat-Operator) in eine Argumentliste umwandeln.

arguments = [1, 2, 3]
my_method(*arguments)

oder

arguments = [2, 3]
my_method(1, *arguments)

Beides ist äquivalent zu:

my_method(1, 2, 3)

Der *-Entpackungsoperator kann auf jedes Objekt angewendet werden, nicht nur auf Arrays. Wenn das Objekt eine to_a-Methode unterstützt, wird diese Methode aufgerufen und sollte ein Array zurückgeben. Die Elemente dieses Arrays werden als separate Positionsargumente übergeben.

class Name
  def initialize(name)
    @name = name
  end

  def to_a = @name.split(' ')
end

name = Name.new('Jane Doe')
p(*name)
# prints separate values:
#   Jane
#   Doe

Wenn das Objekt keine to_a-Methode hat, wird das Objekt selbst als ein Argument übergeben.

class Name
  def initialize(name)
    @name = name
  end
end

name = Name.new('Jane Doe')
p(*name)
# Prints the object itself:
#   #<Name:0x00007f9d07bca650 @name="Jane Doe">

Dies ermöglicht die polymorphe Behandlung eines oder mehrerer Argumente. Beachten Sie auch, dass *nil zu einer leeren Liste von Argumenten entpackt wird, so dass bedingtes Entpacken möglich ist.

my_method(*(some_arguments if some_condition?))

Wenn die to_a-Methode existiert und kein Array zurückgibt, führt dies beim Entpacken zu einem Fehler.

class Name
  def initialize(name)
    @name = name
  end

  def to_a = @name
end

name = Name.new('Jane Doe')
p(*name)
#  can't convert Name to Array (Name#to_a gives String) (TypeError)

Sie können auch ** (wie nachfolgend beschrieben) verwenden, um ein Hash in Schlüsselwortargumente umzuwandeln.

Wenn die Anzahl der Objekte im Array nicht mit der Anzahl der Argumente für die Methode übereinstimmt, wird ein ArgumentError ausgelöst.

Wenn der Splat-Operator zuerst im Aufruf steht, müssen Klammern verwendet werden, um eine Mehrdeutigkeit der Interpretation als Entpackungsoperator oder Multiplikationsoperator zu vermeiden. In diesem Fall gibt Ruby in einem ausführlichen Modus eine Warnung aus.

my_method *arguments  # warning: '*' interpreted as argument prefix
my_method(*arguments) # no warning

Schlüsselwortargumente entpacken

Angenommen, die folgende Methode:

def my_method(first: 1, second: 2, third: 3)
end

Sie können ein Hash mit dem **-Operator (Keyword Splat) in Schlüsselwortargumente umwandeln.

arguments = { first: 3, second: 4, third: 5 }
my_method(**arguments)

oder

arguments = { first: 3, second: 4 }
my_method(third: 5, **arguments)

Beides ist äquivalent zu:

my_method(first: 3, second: 4, third: 5)

Der **-Entpackungsoperator kann auf jedes Objekt angewendet werden, nicht nur auf Hashes. Wenn das Objekt eine to_hash-Methode unterstützt, wird diese Methode aufgerufen und sollte ein Hash zurückgeben. Die Elemente dieses Hashs werden als Schlüsselwortargumente übergeben.

class Name
  def initialize(name)
    @name = name
  end

  def to_hash = {first: @name.split(' ').first, last: @name.split(' ').last}
end

name = Name.new('Jane Doe')
p(**name)
# Prints: {name: "Jane", last: "Doe"}

Im Gegensatz zum *-Operator löst ** einen Fehler aus, wenn es auf einem Objekt verwendet wird, das nicht auf to_hash reagiert. Die einzige Ausnahme ist nil, das diese Methode nicht explizit definiert, aber dennoch für die **-Entpackung verwendet werden darf, ohne zusätzliche Schlüsselwortargumente hinzuzufügen.

Auch dies ermöglicht bedingtes Entpacken.

my_method(some: params, **(some_extra_params if pass_extra_params?))

Wie der *-Operator löst ** einen Fehler aus, wenn das Objekt auf to_hash reagiert, aber kein Hash zurückgibt.

Wenn die Methodendefinition den Keyword-Splat-Operator verwendet, um beliebige Schlüsselwortargumente zu sammeln, werden diese nicht von * gesammelt.

def my_method(*a, **kw)
  p arguments: a, keywords: kw
end

my_method(1, 2, '3' => 4, five: 6)

Gibt aus:

{:arguments=>[1, 2], :keywords=>{'3'=>4, :five=>6}}

Proc zu Block-Konvertierung

Gegeben sei eine Methode, die einen Block verwendet:

def my_method
  yield self
end

Sie können einen Proc oder ein Lambda mit dem &-Operator (Blockkonvertierung) in ein Blockargument umwandeln.

argument = proc { |a| puts "#{a.inspect} was yielded" }

my_method(&argument)

Wenn der Blockkonvertierungsoperator zuerst im Aufruf steht, müssen Klammern verwendet werden, um eine Warnung zu vermeiden.

my_method &argument  # warning
my_method(&argument) # no warning

Methoden-Lookup

Wenn Sie eine Nachricht senden, sucht Ruby nach der Methode, die dem Namen der Nachricht für den Empfänger entspricht. Methoden werden in Klassen und Modulen gespeichert, sodass der Methoden-Lookup diese durchläuft, nicht die Objekte selbst.

Hier ist die Reihenfolge des Methoden-Lookups für die Klasse oder das Modul R des Empfängers:

Wenn R eine Klasse mit einer Oberklasse ist, wird dies mit der Oberklasse von R wiederholt, bis eine Methode gefunden wird.

Sobald eine Übereinstimmung gefunden wurde, stoppt der Methoden-Lookup.

Wenn keine Übereinstimmung gefunden wird, wiederholt sich dies von vorne, sucht aber nach method_missing. Die Standard-method_missing ist BasicObject#method_missing, die einen NameError auslöst, wenn sie aufgerufen wird.

Wenn Verfeinerungen (ein experimentelles Feature) aktiv sind, ändert sich der Methoden-Lookup. Weitere Details finden Sie in der Dokumentation zu Verfeinerungen.