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

时间:2013-10-19 18:09:48

标签: ruby-on-rails ruby

假设我有一个User对象,该对象具有email属性,我需要他们email的最后一个字母:

u = User.find(1)    
letter = u.email.upcase.last

如果此链中的uemailnil,那么我会获得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放入链中,我就必须一直使用它们。还有更好的方法吗?

4 个答案:

答案 0 :(得分:8)

我喜欢使用Null Object PatternAvdi 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

Rails: replacing try with the Null Object Pattern

https://github.com/martinciu/nullobject

答案 3 :(得分:0)

您还可以映射查找结果

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