Frage: Wann muss ich mich in meinen Modellen in Rails selbst verwenden?
Ich habe eine set
Methode 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.
Antworten:
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.
quelle
self
. Dies gilt auch für das Festlegen von Instanzvariablen (direkt) - tatsächlich können Sie dies nicht mitself
(ohnehin ohne Reflexion) tun .foo = bar
eine 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 mitmethod_missing
. Beachten Sie auch, dass beimself.foo = bar
Aufrufen einer Methode mit dem Namenfoo=
keine Instanzvariable (direkt) festgelegt wird.@var_name
.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_flag
in 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
.active_flag
es im Rahmen vonwhats_my_active_flag
?active_flag
es in diesem Bereich?Wenn Sie jedoch
active_flag
innerhalb von definierenwhats_my_active_flag
und dann danach fragen, werden die folgenden Schritte erneut ausgeführt:active_flag
existiert im Rahmen vonwhats_my_active_flag
?In beiden Fällen wird der Wert von nicht geändert , es
self.active_flag
sei 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.
nil
Daher 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!
quelle
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.
quelle
Wenn Sie
active_flag = val
Ruby verwenden, um eine lokale Variable zu definieren, ist der beste Wegself.active_flag = val
, wenn Sie sie haben, zu hoffen, dass Sie wissen, dass diessend(:active_flag=, val)
auch funktioniert.quelle