What is the best way to test a null object before calling a method on it?

I have this calling method that I have to use ...

financial_document.assets.length 

But financial_document.assets may be nil .

I could use ...

 financial_document.assets.nil? ? '0' : financial_document.assets.length 

Is there a less repetitive way to do this?

+6
ruby ruby-on-rails
source share
11 answers

Personally, I would use the or operator:

 (financial_document.assets or []).length 

In any case, .length is called in the array, giving you 0 if nil .

+7
source share

Dave W. Smith is on the right track.

Check it out: http://www.nach-vorne.de/2007/4/24/attr_accessor-on-steroids

One simple solution would look something like this:

 class FinancialDocument attr_accessor :assets def assets @assets ||= Array.new end ... end 
+8
source share

A less repetitive way of dealing with this is to ensure that financial_document.assets is always a non-zero object by placing an appropriate sentinel value for it (for example, an empty collection or a special object that has degenerate behavior).

See Sample Null Object .

+7
source share

Case 1:

financial_document and assets have many relationships. In this case, financial_document.assets always returns an array. Thus, financial_document.assets.size will give you 0 if no matching child entry is found and size otherwise.

Case 2:

assets is just a method / attribute in financial_document . Then use the returned array of the assets method so you can always call it .size. Just like Joel pointed out.

+3
source share

In this case, I use andand gem:

 financial_document.assets.andand.length || 0 
+3
source share

A more general way to solve this class of problems is to add a try method to Object:

  ## # @user.name unless @user.nil? # vs # @user.try(:name) # def try(method, *args, &block) return nil unless method return nil if is_a?(NilClass) and [:id, 'id'].include?(method) self.send(method, *args, &block) if respond_to?(method) end 

I believe that ruby ​​1.9 already has a try method for Object.

Then financial_document.assets.try(:length).to_i will achieve the desired result. This is because nil.to_i returns 0

+3
source share

financial_document.assets.try (: length) || 0

try is a method that will call an object's method if its non nil otherwise returns nil. And try nil methods will always return zero instead of throwing an exception.

http://api.rubyonrails.org/classes/Object.html#method-i-try

This is a ruby ​​way to do it!

+1
source share

You can do this without additional gems. I used || , andand , try , but the following looks simpler. I think this is a ruby ​​way to validate a Dave null object pattern.

 financial_document.assets.to_a.length 
+1
source share

This is Ruby, you can add a length method to NilClass and always return 0.

0
source share

You can make it a little shorter:

 financial_document.assets ? financial_document.assets.length : '0' 

because

 financial_document.assets == !financial_document.assets.nil? 

but overall, IMHO there is no less repetitive, only various workarounds. (And this is one of the things I don't like about Ruby that much.) You can make sure that objects are not null (as other people point out here), but you can't do this everywhere. You can wrap nil verification code in helper methods or in start-rescue blocks.

For example, instead of adding the length method to the nil object (which is IMHO a dirty hack), I wrote a helper method - "get get get":

 def fd_length(financial_document) financial_document.assets ? financial_document.assets.length : '0' end 
0
source share

Something in the model that returns 0 or length. This prevents you from doing the overrated thing in your opinion. Such things can usually be done in a model.

 class FinancialDocument def assets_length assets.length.blank? 0 : assets.length end end 
0
source share

All Articles