处理零对象和属性的最佳实践是什么?
假设我有一个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
您还可以映射查找结果
[User.find(1)].map{|u| (u != nil ? u.mail : "no mail")}[0]