Preferred way to write true AND false conditional in Ruby - ruby

I apologize if this question is answered somewhere but I'm not positive I'm phrasing it right for Google, and I haven't seen it in any style guides.
Since Ruby has multiple ways to show negativity in a conditional, what is the preferred way to write a conditional that is checking that one part is true and one part is false? Example:
if array && !array.include?('Bob')
#do stuff!
But you could also say:
if array
#do stuff! unless array.include?('Bob')
or:
if array && not array.include?('Bob')
#do stuff
or:
if !array.nil? && !array.include?('Bob')
or a wacky double unless:
unless array.nil?
#do stuff unless array.include?('Bob')
And several others. Any idea which is considered the most Rubyish? Sources to back your opinion up?

As far as documented guidelines, the only thing that i can think of is the Google guide that admonishes "don't use and and or; always use && and || instead.".
Other than that, it somewhat depends on the context. If all you have is code to be executed if one condition is true and the other false, then I would definitely put them in a single if with && !:
if array && !array.include?('Bob')
#do stuff!
On the other hand, you might have additonal code that gets executed if the first condition is true even if the second one is also true; in that case, the nested unless or if makes sense:
if array
do stuff! unless array.include? 'Bob'
do other stuff anyway
end

Does not improve readability, but still interesting.
If you use Rails you can use ActiveSupport try extension like this:
if array.try(:include?, 'Bob')
# Do stuff

Related

Rubocop, safe navigation, and negation

