Ich habe Sandi Metz gesehen " nichts ist etwas Präsentation jetzt ein paar Mal. Mir ist klar, dass ich in meinen Rails-Projekten überall keine Überprüfungen durchführe, aber ich weiß nicht, wie ich keine Überprüfungen vermeiden und wie ich es objektorientiert machen soll, wenn es um Assoziationen geht.
Betrachten Sie diese Assoziationen:
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :blog
end
#app/models/blog.rb
class Blog < ActiveRecord::Base
belongs_to :hash_tag
has_one :user
end
#app/models/hash_tag.rb
class HashTag < ActiveRecord::Base
has_one :blog
end
Ich greife nach einem Benutzer:
@user = User.find(1)
Und ich möchte seinen Blog finden:
@user.blog
=> nil
Es kehrt zurück nil
hier weil das user
zufällig nicht zugeordnet zu haben blog
, also der folgende Code würde die Anwendung beschädigen, wenn ich dafür so etwas mache user
:
@user.blog.title
=> undefined method `title" for nil:NilClass
Die einfache Lösung besteht darin, dies nicht objektorientiert zu tun und nur eine Nullprüfung durchzuführen (dies ist jedoch das, was wir vermeiden möchten, da ansonsten Nullprüfungen absolut überall in der Anwendung vorhanden sind):
@user.blog.title if @user.blog
Das Durchführen von Nullprüfungen wird umständlicher und prozeduraler, je tiefer Sie durch die folgenden Assoziationen gehen:
@user = User.find(1)
if @user.blog && @user.blog.hash_tag #checking for nil twice
@user.blog.hash_tag.tag_name
end
Was ist die objektorientierte Methode, um Nullprüfungen für Assoziationen zu vermeiden?
Schienen sind mir bekannt " versuche Methode, obwohl Metz das nicht zu empfehlen schien try
Methode. Vielleicht aber, wenn es um Assoziationen in Schienen geht: try
ist die beste Option?
Antworten:
6 für die Antwort № 1Es gibt eine Programmier-Design-Richtlinie namens Das Gesetz von Demeter
So wenden Sie es auf Ihre Schienenmodelle an:
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :blog
delegate :title, to: :blog, prefix: true, allow_nil: true
# then you can call user.blog_title
delegate :tag_name, to: :blog, prefix: true, allow_nil: true
# follow LoD
end
#app/models/blog.rb
class Blog < ActiveRecord::Base
belongs_to :hash_tag
delegate :tag_name, to: :hash_tag, allow_nil: true
has_one :user
end
Und jetzt können Sie dies tun:
@user = User.find(1)
@user.blog_title # no error even if there is no associated blog
@user.tag_name # no error even if there is no associatd blog or no associated hash_tag object
Bitte lesen Sie den folgenden Link für Referenzen:
- http://apidock.com/rails/Module/delegate
- http://samurails.com/tutorial/rails-delegate-dont-break-the-law-of-demeter/
2 für die Antwort № 2
Es gibt ein schickes Juwel andand
was ich in solchen Fällen benutze. Normalerweise müssten Sie schreiben:
@user.blog && @user.blog.title
andand
gem implementiert ein generisches Nullmuster. andand
erweitert Object um eine Methode andand
, das ein Objekt zurückgibt, für das es aufgerufen wurde, es sei denn, es ist null. Im Fall von nil wird das NullPatern-Objekt zurückgegeben, um nil für alle Methodenaufrufe mit method_missing magic zurückzugeben.
@user.blog.andand.title
Es ist noch leistungsfähiger, wenn Sie mehr Bedingungen überprüfen müssen:
@user.blog && @user.blog.title && @user.blog.title.capitalize
wird:
@user.blog.andand.title.andand.capitalize
1 für die Antwort № 3
Ich denke, das könnte eine Option sein
Ich würde ... benutzen:
rescue
Methode
Vorsicht: Diese Methode fängt Ausnahmen ab und reagiert entsprechend. Verwenden Sie dies nicht, wenn Sie auf Ausnahmen anders reagieren müssen. Ich hoffe, Sie verstehen
@user = User.find(1)
tag_name = @user.blog.hash_tag.tag_name rescue "" # respond with default value
Beispiel:
2.1.1 :001 > var1 = nil
=> nil
2.1.1 :002 > my_val = var1.some_method
NoMethodError: undefined method `some_method" for nil:NilClass
from (irb):2
from /home/shiva/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `<main>"
2.1.1 :003 > my_val = var1.some_method rescue ""
=> ""
Eine andere Option wäre .try
Methode; http://apidock.com/rails/Object/try
.try
ist definiert inRails
also nicht verfügbar sein ausRails
@person.non_existing_method if @person.respond_to?(:non_existing_method)
# instead use
@person.try(:non_existing_method) # => returns nil if does not respond_to
try gibt beim Aufruf von nil nil zurück, unabhängig davon, ob es auf die Methode reagiert:
nil.try(:to_i) # => nil, rather than 0