6

假设我有一个User对象,它有一个email属性,我需要它们的最后一个大写字母email

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

如果uoremailnil这个链中,那么我得到一个NoMethodError: undefined method 'blah' for nil:Nilclass. 在大多数情况下,我应该能够解决它,但有时,anil会到达不应该或难以控制的地方。一种方法是冗长的:

u = User.find(1)
letter = nil
if u && u.email
 letter = u.email.upcase.last
end

但这在视图或长链中变得烦人和危险a.bunch.of.properties.down.a.hierarchy。我阅读了tryRails 中的方法:

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

这不那么冗长,但我觉得写所有这些尝试都很恶心。一旦我放入try链条,我必须一直使用它们。有没有更好的办法?

4

4 回答 4

8

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

例如,您可以这样做:

class NilUser
  def email
    "(no email address)"
  end
end

u = User.find(1) || NilUser.new
u.email.upcase.last # => No errors!
于 2013-10-19T18:21:39.317 回答
3

I just wanted to update this thread with one more option: Ruby now (as of 2.3) gives us a safe navigation operator, the &. syntax.

So:

u.email.upcase

Would become:

u.email&.upcase

Similarly to Rail's try method, the whole chain will return nil if it encounters NoMethodError on a nil.

于 2017-11-30T05:23:39.253 回答
1
User.find(1)    

Will raise exception if user with id 1 not exist so you don't need to worry about nil here

u.email

If you have in your model

validates :email, presence: true

You don't need to worry about nil because User without email cant be in database

But I think you are asking about general way of handling nils in ruby code. Lately I'm using Null Object pattern

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

于 2013-10-19T18:25:19.073 回答
0

您还可以映射 find 的结果

[User.find(1)].map{|u| (u != nil ? u.mail : "no mail")}[0]
于 2014-06-13T16:41:45.220 回答