Check existence of object and it's property at the same time

I run into this from time to time and I was wondering if there is a
cleaner way to do check that an object is not nil and also check a
property of that object.

I want to do this:

if !@member.nil? and @member.is_active?
… do something …
else
… do something else …
end

But this obviously raises a no method error if @member is nil.

I end up having to do:

if !@member.nil?
if @member.is_active?
… do something …
else
… do something else …
end
end

I know it isn’t that much more verbose, but does ruby have a more
elegant way of handling this?

Hi –

On Thu, 19 Aug 2010, Cory P. wrote:

end

But this obviously raises a no method error if @member is nil.

It doesn’t, actually, because if @member is nil, the “and” is
short-circuited so the right-hand expression doesn’t get evaluated:

if !@member.nil? and @member.is_active?; end
=> nil
@member
=> nil

A somewhat more common way of doing this is:

if @member && @member.is_active? … end

There’s a difference, though, which is that “if @member” is false if
@member is false, not just if @member is nil. I’m a big non-fan of
tri-state booleans involving true/false/nil, so I try to avoid
situations where I have to distinguish between nil and false for
condition-testing purposes.

David


David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com

Hi –

On Thu, 19 Aug 2010, Alex S. wrote:

But this won’t:

unless creds.instance_of?(Hash) && creds.has_key?(“user”) && …

They both short-circuit. Note that the puts(“here”) statement never gets
executed.

creds = {}
=> {}
unless creds.class.to_s =~ /Hash/ && creds.has_key?(“user”) && puts(“here”); 1; end
=> 1
unless creds.instance_of?(Hash) && creds.has_key?(“user”) && puts(“here”); 1; end
=> 1

David


David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com

I’ve run into a similar situation before, and found that
short-circuiting in an ‘if’ statement is inconsistent.

For instance, this will short-circuit and prevent evaluation of
subsequent statements if the first one is false (which is desired
obviously):

unless creds.class.to_s =~ /Hash/ && creds.has_key?(“user”) && …

But this won’t:

unless creds.instance_of?(Hash) && creds.has_key?(“user”) && …

I’d be interested to learn why the first one short-circuits and the
second doesn’t. Anyone have any insight?

Thanks,
Alex

David,

You are completely correct. I guess that was a bad example. So the
order of the conditions is important:

if @member and @member.is_active?; end
=> nil
if @member.is_active? && @member; end
NoMethodError: undefined method `is_active?’ for nil:NilClass

I could have sworn I ran into this somewhere and it didn’t work as
expected.

Thanks so much for the insight, it is good information to have.

Cory

David A. Black wrote:

Hi –

On Thu, 19 Aug 2010, Cory P. wrote:

end

But this obviously raises a no method error if @member is nil.

It doesn’t, actually, because if @member is nil, the “and” is
short-circuited so the right-hand expression doesn’t get evaluated:

if !@member.nil? and @member.is_active?; end
=> nil
@member
=> nil

A somewhat more common way of doing this is:

if @member && @member.is_active? … end

There’s a difference, though, which is that “if @member” is false if
@member is false, not just if @member is nil. I’m a big non-fan of
tri-state booleans involving true/false/nil, so I try to avoid
situations where I have to distinguish between nil and false for
condition-testing purposes.

David


David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com

On Thu, 19 Aug 2010, David A. Black wrote:

creds = {}
=> {}
unless creds.class.to_s =~ /Hash/ && creds.has_key?(“user”) &&
puts(“here”); 1; end
=> 1
unless creds.instance_of?(Hash) && creds.has_key?(“user”) && puts(“here”);
1; end
=> 1

(P.S. I’ve made the second condition fail, rather than the first, but if
you make creds a non-hash it will short-circuit after the first one.)


David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com

Strange… it was failing before (like a few weeks ago) w/ a
NoMethodError on has_key? when it wasn’t a hash. But trying it again
now works fine as you note.

Hi –

On Thu, 19 Aug 2010, Alex S. wrote:

Strange… it was failing before (like a few weeks ago) w/ a
NoMethodError on has_key? when it wasn’t a hash. But trying it again
now works fine as you note.

Maybe you were doing has_key? first. Unless you did this:

h = {}
class << h; undef_method :has_key?; end

:slight_smile:

David


David A. Black, Senior Developer, Cyrus Innovation Inc.

The Ruby training with Black/Brown/McAnally
Compleat Philadelphia, PA, October 1-2, 2010
Rubyist http://www.compleatrubyist.com

On Wed, Aug 18, 2010 at 11:47 PM, Cory P. [email protected]
wrote:

David,

You are completely correct. I guess that was a bad example. So the
order of the conditions is important:

if @member and @member.is_active?; end
=> nil
if @member.is_active? && @member; end
NoMethodError: undefined method `is_active?’ for nil:NilClass

Yes, it’s important. Ruby starts evaluating from left to right, and
will shortcircuit on the first value that would make the whole
expression false (or true if it’s an “or”).

irb(main):004:0> member = nil
=> nil
irb(main):005:0> if member or member.is_active?
irb(main):006:1> puts “one or both were true”
irb(main):007:1> end
NoMethodError: undefined method `is_active?’ for nil:NilClass
from (irb):5
from :0
irb(main):008:0> member = “test”
=> “test”
irb(main):009:0> if member or member.is_active?
irb(main):010:1> puts “one or both were true”
irb(main):011:1> end
one or both were true

Here you have the opposite situation: when the first truthy expression
is found, the evaluation is shortcircuited to that value, as you can
see that the is_active? method is not called in the last example.

Jesus.

2010/8/19 Jesús Gabriel y Galán [email protected]:

Yes, it’s important. Ruby starts evaluating from left to right, and
will shortcircuit on the first value that would make the whole
expression false (or true if it’s an “or”).

For the record: short circuiting also happens in Enumerable#any? and
#all?. And btw, the same short circuiting is done in Java and I guess
most modern languages.

Kind regards

robert