Ruby: Clean code for checking nil /false conditional statement? - ruby

I always meet this Ruby problem, I want to write it more cleanly.
var a can be nil
a.value can also be nil
a.value has possible true or false value
if (not a.nil?) && (not a.value.nil?) && a.value == false
puts "a value is not available"
else
puts "a value is true"
end
The problem is that the conditional statement is too clumsy and hard to read.
How can I improve the checking nil and false conditional statement?
Thanks, I am a Ruby newbie

Ruby on rails has an extension called try which allows you to write:
if a.try(:value) == false
which is very clean. Without try, you can just write
if a && a.value == false
If a.value is nil, it is not false, so that is ok :)
If it is possible that a.value is not defined (which would raise an exception), I would write that as follows:
if a && a.respond_to?(:value) && a.value == false
[UPDATE: after ruby 2.3]
Since ruby 2.3 there is an even shorter version:
if a&.value == false
which is almost equivalent to a.try(:value) (but is pure ruby). Differences:
if value does not exist, the &. operator will throw, try will just return nil (preferable or not?)(note: try! would also throw).
when cascading try or &. they also handle false differently. This follows logically from previous difference, try will return nil, while &. will throw because false knows no methods :P

You can achieve it in more compacted way using Safe Navigation Operator (&.):
if a&.value == false
Source : http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

if a && a.value!=false
puts "a value is true"
else
puts "a value is not available"
end
or just
puts a && a.value!=false ? "a value is true" : "a value is not available"

The simplest and cleanest way is to flip it and reverse it. Check for the truthy value rather than the falsey values
if a && a.value
puts "a value is true"
else
puts "a value is not available"
end
Of course in Rails you could do it either way by using blank? or present?

Your condition is redundant. If a.value is to be false, then it would not be nil.
if a.nil?.! && a.value == false
puts "a value is not available"
else
puts "a value is true"
end

This will always return a boolean, if nil it will return false; if false it returns false; if true returns true. Try it out, it's nice and short:
!!a.try(:value) == false

Related

How to reduce this If block to one statement?

if firstcheck? || secondcheck?
nil
else
true
end
How can i reduce this block of code to one single line?
If the condition is correct i need to return nil, else true
Please help
This line is equivalent to the snippet of code you wrote:
true unless firstcheck? || secondcheck?
I have to wonder if you really have a requirement to return nil instead of false. If you are OK with returning false instead of nil, you could write it as:
!firstcheck? && !secondcheck?

Conditioning tri-state logic

I have a variable that takes a value out of tri-state booleans:
asap = true
asap = false
asap = nil
Ruby's conditionals interpret them as truthy or falsey. Especially, nil is not distinguished from false.
asap = nil
unless asap
puts 'False'
end
# => 'False'
A statement that distinguishes false from nil is:
if asap == false
puts 'False'
end
but RubyMine 6.3.3 complains that the expression could be simplified, and suggests unless asap. Is there a different (right) way to do the conditioning?
TLDNR;
if FalseClass === asap
puts 'False'
end
Tri-state usually suggests that there are three possible branches, which forces me to think that neither if nor ternary ? is generally applicable.
puts case asap
when FalseClass then 'False'
when TrueClass then 'True'
when NilClass then 'Nil'
else 'You are kidding on me, aren’t you?'
end
Hope it helps.
UPD: The more I think about it, the more I am inclined to think that ternary is fine as well:
puts asap.nil? ? 'Nil' : asap ? 'True' : 'False'
UPD2: Actually, the tri-equal comparision operator, which is transparently used in case and which apparently evaluates to true for false === false and true === true might satisfy IDEA aka RubyMine as well:
if asap === false
puts 'False'
end
UPD3: As #sawa mentioned in the comment, IDEA is wrong here since nil == false evaluates to false and therefore == false is not generally equal to unless.

RSpec should be_true vs should == true

Is there any difference at all between:
something.should == true
and
something.should be_true
?
The convention is: expect(something).to be_true however there is no difference when used properly. Expecting something to == true should only be evaluated against expressions.
See docs: https://github.com/rspec/rspec-expectations#truthiness
Rspec Tests:
describe "is the equation true? " do
let!(:add) { 5+5 == 10 }
it "be_true" do
add.should be_true #passes
end
it "expect be_true" do
expect(add).to be_true #passes
end
it "== true" do
add.should == true #passes
end
end
Update for strings:
A string is not == true so it will fail, but it will pass as be_true because anything except for false or nil is considered truthy. However, this is NOT how you check for a string.
See this example for how to properly check for a value in a string:
str = "happy dance!"
For finding if text is in a string, you would use: expect(str).to include("happy dance!")
The answer supplied to this question is wrong as the below depicts, the first test is a test of equality while the second is a test of truthiness, the first would fail because something != true while the later would pass because anything order than nil or false is truthy in ruby
require 'rspec'
describe "do something" do
let(:something) { "hey" }
it { something.should == true }
it { something.should be_true }
end
No, there is no difference.
The latter is syntactic sugar for the former.

