假设我有一个User
对象,该对象具有email
属性,我需要他们email
的最后一个字母:
u = User.find(1)
letter = u.email.upcase.last
如果此链中的u
或email
为nil
,那么我会获得NoMethodError: undefined method 'blah' for nil:Nilclass
。在大多数情况下,我应该可以解决它,但有时候,nil
会得到它不应该包含的地方或者很难包含它。一种方法是冗长的:
u = User.find(1)
letter = nil
if u && u.email
letter = u.email.upcase.last
end
但是这在一个视图中,或在a.bunch.of.properties.down.a.hierarchy
的长链中变得烦人和危险。我在Rails中阅读了try
方法:
u = User.find(1)
letter = u.try(:email).try(:upcase).try(:last)
这不那么冗长,但我觉得写这些尝试都很蠢。一旦我将try
放入链中,我就必须一直使用它们。还有更好的方法吗?
答案 0 :(得分:8)
我喜欢使用Null Object Pattern。 Avdi has a great post explaining this,但基本的想法是你有一个小类可以代表一个对象并合理地回应你可能传递原始对象的消息。我发现这些不仅有助于避免NoMethodError
,还有助于设置默认值/好消息。
例如,你可以这样做:
class NilUser
def email
"(no email address)"
end
end
u = User.find(1) || NilUser.new
u.email.upcase.last # => No errors!
答案 1 :(得分:3)
我只是想用另外一个选项来更新这个线程:Ruby now(从2.3开始)为我们提供了一个安全的导航操作符,&.
语法。
所以:
u.email.upcase
会变成:
u.email&.upcase
与Rail的try
方法类似,如果在nil
遇到NoMethodError
,整个链将返回nil
。
答案 2 :(得分:1)
User.find(1)
如果id为1的用户不存在则会引发异常,因此您不必担心这里的
u.email
如果您的模型中有
validates :email, presence: true
您无需担心nil,因为没有电子邮件的用户无法进入数据库
但我认为你问的是在ruby代码中处理nils的一般方法。最近我使用空对象模式
http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/
http://robots.thoughtbot.com/post/20907555103/rails-refactoring-example-introduce-null-object
答案 3 :(得分:0)
您还可以映射查找结果
[User.find(1)].map{|u| (u != nil ? u.mail : "no mail")}[0]