Ruby: "if !object.nil?" or "if object" - ruby

Are they the same when used in an if/else/end statement? What do you usually do? I'm wondering if there are any subtle differences or edge cases where object and !object.nil? would respond differently.

There are differences. For example:
false.nil?
# => false
So:
if !false.nil?
'foo'
end
# => "foo"
if false
'foo'
end
# => nil
As #tokland suggested, in most cases using !obj.nil? construction is unnecessary.

There is one and only one case in which !object.nil? and object evaluate to different results in a boolean context and that is if object is false. In all other situations the result is the same.
With this, I think we can answer your real question (which is: Is there any situation where I must use if !object.nil? instead of just if object when protecting against object being nil?):
No, you can always use if object if you want to check against nil.

if object tests if object isn't either nil or false.
!object.nil? tests if object isn't nil. (Rubydoc)
So when object is false, they have different values.

Well. if object will behave differently from if !object.nil? if object=false.
That's about it.

Objects can override nil? but they cannot be falsely unless they are nil or false. Personally I use nil? or ActiveSupport's present? so that I maintain that flexibility. I also think it reads a bit better.

Related

Why does `defined?` return a string or nil?

In ruby, why would defined? return a string? Most other ruby methods ending with a ? return a boolean.
Was this a hack to support a feature request, or was there intentional misuse of ruby convention, and why?
Examples:
defined?(super)
=> "super"
defined?(nil)
=> "nil"
defined?(Object)
=> "constant"
No, it was neither a hack nor a misuse of Ruby convention. As matz writes in ruby-talk 7986:
The '?' methods ... return either
(a) true or false
(b) non-false informative value or nil
defined? falls into (b).
Also, as commenters have pointed out, defined? is not a method. Matz expands in ruby-talk 1637:
[defined? is] a control structure. Not everything is a message send in Ruby, e.g. control structures, variables, blocks are not objects. defined? is among these things.
As sawa points out defined? is not actually a method.
If it were, the Ruby source code docs states this is allowed for methods that end in a question mark.
Methods that end with a question mark by convention return boolean. But they may not always return just true or false. Often they will may return an object to indicate a true value (or “truthy” value).
ref: https://github.com/ruby/ruby/blob/c8b3f1b470e343e7408ab5883f046b1056d94ccc/doc/syntax/methods.rdoc

Make a NullObject evaluate to falsy in Ruby

After implementing Null Object Pattern in a Rails application (also described in RubyTapas episode 112) I refactored out some code, but there's a syntax construct that seems to not work anymore.
I used to write statements like current_user || redirect_out, where, if current_user was set it would return it, and if it was nil it redirects out, but now current_user may be an instance of Null::User and thus "truthy", and that snippet would never redirect out.
I tried defining the || operator, but didn't work. Is there any way this syntax can still be used with null (but "truthy") objects?
I think you have only halfway adopted that pattern, and have not correctly adopted its spirit. My understanding is that the very purpose of the pattern is to avoid ||.
You should have some purpose for calling current_user || redirect_out, and that could be doing something with it, or getting some attribute of it. For example, suppose your code has:
(current_user || redirect_out).foo
When current_user is not an instance of Null::User, you wanted to call foo on it. In that case, what you should do is define Null::User#foo to be redirect_out (possibly followed by some more operations like foo on other classes).
class Null::User
def foo; redirect_out ... end
end
and in place of (current_user || redirect_out).foo, you should just do
current_user.foo
When current_user is not a Null::User instance, it will call foo. When it is such instance, then the redirect_out ... routine will be called on it.
I once wrote an article about how it's not possible to define "falsy" objects in Ruby, and why attempts to make a Null Object falsy are generally misguided.
Basically, the best you can do is come up with a confusingly inconsistent object using #!, nil?, etc.
As others have noted, usually when you want to make a Null Object "falsy" it's because you're not full leveraging polymorphism. The whole point of a Null Object is to avoid type checks, and checking for NilClass in the form of an if statement is just as much a type check as any other.
That said, sometimes it's unavoidable. That's why in my Naught library I generate a helper conversion function called Actual() (among several other conversions). Actual() converts Null Objects back to nil values, but leaves all other objects alone. So for the cases where you need to switch on an object's truthiness, you can do it like this:
if Actual(obj_that_might_be_null)
# ...do stuff...
end
There are exactly two objects which are falsy in Ruby: nil and false. Period.
Unfortunately, it is not possible to define your own falsy objects, nor is it possible to override the Boolean operators (except for not/!). This is a shame, really: it is one of the basic pillars of OO that an object can simulate another object, but in Ruby it is not possible to simulate false or nil, therefore breaking one of the fundamental properties of OO in a language with an otherwise pretty good OO model.

Is there no convenience method for !nil?

