Conditioning tri-state logic - ruby

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.

Related

Why does an empty String returns false when comparing to boolean in ruby?

It might be a dumb question, but can someone please explain this:
if ""
true
else
false
end
=> true (OK)
!!("")
=> true (OK)
"" === true
=> false (OK)
"" == true
=> false (But why?)
Have a nice day!
The basic concept of this question is a deep misunderstanding of Ruby operators. Here's the short of it - there's no such thing as operators in Ruby! All this !, =, == and === that you throw around - these are not operators.
So what's going on?
Ruby is an object oriented language (a real one, not like that fake Java and JavaScript things) and all these fancy character sequences you think of as operators are actually method calls on the object in their left:
== method usually checks for equality - is the object on the right equal in content.
=== method is not used for "strict equality" or "identity" like it is often in other languages - some Ruby objects that implement it use it for "membership" tests, like in ranges ((1..3) === 2 ==> true) or regular expressions (/el/ === "hello" ==> true you can think of regular expressions as the group of all strings that would match), others implement it as equals.
So - how is the if working? Well, if and other "forced boolean contexts" check for "falsehood". In Ruby we recognize two false values - false and nil, everything else will run the truth branch of an if. This is actually the method ! that you also used in your second example. This method is implemented in BasicObject to return true for all objects, except if the object's type is the type of false - FalseClass or the type for nil - NilClass, in which case it returns true.
So your examples actually mean:
Check for the truthiness of "". Because that value's type is neither FalseClass or NilClass, it is always true.
For the value "", call the method ! - which will return false because the object is neither a FalseClass or a NilClass - then call ! on the result, which will return true because false is a FalseClass instance.
For the value "" call the method === with the argument true, but since it is an alias for == - call that instead (see 4).
For the value "" call the method == with the argument true. String's implementation of == will never return true for an argument that isn't a String type.
The only falsy values in Ruby are false and nil. Everything else is "truthy," but not necessarily equal to true. This includes empty objects like a String, Hash, or Array.
Pragmatically, it might help to think of == as "comparably equivalent to" rather than "equals." For example:
1 == 1.0 #=> true
This is true even though one is an Integer and one is a Float because they are comparably equivalent in value, even if they aren't the same object or of the same type.
In the same way, "" is truthy because it is not comparably equivalent to false or nil. However, it's also not the same object type as true, nor comparably equivalent to true. An empty String is simply "not falsy," which makes it truthy but not actually true.
Remember, only false and nil are falsy. Everything else, and I mean everything, is truthy even if it isn't strictly speaking true.
In Ruby, the only "falsey types" are FalseClass and NilClass, which have the instances false and nil respectively. All other values are considered "truthy". This is possibly different to what you'd expect coming from other C-like values, in which we do things like this pretty freely:
int x = get_value();
if (x) { /* implied x != 0 }
So, if you had something like this in Ruby:
puts 0 if 0 # => "0"
puts 1 if "" # => "1"
puts 2 if [] # => "2"
puts 3 if false # => nil
puts 4 if true # => "4"
puts 5 if nil # => "5"
So, if "" acts truthy, why isn't it equal to true? If that were how we defined ==, then this would also need to resolve to true then, since both values are truthy:
"1" == "2"
The difference here is that == is asking if two things are the same, which "" and true are most certainly not. Further, Ruby does not automatically convert types for you (like other languages like JavaScript do), so "" does not automatically get converted to a boolean during its comparison.

simple ruby unless method mistake

I am very new to ruby. This might sound very naive to you but I can't understand what is happening here
matched_criteria = match_criteria(c1, c2)
puts "answer is #{matched_criteria}"
def match_criteria(crit_val1, crit_val2)
if crit_val1.present?
puts "present"
else
puts "absent"
end
return true unless crit_val1.present? || crit_val2.present?
return false unless crit_val1.present? && crit_val2.present?
end
Output:
absent
answer is false
please explain
def match_criteria(crit_val1, crit_val2)
# Check if #1 is present? or blank?
if crit_val1.present?
puts "present"
else
puts "absent"
end
# Return true unless EITHER (||) are present
# ...or you could think of it like...
# Return true if BOTH (&&) are blank
return true unless crit_val1.present? || crit_val2.present?
# Return false unless BOTH are present
# ..or...
# Return false if EITHER is blank
return false unless crit_val1.present? && crit_val2.present?
end
This is actually a Ruby on Rails question. blank? and present? are instance methods only available in Rails, but not in the standard Ruby library.
Whenever you call match_criteria with one parameter that is a non-blank value, the condition
crit_val1.present? || crit_val2.present?
will be true, but since you are negating it the first return statement does not get called.
In that same situation the second conditional evaluates to false, and since you're negating it the second return successfully returns false.
Your problem has nothing to do with Ruby (or Rails) per se. It has to do with misunderstanding the logical operators involved, which of course would be a problem in any language.
Consider this:
def test(x, y)
return true unless x || y
false unless x && y
end
p test(true, false) # => false
p test(true, true) # => nil
p test(false, false) # => true
In the first test (x = true, y = false) the first condition fails because x || y is true (it would succeed only if x || y evaluated to false, since you're using unless instead of if). The second condition succeeds because x && y is not true. So, the method returns false.
In the second test, neither condition succeeds, because both x || y and x && y evaluate to true. (Again, since you are using unless instead of if, a condition can only succeed if it evaluates to false.) So, the method returns nil.
In the third test, the first condition succeeds, because x || y evaluates to false. So, the method returns false.
It's most unlikely that you want these logical conditions. In the first place, you probably don't want a set of logical conditions that doesn't cover all the possibilities. Also, I don't see a reason for using unless instead of if; it seems like you're needlessly complicating your logic.
Of course, you haven't explained exactly what logical conditions you're looking for, so we're all playing "blind mechanic" here to some extent. But my feeling is that you are attempting to check whether your crit_val1 or crit_val2 are present, and return false if either of them is. If so, all you would have to do is this:
def match_criteria(crit_val1, crit_val2)
# other stuff
return false if crit_val1.present? || crit_val2.present?
true
end

Ruby (Rails 4.0.2): (1 == true) => false?

My understanding is that in the Ruby programming language, only nil and false are considered false, and EVERYTHING else is considered true.
Thus, 1 == true SHOULD return true.
I was working on a Rails 4.0.2 project using MS SQL Server (boolean field, tinyint(1)) and couldn't get the boolean working properly. The value in the DB column was 1.
Stumped, I started testing some things in the console. Here's what I got in my Rails console:
1 == true # => false
0 == true # => false
"test" == true # => false
"" == true # => false
Why is everything registering as false? I thought they were all supposed to be true?
I ended up having to do my boolean check via <attribute>.to_i == 1 to get a valid true/false value.
Am I missing something in the Ruby language or was it updated in some version I wasn't aware of? I'm using RVM, ruby 2.0.0p481 (2014-05-08 revision 45883) on a Mac machine.
Updated: 2014-10-28 09:00
After reading the excellent answers below, I understand where my thinking was wrong.
I think one of the problems is that most of the DB adapters I use (PostgreSQL, MySQL, etc...) converted boolean values in the DB for me.
For instance, in a lot of my code bases I have:
# User has an :admin boolean attribute
class User < ActiveRecord::Base
def admin?
admin == true
end
end
And I suspect this works "fine" because the DB adapter takes care of converting the DB value to a proper boolean for me.
However, I'm currently work on a project where I'm required to use MS SQL Server, and I've run into many "gotchas" with the DB adapter, one of which is I don't believe it treats boolean fields the same as the other adapters do.
Thus, I always considered any TRUTHY value to be equal to true, which it is not. I guess that's why rspec and others have moved to use be_truthy in their own syntax instead of purely be_true.
I'm just surprised it took me a few years before I ran into a problem like this!
My understanding is that in the Ruby programming language, only nil and false are considered false, and EVERYTHING else is considered true
This is true.
However,
"Thus, 1 == true SHOULD return true."
is false.
Explanation: in Ruby everything except false/nil is considered true. However when testing for equality, like x == true you're actually not testing "is this value considered true" but "is this value equal to true".
And of course 1 isn't equal to true, 1 is a number and true is a boolean value. You can read a very good blog post about equality in Ruby at "Ruby Equality And Object Comparison".
If you really, really want to test it that way, you need to convert the expression like 1 to its boolean value, which you can do by double negating it. So !!1 # => true.
So, your examples will look like this:
!!1 == true # => true
!!0 == true # => true
!!"test" == true # => true
!!"" == true # => true
However I believe it isn't necessary in most cases.
When working with MSSQL you should let Active Record do all the conversions for you. Declare the field in a migration, with type boolean, and when you do your where search in the database, do it like this:
Model.where(['attribute = ?', true])
Model.where(:attribute=> true)
This way ActiveRecord will do all the work related to converting the type to a database compatible one for you.
In terms of logical predicates every object besides nil and false are TRUTHY. What basically means that statement corresponding our predicate (condition) will be executed.
While nil and false are FALSEY predicate.
In practice it means:
if predicate
statement_a
else
statement_b
end
Whatever is not a predicate, except nil and false statement_a will be executed. Otherwise statement_b will get the jack pot.
First off, your assumption that everything in Ruby that isn't false or nil is considered true, is true.
Let's take each of your four examples:
1 == true # => false
When evaluating this expression, Ruby takes the class of the first argument, looks for the operator method and evaluates. So, for Fixnums, it evaluates whether or not 1 is equal to another Numeric number. Fixnum#== docs that is equivalent to 1.
0 == true # => false
Same as above.
"test" == true # => false
This is doing an equality test through the String class. As stated in the String#== docs, the class is tested on the right side and if it isn't a string, but responsed to to_str (true does), then the Object#=== method is used and the evaluation asks if the two are the same object, which is not the case here.
"" == true # => false
This is the same as above.
Esse's answer is correct in showing that all objects other than false and nil are true.
!!false # => false
!!nil # => false
The == is a method just like any other method in Ruby that a class uses to decide whether the passed parameter (the parameter on the right side) is equal to its object or not, just like you can define your own == method for your classes. In fact you could even modify the way == methods works for any Ruby basic object such as Fixnum if you wish.
So when you say 1 == true what you are actually saying is 1.==(true) in which case the class Fixnum doesn't like to consider the passed parameter true to be equal to its object 1.
Now when you say everything in Ruby except nil and false are considered true, the correct statement is that in Ruby only false and nil are falsey and everything else is truthy. So when Ruby has to decide whether a value or object count as or evaluates as true (such as in an if statement: if ('test')), it determines whether it is truthy or falsey, but not whether (object).==(true) returns true or false

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

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

Writing better Ruby: How to cast a objects to false or true?

In ruby, what is the best/most-elegant way to return a value such as:
#method returns true or false if 'do i match' is present in string
def method(str)
str =~ /do i match/
end
Some people would do:
def foo
!!(str=~/do i match/)
end
# or
def foo
match = str=~/do i match/
!!match
end
The second ! runs the truthiness test and negates the answer, then the first ! negates it again to get the initial truthy result.
I rather prefer the more explicit syntax:
def foo
str =~ /do i match/ ? true : false
end
This does the truthiness, but to me feels clearer. Do what feels cleanest to you.
I may be a heretic for saying it but I think that they implicit return is beautiful. In this case it evaluates to true or false but I can see how people might think that this is unclear.
You could keep the implicit return and make this instead:
str =~ /do i match/ ? true : false
A compromise between readability and performance
!str.match(/do i match/).nil?

Resources