Wann soll Self in Model verwendet werden?

74

Frage: Wann muss ich mich in meinen Modellen in Rails selbst verwenden?

Ich habe eine setMethode in einem meiner Modelle.

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    self.active_flag = val
    self.save!
  end
end

Wenn ich das mache, funktioniert alles gut. Wenn ich dies jedoch tue:

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
    save!
  end
end

Der Wert für active_flag ändert sich nicht, sondern schlägt stillschweigend fehl. Kann jemand erklären?

Ich kann keine Duplikate finden, aber wenn jemand eines findet, ist das auch in Ordnung.

varatis
quelle
Mögliches Duplikat von Wann soll 'self' in Ruby verwendet werden
?

Antworten:

70

Wenn Sie eine Aktion für die Instanz ausführen, die die Methode aufruft, verwenden Sie self.

Mit diesem Code

class SocialData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
    save!
  end
end

Sie definieren eine brandneue lokale Variable mit Gültigkeitsbereich namens active_flag und setzen sie auf den übergebenen Wert. Sie ist mit nichts verknüpft. Sie wird daher sofort weggeworfen, wenn die Methode endet, als ob sie nie existiert hätte.

self.active_flag = val

Weist die Instanz jedoch an, anstelle einer brandneuen Variablen ein eigenes Attribut namens active_flag zu ändern. Deshalb funktioniert es.

DVG
quelle
8
"Wenn Sie eine Aktion für die Instanz ausführen, die die Methode aufruft, verwenden Sie self." Das ist nicht ganz richtig. Das Aufrufen einer (Nicht-Setter-) Methode für das aktuelle Objekt funktioniert ohne self. Dies gilt auch für das Festlegen von Instanzvariablen (direkt) - tatsächlich können Sie dies nicht mit self(ohnehin ohne Reflexion) tun .
sepp2k
Wie seltsam, dass es nicht auf die Instanzvariable verweist ... ein weiterer Unterschied zu Java. Danke für die Antwort.
Varatis
3
@varatis: Da Variablen nicht in Ruby deklariert sind, können Sie eine Variable nur erstellen, indem Sie ihr etwas zuweisen. Java kann erkennen, ob foo = bareine lokale Variable oder eine Instanzvariable zugewiesen wird, da es nur prüfen kann, ob eine lokale Variable mit diesem Namen deklariert wurde. Ruby kann das nicht, weil es keine Deklarationen gibt. Sie könnten sagen "Rufen Sie die Setter-Methode auf, falls eine vorhanden ist - andernfalls erstellen Sie eine lokale Variable", aber dann stoßen Sie auf Probleme mit method_missing. Beachten Sie auch, dass beim self.foo = barAufrufen einer Methode mit dem Namen foo=keine Instanzvariable (direkt) festgelegt wird.
sepp2k
1
@varatis Auch Instanzvariablen sind @var_name.
Dave Newton
1
könnte jemand erklären warum
jj_
62

Dies geschieht aufgrund von Scoping. Wenn Sie in einer Methode, und Sie versuchen, setzen Sie eine neue Variable wie folgt aus :

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
  end
end

Sie erstellen eine brandneue Variable, die sich in set_active_flag befindet. Sobald die Ausführung abgeschlossen ist, verschwindet sie und ändert sich self.active_flagin keiner Weise (die eigentliche Instanzvariable).

JEDOCH (dies war eine Quelle der Verwirrung für mich): Wenn Sie versuchen, eine Instanzvariable in Ruby wie folgt zu lesen :

class SomeData < ActiveRecord::Base
  def whats_my_active_flag
    puts active_flag
  end
end

Sie erhalten tatsächlich self.active_flag(die eigentliche Instanzvariable) zurückgegeben.


Hier ist der Grund:

Ruby wird alles tun, um eine Rückkehr zu vermeiden nil.

  1. Zunächst wird gefragt, ob active_flages im Rahmen von whats_my_active_flag?
  2. Es sucht und erkennt die Antwort „nein“, so springt er auf eine Ebene, auf die Instanz von some
  3. Es fragt noch einmal dasselbe: "Existiert active_flages in diesem Bereich?
  4. Die Antwort ist "yup" und so heißt es "Ich habe etwas für dich" und es gibt das zurück!

Wenn Sie jedoch active_flaginnerhalb von definieren whats_my_active_flagund dann danach fragen, werden die folgenden Schritte erneut ausgeführt:

  1. Es fragt " active_flagexistiert im Rahmen von whats_my_active_flag?
  2. Die Antwort lautet "yup", daher wird dieser Wert zurückgegeben

In beiden Fällen wird der Wert von nicht geändert , es self.active_flagsei denn, Sie teilen dies ausdrücklich mit.

Eine einfache Möglichkeit, dieses Verhalten zu beschreiben, besteht darin, "Sie nicht enttäuschen zu wollen" und zurückzukehren. nilDaher bemüht es sich, alles zu finden, was es kann.

Gleichzeitig "möchte es keine Daten durcheinander bringen, die Sie nicht ändern wollten", sodass die Instanzvariable selbst nicht geändert wird.

Hoffe das hilft!

Yuval Karmi
quelle
4
Tolle Erklärung. Ich habe eine Stunde lang gesucht, um herauszufinden, warum ich self zum Einstellen von Werten verwenden muss, kann aber zu verschiedenen Methoden springen und eine scheinbar lokale Variable für Lesevorgänge verwenden, ohne self zu verwenden
bkunzi01
1
Tolle Erklärung! Ich war auch durch den Unterschied verwirrt, als Sie die Variable lesen oder schreiben
Mario
1

Damit stellen Sie sicher, dass Sie die Setter-Methode verwenden und keine neue Variable festlegen. Es handelt sich um ein Ruby- und AR-Verwendungsdetail, das häufig Personen auslöst (das andere ist die (falsche) Verwendung einer Instanzvariablen).

Beachten Sie, dass es bereits update_attributes gibt! obwohl ich den Wunsch zu abstrahieren verstehe.

Es gibt auch umschalten! , was je nach Schnittstelle zur Flagge noch schöner sein könnte.

Dave Newton
quelle
Ich habe hier möglicherweise ein Problem mit baumelnden Modifikatoren. Wollen Sie damit sagen, dass die Tatsache, dass "foo = bar" eine lokale Variable erstellt, ein Detail der AR- Implementierung ist?
sepp2k
@ sepp2k Das würde eigentlich keinen Sinn ergeben, aber meine Bearbeitung war nicht klar.
Dave Newton
0

Wenn Sie active_flag = valRuby verwenden, um eine lokale Variable zu definieren, ist der beste Weg self.active_flag = val, wenn Sie sie haben, zu hoffen, dass Sie wissen, dass dies send(:active_flag=, val)auch funktioniert.

Fangxing
quelle