I think I would see my code better if I would ask myself object.not_nil? vs !object.nil?. So my question: Is there really no convenience method for !nil? to sugar things up? Is it in front of my eyes and I cannot see it or am I just missing an important point?
How about this?
not object.nil?
But the easier thing to do would be to check for the "truthiness" of by testing the variable itself. Since nil is implicitly false you can just check object.
You can introduce the sugar at an upper level. Instead of:
if not object.nil?
you can write:
unless object.nil?
What about this ?
if object
# sth
end
It is not the same as it will not be executed if object is false but depending on you code, it could be better.
Another solution (which is not the same either), as you tagged your question with ruby-on-rails-3 : using present? which will not execute the block for [] or {} unlike !object.nil?.
Again another one depending of the case : using unless which won't be really nice if your condition is more complex (with && and/or ||).
If your condition is of this form :
if !object.nil? && object.something?
# sth
end
You can use try, as you are using Rails, like this :
if object.try(:something?)
# sth
end
In all the other cases, !object.nil? or not object.nil? stays the best solution I guess.
When convenience around #nil? is discussed, Activesupport's methods #blank? and #present? shouldn't be forgotten either.
Not that you'd necessarily want to, but you can introduce not_nil? yourself:
class Object
def not_nil?
!self.nil?
end
end
then you can do things like:
nil.not_nil?
==> false
3.not_nil?
==> true
a = []
a.not_nil?
==> true

In ruby, is truthiness idiomatic for a method name ending with a question mark?

Is it normal for methods with a question mark to return something that's truthy (for example, a number) to indicate that something is true, or should true itself be returned?
Are there any examples of truthiness being used in the Ruby standard library or by Rails, for example?
Background: Someone wrote a String#int? method in an answer to a separate question, which returned an integer to represent true, and nil to represent false. Another user was surprised at not returning a boolean.
Adding a ? to a method name in Ruby is idiomatic that the method will return true or false. Object#nil? is a good example. In fact, Object has a lot of good examples of truthiness checks.
It is usual for methods ending with ? to return either true or false but it is not systematic and no core method will assume it.
An example in the core classes is Numeric#nonzero? which never returns true or false.
42.nonzero? # => 42
The library Set has add? and delete? too. I wish Enumerable#one? returned nil or false to distinguish cases where the count is zero from when it is greater than one.
A similar example are the comparison operators (<, >, ...), which usually return only true or false. Here again exceptions exist for Module's operators that will return nil instead when the two modules are not related:
Array > Enumerable # => false
Array > Fixnum # => nil
There are two answers to your question, and both are valid:
Technically, anything returning a false or nil value acts as a false boolean when doing a conditional test, just as a non-nil or true value acts as a true. So, the method in question will work correctly for most times you'd want to know if something is an integer.
But stylistically a method that ends with '?' should return either a Boolean true or false and only those.
The method in question doesn't really play nicely with our expectations and fails the POLS ("principle of least surprise") because you can reasonably expect a Boolean value being returned, and get an integer or a nil. THAT could lead to unpleasant surprises in code when it fails with an unexpected nil or a Fixnum value.
So, while it's a valid method, it's not a good method, and I would have brought it up in a code review. And that leads to a separate discussion of how subtle things like that can enhance or hurt code maintenance, but that's an entirely different discussion.
I renamed the method in question to remove the ?, which was really not important to the question being answered. Scanning through the core functions that end in ?s, the only ones I spotted that returned data or nil were the add? and delete? methods on Set.

Tips on understanding Ruby syntax, when to use ?, and unless

Is the keyword unless the same as if?
When do you use ??
I've seen:
if someobject?
I know it checks against nil correct?
Is the keyword 'unless' the same as 'if' ?
No, it's the opposite.
unless foo is the same as if !foo
if someobject?
I know it checks against nil correct?
No it calls a method named someobject?. I.e. the ? is just part of the method name.
? can be used in methodnames, but only as the last character. Conventionally it is used to name methods which return a boolean value (i.e. either true or false).
? can also be used as part of the conditional operator condition ? then_part : else_part, but that's not how it is used in your example.
unless is actually the opposite of if. unless condition is equivalent to if !condition.
Which one you use depends on what feels more natural to the intention you're expressing in code.
e.g.
unless file_exists?
# create file
end
vs.
if !file_exists?
# create file
end
Regarding ?, there is a convention for boolean methods in Ruby to end with a ?.
This statement:
unless conditional expression
Is the equivalent to:
if not (conditional expression)
In Ruby you can end your method names with a question mark which is normally used to show that it is a boolean method.
With Rails a check against nil would look like this:
someobject.nil?
This calls the nil?() method of the object, which returns true for NilObject and false for anything else.
I think the convention for ?-suffix is to use it when naming a method that returns a boolean value. It is not a special character, but is used to make the name of the method easier to understand, or at least I think that's what the intention was. It's to make it clear that the method is like asking a question: it shouldn't change anything, only return some kind of status...
There's also !-suffix that I think by convention means that the method may have side-effects or may modify the object it is called on (rather than return a modified copy). Either way, the ! is to make you think carefully about calling such a method and to make sure you understand what that method does.
I don't think anything enforces these conventions (I've never tried to break them) so of course you could abuse them horribly, but your fellow developers would not be happy working with your code.
for unless see here: http://railstips.org/blog/archives/2008/12/01/unless-the-abused-ruby-conditional/
if someobject?
The appending of a '?' here only means that it returns a boolean.

Resources