Sarp Kaya
Sarp Kaya

Reputation: 3794

Ruby one line if statement with exception handling

I cannot seem to find a way to do this. All I want to do is try the first statement, if it is blank or null (at any stage) then return the other one. For instance

a.b.c.blank? ? a.b.c : 'Fill here'

This is causing me nil:NillClass exception. Is there a way to fix this in a simple one liner way?

Upvotes: 0

Views: 2221

Answers (4)

danield
danield

Reputation: 21

Since Ruby 2.3.0 (released 2015-12-25), this can be achieved using the save navigation operator, which is similar to Groovy's and Kotlin's null safe ?:

new method call syntax, object&.foo', method foo is called onobject' if it is not nil.

# a as object { b: { c: 'foobar' } }
a&.b&.c&.empty? ? 'Fill here' : a.b.c #=> 'foobar'
nil&.b&.c&.empty? ? 'Fill here' : a.b.c #=> 'Fill here'

Safe calls return nil when they're called upon nil objects. This is why the second case in the above example evaluates to nil and thus false.

source: NEWS for Ruby 2.3.0 (Feature #11537)
see also: What is the difference between try and &. (safe navigation operator) in Ruby

Upvotes: 0

Arie Xiao
Arie Xiao

Reputation: 14082

If you have active_support available, you can use Object#try:

a.try(:b).try(:c) or 'Fill here'

If you don't have that, it's pretty easy to monkey-patch Object to add one. Here's the code in active_support, just put it some where before you are using try method.

class Object
  def try(*a, &b)
    if a.empty? && block_given?
      yield self
    else
      public_send(*a, &b) if respond_to?(a.first)
    end
  end
end

After that, you can use it:

a = nil
a.try(:b).try(:c).try(:nil?)    #=> true

b = 1
b.try(:+, 2)    #=> 3

Upvotes: 2

spickermann
spickermann

Reputation: 106982

I would require the ActiveSupport package that allow the use of presence do something like this:

require 'active_support'
require 'active_support/core_ext/object/blank'
a.presence && a.b.presence && a.b.c.presence || 'Fill here'

See: http://apidock.com/rails/Object/presence

Upvotes: 0

Jaffa
Jaffa

Reputation: 12719

There's not nil:NilClass exception thrown by default.

a or b or c may be nil, so for a one line statement you can do:

# require this to use present?, not needed with rails
require 'active_support/all'

a.present? ? (a.b.present? ? (a.b.c.present? ? a.b.c : 'Fill here') : 'Fill here') : 'Fill here'

(And this is ternary expression, not exactly an if statement)

But this is ugly, although you can remove parts of the expression if you are sure that a or a.b is never nil.

I used present? over blank? to keep the same order as your expression. The ternary operator evaluates the first expression if the condition is true, so this may be your error.

Upvotes: 0

Related Questions