在Rails/Ruby中检查对象是否存在的正确方法是什么?

5

我有很多模型和关联。因此,在视图/控制器中有很多调用,看起来像这样:

 @object.something.with_something.value 

某些链的部分可能最终变成nil,这是完全可以接受的。检查终端对象的存在的正确/清洁/快速方法是什么?

像这样调用是否正确:

 @object.something.with_something.value if defined? @object.something.with_something.value 

考虑是否可行?


这个问题表述不是很清楚...你只关心@object.something.with_something.value是否为nil,还是担心链中的任何内容都是nil时引发的NoMethodError?我猜想是后者? - Bob Aman
4个回答

8
原生情况下,您需要使用&&运算符(而不是defined?),但这样很快就会变得非常冗长。
因此,不要这样做:
(@object && @object.something && @object.something.with_something &&
  @object.something.with_something.value)

当 ActiveSupport 存在时,您可以这样做:
@object.try(:something).try(:with_something).try(:value)

或者安装调用构建工具包并使用其守护式评估工具:

Ick::Maybe.belongs_to YourClass
maybe(@object) { |obj| obj.something.with_something.value }

3
最好将代码的其余部分排列有序,以便在链中最后一个对象时查看此问题。
“defined?”不能达到你想要的效果。某些情况下可以同时是“defined?”和“nil”。
当问题仅限于参考链中的最后一个属性时:
@object.something.with_something.value if @object.something.with_something

我可以利用以下事实:

我可能会利用以下事实:

nil.to_a => []
nil.to_s => ''
nil.to_f => 0.0
nil.to_i => 0

因此,如果您知道某个东西是nil或一个Array,通常可以通过编写以下内容来编写更好的代码而无需任何条件语句:

something.to_a.each do |e|
  . . .

是的,我重新阅读了原帖并理解了你的意思。我对原帖的问题进行了一些深入的阅读,因为我知道我回答的问题更有可能是他实际遇到的麻烦。 - Bob Aman
2
说实话,我并不是你的解决方案的忠实粉丝。我已经看到它出现了几次,每当我看到它时,我都会将其删除,因为我非常坚信它会使代码变得更难读。其他方法,如“尝试”和“可能”,更通用地解决了这个问题,并以更易读的方式实现了这一点。 - Bob Aman
我知道你的意思,但当我发现nil有专门的Object的to_x函数时,我想:Matz真是个天才。例如,**(something.somethingelse || []).each do**相比之下真的有点笨拙。 - DigitalRoss

2

what.you.are.doing有时被称为“火车失事”,也被描述为违反Demeter法则。

话虽如此,我认为有一种叫做“andand”的东西可以帮助你解决目前的问题。


1
是的,Object#andand 是一种解决方案,但我尽量避免使用它——引入一个本质上只提供了一个方法的依赖关系有点奇怪。此外,默认情况下它会对 Object 类进行猴子补丁,这是很糟糕的。 - Bob Aman

0

另一个选择是使用空对象模式来确保这些对象中没有一个是nil。可以说,如果您的代码要以这种方式链接访问,那么某些东西应该始终被定义。


这个最好的部分是,在Ruby中,这个模式可以透明地有效实现。这本质上就是调用构建工具包在幕后实现maybe的方式。它提供了一个IdentityWrapper和一个GuardWrapperGuardWrapper的目的与该模式中的空对象基本相同,只不过当块评估完成时,包装器可以自动剥离。 - Bob Aman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接