Why does Ruby expression with double ampersand using a return statement cause a syntax error

def foo
true && return false
end
def bar
true and return false
end
foo method causes a syntax error. bar doesn't. Why?
Assuming I want to evaluate one-liners with a return statement (similar to a certain commonly used shell/bash expression), would this be a recommended way to do it, or is there a more recommended approach?
By operator associativity strength,
true && return false
evaluates to
(true && return) false
of which
true && return
is valid, but the false after it is invalid. You cannot have two statements lined up without anything in between.
Side Note
It is worth noting that and and && are not equivalent.
and is a flow control operator while && is a Boolean operator. What is the difference?
One example of the differences, using and, you can assign values.
value = nil and value / 42
This would fail if you tried it as below
value = nil && value / 42
Original Question 1
Assuming I want to evaluate one-liners with a return statement (similar to a certain > commonly used shell/bash expression), would this be a recommended way to do it, or is there > a more recommended approach?
The way I have always seen this done in Ruby is this:
value if conditional
This will return the value if the conditional is met and nil otherwise. No need for the return statement if this is the last command in the function!
If you are using this for an early exit to a function, I prefer using an unless. For instance...
return unless arguments_are_valid
Original Question 2
foo method causes a syntax error. bar doesn't. Why?
It's a matter of operator precedence. See the example below showing how they are evaluated.
(true && return) false
(true) and (return false)
Because of the && operator precedence, the following line
true && return false
evaluates as
(true && return) false
that does not makes sense. In order to execute your code you need to use
def foo
true && (return false)
end
and doesn't suffer of the same issue because and has lower precedence than &&.
if there is need for shorten statements use
def foo
return false if true
end
def bar
return false if true
end
return is not a function :) therefore it doesn't make sense to say
when true and return is ok send a false

Odd Ruby If Statement Question

I've noticed this little oddity(I think) in If Statements in Ruby. Here's an example:
my_number = nil
if my_number < 3
'number is less than 3'
end
Obviously, when you run this code you'll get a "comparison of Fixnum with nil failed" error. Now here's something strange. If I make a little change in the If Statement to check for nil, it works fine. Example:
my_number = nil
if my_number && my_number < 3
'number is less than 3'
end
Adding the check for nil makes it not crash. This may all sound stupid but I can't figure out why that works. Shouldn't it still throw an error?
Thanks to anyone who can explain this. :) Thanks!
Boolean expressions are evaluated in what is known as "short circuit" fashion. That is, as soon as it know the result, it doesn't keep trying to evaluate expressions.
So it does the if my_number and since that's false, there is no need to continue, because false && <anything> is always false.
This is a useful language feature, and many languages work like this (the original Visual Basic is one exception that I can think of) because it lets you do exactly this sort of test without requiring cumbersome nested 'if's.
This is no way specific to Ruby: I've noticed this behaviour in all languages I've ever used, from Pascal to Java.
If you have boolean expression b1 && b2 && b3 ... && bn, most languages guarantee that bi will be evaluated from left to right and if some bi turns out to be false, evaluation will be stopped. (because whole expression is false then). Same for boolean || operator.
It has to be OK to test for nil, so if nil or something like it is OK in almost every language.
But for an arithmetic comparison, if it didn't throw an exception it would have to return true or false. Either way is problematic.
True kind of doesn't make sense, as nil would be both < and > than 3.
False is almost as bad, as now nil < 3 and nil >= 3 are both false, and that's not ideal.
So, the comparison method throws an exception. Problem solved.
nil is "falsy". Try this in irb:
irb(main):001:0> puts "Nil is true " if nil
=> nil
irb(main):002:0> puts "Nil is not true " if !nil
Nil is not true
=> nil
nil isn't the same as false, but making it act as such help a lot in loops and other tests:
false == nil
=> false
Since the first part of your and is false ruby does the lazy thing and doesn't even bother to evaluate the next bit, if it's > 3.
What's happening is called short-circuit evaluation. You're statement can be thought of like this:
if( my_number )
if( my_number < 3)
'number is less than 3'
end
end
Since the first condition is false there's no reason to evaluate the second condition of the statement -- the my_number < 3 part.

Resources