I've got constructs like the following that Rubocop complains about that I'm trying to appease
unless foo && !foo.empty? ...
(item exists and is not empty)
and
bar && !bar.positive?
(item exists and is not positive)
In both cases, Rubocop throws a Style/SafeNavigation and says I should use &. instead. However, there does not seem to be a way to write it.
In both cases part of the problem is that there is no concise way of expressing the opposite function - there is no "not empty" for strings, and no "negative or zero" for numbers. Writing bar&.negative? returns the opposite result for zero, for example.
In the other case, writing unless !foo&.empty? has Rubocop complain about unless+a negation, but again there's no way to rewrite it as an if foo&. with any operation that exists.
(btw this is straight Ruby, no Rails, so I don't have blank? and present?)
This:
next unless foo && !foo.empty?
Could be replaced with:
next if foo.nil? || foo.empty?
And similarly, this:
next unless bar && !bar.positive?
Could be replaced with:
next if bar.nil? || bar.positive?
The real code smell here, in my opinion, was the next unless ... not. The double negative indicates that there's probably a cleaner way to write it.
This is an oversight in RuboCop, because the suggested correction can not be guaranteed to have the same semantics. It will be fixed in the upcoming 0.50.1 patch, and the cop will ignore cases with negation.
In the meanwhile, if you're dealing with conditions where your variable could be nil, one option is to use the fact that nil responds to the coercion methods. Example, assuming foo is either an array or nil:
# With nil check
unless foo && !foo.empty?
# With array coercion
if foo.to_a.empty?

What's the purpose of using the suffix "if" statement in Ruby?

I know Ruby supports a suffix if like:
number = -42 if opposite
but what's the purpose of this? Why would it be used in place of the prefix if statement?
The suffix-style if and unless can also be good for "guard clauses", in the form of:
return if ...
return unless ...
Here's an example:
# suffix-style
def save
return false if invalid?
# go for it
true
end
Versus:
# indented style
def save
if valid?
# go for it
true
else
false
end
end
In the second example, the entire implementation of the method has to be shifted over by one indent due to the valid? check, and we need an extra else clause. With the suffix style, the invalid? check is considered an edge case that we handle and then bail out, and the rest of the method doesn't need an indent or an else clause.
This is sometimes called a "guard clause" and is recommended by the Ruby Style Guide.
It can make the code easier to read in some cases. I find this to be true especially in the case of unless, where you have some action you usually want to perform:
number = -42 unless some_unusual_circumstance_holds
Once you have it for unless, for symmetry it makes sense to support it for if as well.
number = -42 if opposite
is the same as
if opposite
number = -42
end
Some people prefer the one-liner for readability reasons. Imagine a line like:
process_payment if order_fulfilled?
Doesn't that read nice?
Postfix style does not have the else section. It is useful when you only want to do something with one of the two cases divided by the condition and don't want to mess with the other case.
It's the same as prefix but shorter. The only reason is to save vertical space in the text editor.

2 Conditions in if statement

I am trying to detect if the email address is not one of two domains but I am having some trouble with the ruby syntax. I currently have this:
if ( !email_address.end_with?("#domain1.com") or !email_address.end_with?("#domain2.com"))
#Do Something
end
Is this the right syntax for the conditions?
Rather than an or here, you want a logical && (and) because you are trying to find strings which match neither.
if ( !email_address.end_with?("#domain1.com") && !email_address.end_with?("#domain2.com"))
#Do Something
end
By using or, if either condition is true, the whole condition will still be false.
Note that I am using && instead of and, since it has a higher precedence. Details are well outlined here
From the comments:
You can build an equivalent condition using unless with the logical or ||
unless email_address.end_with?("#domain1.com") || email_address.end_with?("#domain2.com")
This may be a bit easier to read since both sides of the || don't have to be negated with !.
If more domains are added, then the repetitive email_address.end_with? is getting boring real fast. Alternative:
if ["#domain1.com", "#domain2.com"].none?{|domain| email_address.end_with?(domain)}
#do something
end
I forgot end_with? takes multiple arguments:
unless email_address.end_with?("#domain1.com", "#domain2.com")
#do something
end
How about:
(!email_address[/#domain[12]\.com\z/])

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

Why Ruby 1.9 allows overriding ! != !~?

There were two good reasons why Ruby 1.8 didn't support certain kinds of overloading like ||/or, &&/and, !/not, ?::
Short circuit semantics cannot be implemented with methods in Ruby without very extensive changes to the language.
Ruby is hard coded to treat only nil and false as false in boolean contexts.
The first reason doesn't apply to !/not but second still does. It's not like you can introduce your own kinds of boolean-like objects using just ! while &&/|| are still hard-coded. For other uses there's already complementarity operator ~ with &/|.
I can imagine there's a lot of code expecting !obj to be synonymous with obj ? false : true, and !!obj with obj ? true : false - I'm not even sure how is code supposed to deal with objects that evaluate to true in boolean context, but ! to something non-false.
It doesn't look like Ruby plans to introduce support for other false values. Nothing in Ruby stdlib seems to override ! so I haven't found any examples.
Does it have some really good use I'm missing?
Self-replying. I found one somewhat reasonable use so far. I can hack this to work in 1.9.2 in just a few lines:
describe "Mathematics" do
it "2 + 2 != 5" do
(2+2).should != 5
end
end
Before 1.9.2 Ruby this translates to:
describe "Mathematics" do
it "2 + 2 != 5" do
((2+2).should == 5) ? false : true
end
end
But as return value gets thrown away we have no ways of distinguishing == 5 from != 5 short of asking Ruby for block's parse tree. PositiveOperatorMatcher#==(5) would just raise ExpectationNotMetError exception and that would be it. It seems should !~ /pattern/, should !be_something etc. can also be made to work.
This is some improvement over (2+2).should_not == 5, but not really a big one. And there's no way to hack it further to get things like (2+2).should be_even or be_odd.
Of course the right thing to do here would be asking Ruby for parse tree and macro-expanding that. Or using debug hooks in Ruby interpreter. Are there use cases better than this one?
By the way, it wouldn't be enough to allow overrides for ! while translating !=/!~ the old way. For !((2+2).should == 5) to work #== cannot raise an exception before ! gets called. But if #== fails and ! doesn't get run execution will simply continue. We will be able to report assertion as failed after block exits, but at cost of executing test after the first failure. (unless we turn on ruby debug hooks just after failed assertion to see if the very next method call is !self, or something like that)

Resources