处理零对象和属性的最佳实践是什么?

假设我有一个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了链条,我就必须一直使用它们。 有没有更好的办法?

我喜欢使用Null对象模式 。 Avdi有一篇很好的post解释了这一点 ,但基本的想法是你有一个小类可以代表一个对象并合理地回应你可能传递原始对象的消息。 我发现这些不仅对于避免NoMethodError有用,而且对于设置默认值/好消息也很有用。

例如,你可以这样做:

 class NilUser def email "(no email address)" end end u = User.find(1) || NilUser.new u.email.upcase.last # => No errors! 

我只想用另外一个选项来更新这个线程:现在Ruby(从2.3开始)为我们提供了一个安全的导航操作符, &. 句法。

所以:

u.email.upcase

会成为:

u.email&.upcase

与Rail的try方法类似,如果在nil上遇到NoMethodError ,整个链将返回nil

 User.find(1) 

如果id为1的用户不存在则会引发exception,因此您不必担心这里的nil

 u.email 

如果你有你的模型

 validates :email, presence: true 

您不需要担心nil,因为没有电子邮件的用户不能在数据库中

但我认为你问的是在ruby代码中处理nils的一般方法。 最近我使用Null Object模式

http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/

http://robots.thoughtbot.com/post/20907555103/rails-refactoring-example-introduce-null-object

Rails:用Null对象模式替换try

https://github.com/martinciu/nullobject

您还可以映射查找结果

 [User.find(1)].map{|u| (u != nil ? u.mail : "no mail")}[0]