2

Is there a .? operator in Ruby that checks if an object is nil before calling a method on it?

For instance if I have to code:

if person and person.neighbor and person.neighbor.house and person.neighbor.house.rooms
    person.neighbor.house.rooms.each do |room|
        blah
    end
end

Is there a better way than having to do an if check on everything?

And please don't say "code in such a way that these objects cannot ever be nil", I am getting these things from an API call and can't control it.

Ulysse BN
  • 10,116
  • 7
  • 54
  • 82
Razor Storm
  • 12,167
  • 20
  • 88
  • 148
  • 1
    The [Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter) may be interesting to you, as your code violates it heavily. – Andrew Marshall Mar 12 '12 at 00:05
  • possible duplicate of [Is there an equivalent null prevention on chained attributes of groovy in ruby?](http://stackoverflow.com/questions/8805582/is-there-an-equivalent-null-prevention-on-chained-attributes-of-groovy-in-ruby) – Andrew Marshall Mar 12 '12 at 00:06
  • @AndrewMarshal, I definitely agree. However I can't think of a good way to follow LoD in my case. I am basically making an API call to a 3rd party authentication platform (ie facebook twitter etc) and getting back a huge nested object of information. Within this, I only want to grab 1 piece of info (specifically, the user's education history), which is deeply nested. Unless I write 5 different methods each of which only makes a call to its descendent, I am forced to violate LoD. Maybe there's a better way? – Razor Storm Mar 12 '12 at 00:12

5 Answers5

6

As of Ruby 2.3.0, the &. "safe navigation" operator does exactly this. So instead of

person and person.neighbor and person.neighbor.house and person.neighbor.house.rooms

you can now just do

person&.neighbor&.house&.rooms.

http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

Milo P
  • 1,377
  • 2
  • 31
  • 40
3

Easiest thing to do is use the andand gem, although there are some other, pretty trivially-implemented options, like this, or this, or etc.

Community
  • 1
  • 1
Dave Newton
  • 158,873
  • 26
  • 254
  • 302
1

The method you are looking for is nil?

Example:

foo.nil?

For a more elegant approaches to programming defensive against nil values, see my question on the same topic. For example you could write:

person.neighbor.house.rooms.each do |room|
    blah
end
rescue false
Community
  • 1
  • 1
wintersolutions
  • 5,173
  • 5
  • 30
  • 51
1

For second case, checking for does the object have a list of methods or not, you may use try method

require "active_support/core_ext/object/try"
person.try(:neighbor).try(:house).try(:rooms).each do |room|
   blah
end

Or my version of try

class Object
  def try(*args, &block)
    if args.empty? and block_given?
      begin
        instance_eval &block
      rescue NameError => e
        puts e.message + ' ' + e.backtrace.first
      end
    elsif respond_to?(args.first)
      send(*args, &block)
    end
  end
end

Then you can do it in short way:

person.try{neighbor.house.rooms}.each do |room|
   blah
end
megas
  • 21,401
  • 12
  • 79
  • 130
1

why not

begin
    person.neighbor.house.rooms.each do |room|
        ...
    end
rescue NameError
    ...
end

Exception handlers are made for handle exception :) So if you depend on someone else code, you should not prevent but handle your code for crash, not to check if everything is ok.

Ismael Abreu
  • 16,443
  • 6
  • 61
  • 75