
Boolean Operators
As we know, Booleans tell us whether a value or condition is true and false. We can work with Booleans using the following three operators:
- AND
- OR
- NOT
The AND Operator
In Ruby, the AND operator is represented by a double ampersand, &&. It represents what the truthiness is across two values if both are true. Consider the following sample code snippet:
var1 = true
var2 = true
var1 && var2
var1 = false
var2 = true
var1 && var2
var1 = false
var2 = false
var1 && var2
The output should look like this:

Figure 3.1: Output for the Boolean AND operator
In the preceding example, the var1 and var2 variables depict true and false Boolean states in different combinations and the && operator gives results as per the combination.
The OR Operator
In Ruby, the OR operator is represented by a double pipe, ||. It represents what the truthiness is across two values if one is true. Consider the following sample code snippet:
var1 = true
var2 = true
var1 || var2
var1 = false
var2 = true
var1 || var2
var1 = false
var2 = false
var1 || var2
The output should look like this:

Figure 3.2: Output for the Boolean OR operator
As depicted in the previous example, the var1 and var2 variables depict true and false Boolean states in different combinations and the || operator gives results as per the combination.
The NOT Operator
In Ruby, the NOT or negation operator is represented by a bang or exclamation point (!). It represents the opposite of a value. If a value is true, the negation of it is false, and if a value is false, the negation of it is true:
var1 = true
!var1
var1 = false
!var1
The output should look like this:

Figure 3.3: Output for the Boolean NOT operator
Truth Tables
The possibilities of these operators are best described with a truth table. Here is a sample truth table for the AND and OR operators:

Figure 3.4: Truth table for the AND and OR operators
As you can see from this table, there are some rules that can be inferred:
- With AND, the result is true only if both are true.
- Conversely, with AND, the result is false if either of the variables is false.
- With OR, the result is true if either of the variables is true.
- Conversely, with OR, the result is only false if both variables are false.
The following table is a truth table for the NOT operator:

Figure 3.5: Truth table for the NOT operator
As seen in the preceding table, the following statements can be inferred:
- The result is true if the variable is false.
- The result is false if the variable is true.
Truth tables are the base of Boolean algebra, which is beyond the scope of this book. However, let's cover two additional interesting properties that occur when we combine the negation operator with the AND and OR operators.
Consider the following statements:
- !(x && y) is the same as saying !x || !y
- !(x || y) is the same as saying !x && !y
Another way of looking at this is to say if you want to negate an operator used with two variables, then you distribute the negation to each variable and "flip" the operator. Flipping the operator means to switch between && and ||.
We can confirm this with a truth table, which is just a representation of two variables and all the possible combinations of true and false and the result of each operator:

Figure 3.6: Truth table of flipped operators
Here is another truth table:

Figure 3.7: Truth table of flipped operators
This is an example of one of the many laws of Boolean algebra.
Understanding Booleans and truthiness is a foundational concept that will lead us through to the use of all other aspects of program flow in Ruby.
Truthiness
As we mentioned, in Ruby, you can have Booleans that are direct representations of true and false. However, other values, such as strings and numbers, can be evaluated as to whether they should be true or false.
Take the number 0, for instance. Is the number 0 a true or false value? In other words, if we had an if condition statement that evaluated 0, Ruby should choose the true branch or the false branch. This is what we mean by "truthy." If a variable is "truthy," it means it doesn't contain a direct true value but will be treated as true for the purposes of program flow or Boolean evaluation. Different languages handle truthiness in different ways. Here is the truthiness table for Ruby:

Figure 3.8: Truthiness table for Ruby
As you can see, 0 evaluates to truthy In fact, in Ruby, almost everything evaluates to true except for nil and false. We can see this in an IRB console using a special trick: the bang, (!), and the double bang, (!!).
! is the negation operator in Ruby and therefore gives us the Boolean opposite of whatever value we are evaluating. When we apply the double bang operator, !!, we will get the Boolean opposite of the variable, which if you are following, gives us the Boolean value of the variable.
Let's look at the following example:
def is_truthy(var)
if var
puts "The var #{var} is truthy!"
else
puts "The var #{var} is falsey"
end
end
is_truthy(0) # "The var 0 is truthy
is_truthy(nil) # "The var is falsey"
is_truthy(false) # "The var false is falsey"
is_truthy("") # "The var is truthy!"
is_truthy(5) # "The var 5 is truthy!"
is_truthy("hello") # "The var hello is truthy!"
is_truthy(!0) # "The var false is falsey"
is_truthy(!!0) # "The var true is truthy!"
is_truthy(!nil) # "The var true is truthy!"
is_truthy(!!nil) # "The var false is falsey"
The output should look like this:

Figure 3.9: Output for truthiness
Precedence
In Ruby, there are both && and and as well as the || and or syntaxes. They serve the same purpose in that they both do a logical comparison, however, there is a small and notable difference in how they are interpreted by Ruby. The difference lies in the precedence of operators. In other words, Ruby will process some operators sooner than other operators.
The following is a table on operator precedence in Ruby

Figure 3.10: Operator precedence
We can see that the logical && and || operators have higher precedence than the AND and OR operators. In most cases, the difference in precedence is negligible. However, there are a couple of situations where it is important, particularly when doing variable assignment:
var1 = true AND 1 # var1 == true
var2 = true && 2 # var2 == 2
In the preceding example, var1 is true because the variable assignment has higher precedence than the"and operator. However, var2 is equal to 2 because the && operator is evaluated first and is then assigned to the variable. The astute reader will note that even though true && 2 is a logical comparison, the assigned value is 2. Ruby will return the actual value of logical comparison if it is true. The value it returns depends on the operator.
Consider the following example:
var1 = 5 || true # var1 == 5
var2 = true or 5 # var2 == true
var3 = false || nil # nil
In the case of var1, we see that even though we are using the || operator, which has high precedence, var1 is getting assigned 5. This is because when Ruby processes the || operator, it will stop processing once it has satisfied the || operator (that is, it has reached a true condition). In the case of var2, the assignment operation happens first (var2 is assigned to true) and then is or'd with 5 (which is also true). In the case of var3, the Ruby interpreter can't satisfy the or condition with false, so it proceeds and then hits nil, which is then returned to the assignment operation and gets assigned to var3.
The general rule of thumb is that it is safe to use and and or in a program flow (for example, if/else conditions), whereas you always use && and || when doing variable assignment. While the and and or syntax is more readable than && and ||, it avoids sinister bugs that come up due to oversights in the order of precedence. Therefore, it is recommended to always use && and ||.
Exercise 3.01: Implementing Boolean Operators
In this exercise, we will demonstrate how Boolean operators work by creating a method that evaluates each of the operators. We can then pass different values in to see how the Boolean operators work. The following steps will help you with the solution:
- Open a new session on IRB.
- We first create a method that runs through all the scenarios of Boolean operators using two arguments. As you already know, puts is used to print the result of the operation:
def truthiness(x, y)
puts "AND: ", x && y
puts "OR: ", x || y
puts "!AND: ", !(x && y)
puts "!OR: ", !(x || Y)
end
- Apply the operators to different values for x and y:
truthiness(true, true)
truthiness(true, false)
truthiness(false, true
truthiness(false, false)
truthiness(0, true)
truthiness(nil, false)
Here's the expected output:
Figure 3.11: Output for Boolean operators
Conditional Expressions
Now that we are well-versed in Booleans and truthiness, we can begin to dive into program flow. We'll begin by discussing branches, which are also known as conditionals.
There are several keywords that denote conditionals:
- if
- else
- elsif
- unless
In Ruby, conditionals are structured into blocks of code that begin with a conditional keyword and end with an end keyword.
Let's look at each of them, one by one.
The if Statement
The if conditional is used to evaluate the existing condition of a variable and display the result accordingly. Consider the following example:
good_weather = true
if good_weather
puts "Go outside"
end
The output should look like this:

Figure 3.12: Output for the if statement
Here, we initialize a good_weather variable, which will represent whether the weather is good. If it's good weather, we will output a statement telling us to go outside.
Next, we will see how to branch our code to handle good and bad, which are the weather conditions.
The else Statement
An else condition will help provide an alternate result if the desired condition is not satisfied. Consider the following example:
good_weather = true
if good_weather
puts "Go outside"
else
puts "Stay inside"
end
The output should look like this:

Figure 3.13: Output for the else statement
In this example, if the weather is good, we will get an output statement: Go outside. If not, the statement will be Stay inside.
The elsif Statement
We can use the else keyword to represent an either/or conditional. If we have more than two choices, we can use the elsif keyword to match multiple conditions. Look at the following example:
bananas_are_fresh = true
oranges_are_fresh = true
if bananas_are_fresh
puts "Make a smoothie"
elsif oranges_are_fresh
puts "Make orange juice"
else
puts "Go to the farmers market to get more fresh fruit"
end
In the preceding example, there are two variables, bananas_are_fresh and oranges_are_fresh, which are set to true. If the first condition is met, the output statement will be Make a smoothie. If the second condition is also met, the output will be Make orange juice. If both conditions are not met, the output will be Go to the farmers market to get more fresh fruit.
The output should look like this:

Figure 3.14: Output for the elsif statement
The unless Statement
If statements, like most other programming languages, are saying if the following evaluation is true, then execute the next block of code. Ruby has an additional keyword to express a conditional when something is false. That keyword is unless. Look at the following example:
its_raining = false
unless its_raining
puts "Go outside"
end
The output should look like this:

Figure 3.15: Output for the unless statement
As you might guess, the unless keyword is the same as saying if not. While Ruby has elsif for additional conditions within an if/else block, there is no corresponding keyword for unless. Therefore, unless can only be paired with else, as in the following example:
its_raining = false
unless its_raining
puts "Go outside"
else
puts "Stay inside"
end
When deciding which keyword to use, one of the considerations should be readability. Which form makes it more readable to you and/or for others later on?
Let's look at an example that combines conditional keywords with Boolean operators:
its_raining = false
its_cold = true
if its_raining && its_cold
puts "Bring rain jacket"
elsif its_raining
puts "Bring umbrella"
else
puts "It's nice outside, just don't forget your wallet"
end
The preceding code clearly depicts the use of the && Boolean operator to combine two conditions and give out the result accordingly. Additionally, if one of the conditions is met, the output will be Bring umbrella. If both conditions are not met, the output would be It's nice outside, just don't forget your wallet.
The output should look like this:

Figure 3.16: Output for the if statement
Note
It's good practice to steer clear of excessively lengthy and nested if/else statements. This is called a "code smell" because it makes you wince when you try to understand it. It's a good time to consider whether there is a better way to organize the code.