2.4 Attributes, booleans, and control flow
Everything in Ruby, including strings, is an object. This means that we can get useful information about strings and do useful things with them using the same dot notation used in many object-oriented languages (e.g., JavaScript, as seen in Learn Enough JavaScript to Be Dangerous).
We’ll start by accessing a string attribute, which is a piece of data attached to an object. In particular, in the console we can use the length
attribute to find the number of characters in a string:
$ irb
>> "badger".length # Accessing the "length" property of a string
=> 6
>> "".length # The empty string has zero length.
=> 0
The length
attribute is especially useful in comparisons, such as checking the length of a string to see how it compares to a particular value (note that the REPL supports “up arrow” to retrieve previous lines, just like the command-line terminal):
>> "badger".length > 3
=> true
>> "badger".length > 6
=> false
>> "badger".length >= 6
=> true
>> "badger".length < 10
=> true
>> "badger".length == 6
=> true
The last line uses the equality comparison operator ==
, which Ruby shares with many other languages. (Note that, like JavaScript, Ruby supports ===
, and indeed has several comparison operators in general, but ==
works in almost all cases of relevance.)
The return values in the comparisons above, which are always either true
or false
, are known as boolean values, after mathematician and logician George Boole (Figure 2.4).7

Boolean values are especially useful for control flow, which lets us take actions based on the result of a comparison (Listing 2.5).
if
.
>> password = "foo"
=> "foo"
>> if (password.length < 6)
>> "Password is too short."
>> end
=> "Password is too short."
Note in Listing 2.5 that the comparison after if
is in parentheses, and the if
statement is terminated by the end
keyword. The latter is required, but in Ruby (unlike many other languages) the parentheses are optional, and it’s common to leave them off (Listing 2.6).
if
and no parentheses.
>> if password.length < 6
>> "Password is too short."
>> end
=> "Password is too short."
Listing 2.5 and Listing 2.6 also follow a consistent indentation convention, which is irrelevant to Ruby but is important for human readers of the code (Box 2.2).
The code samples in this tutorial, including those in the REPL, are designed to show how to format Ruby in a way that maximizes readability and code comprehension. The programs executing Ruby programs, whether irb or Ruby itself, don’t care about these aspects of the code, but human developers do.
While exact styles differ, here are some general guidelines for good code formatting:
-
Indent code to indicate block structure. Pretty much every time you see an opening curly brace
{
, you’ll end up indenting the subsequent line. (Some text editors even do this automatically.) - Use two spaces (typically via emulated tabs) for indentation. Many developers use four or even eight spaces, but I find that two spaces are enough to indicate block structure visually while conserving scarce horizontal space.
- Add newlines to indicate logical structure. One thing I particularly like to do is add an extra newline after a series of variable assignments, in order to give a visual indication that the setup is done and the real coding can begin. An example appears in Listing 4.9.
-
Limit lines to 80 characters (also called “columns”). This is an old constraint, one that dates back to the early days of 80-character-width terminals. Many modern developers routinely violate this constraint, considering it outdated, but in my experience the 80-character limit is a good source of discipline, and will save your neck when using command-line programs like
less
(or when using your code in a document with more stringent width requirements, such as a book). A line that breaks 80 characters is a hint that you should introduce a new variable name, break an operation into multiple steps, etc., to make the code clearer for anyone reading it.
We’ll see several examples of more advanced code-formatting conventions as we proceed throughout the rest of this tutorial.
We can add a second behavior using else
, which serves as the default result if the first comparison is false
(Listing 2.7).
if
and else
.
>> password = "foobar"
>> if password.length < 6
>> "Password is too short."
>> else
>> "Password is long enough."
>> end
=> "Password is long enough."
The first line in Listing 2.7 redefines password
by assigning it a new value. After reassignment, the password
variable has length 6, so password.length < 6
is false
. As a result, the if
part of the statement (known as the if
branch) doesn’t get evaluated; instead, Ruby evaluates the else
branch, resulting in a message indicating that the password is long enough.
Unusually among programming languages, Ruby has a special elsif
keyword meaning “else if”, as shown in Listing 2.8 (Figure 2.5).8
elsif
.
>> password = "goldilocks"
>> if password.length < 6
>> "Password is too short."
>> elsif password.length < 50
>> "Password is just right!"
>> else
>> "Password is too long."
>> end
=> "Password is just right!"

As a final example, it’s worth noting that Ruby allows us to place the if
part after the statement when there’s only one line:
>> password = "foo"
>> "Password is too short." if password.length < 6
=> "Password is too short."
The if
here can be negated using unless
instead, with the opposite comparison as well:
>> "Password is too short." unless password.length >= 6
=> "Password is too short."
It’s essentially never wrong to use if
, but in some cases the conditional sounds better using unless
. I suggest pronouncing the conditional as if it were English and choosing whichever variant sounds more natural.
2.4.1 Combining and inverting booleans
Booleans can be combined or inverted using the &&
(“and”), ||
(“or”), and !
(“bang” or “not”) operators.
Let’s start with &&
. When comparing two booleans with &&
, both have to be true
for the combination to be true
. For example, if I said I wanted both french fries and a baked potato, the only way the combination could be true is if I could answer “yes” (true) to both of the questions “Do you want french fries?” and “Do you want a baked potato?” If my answer to either of those is false, then the combination must be false as well. The resulting combinations of possibilities are collectively known as a truth table; the truth table for &&
appears in Listing 2.9.
&&
(“and”).
>> true && false
=> false
>> false && true
=> false
>> false && false
=> false
>> true && true
=> true
We can apply this to a conditional as shown in Listing 2.10.
&&
operator in a conditional.
>> x = "foo"
>> y = ""
>> if x.length == 0 && y.length == 0
>> "Both strings are empty!"
>> else
>> "At least one of the strings is nonempty."
>> end
=> "At least one of the strings is nonempty."
In Listing 2.10, y.length
is in fact 0
, but x.length
isn’t, so the combination is false
(in agreement with Listing 2.9), and Ruby evaluates the else
branch.
In contrast to &&
, ||
lets us take action if either comparison (or both) is true (Listing 2.11).
||
(“or”).
>> true || false
=> true
>> false || true
=> true
>> true || true
=> true
>> false || false
=> false
We can use ||
in a conditional as shown in Listing 2.12.
||
operator in a conditional.
>> if x.length == 0 || y.length == 0
>> "At least one of the strings is empty!"
>> else
>> "Neither of the strings is empty."
>> end
=> "At least one of the strings is empty!"
Note from Listing 2.11 that ||
isn’t exclusive, meaning that the result is true even when both statements are true. This stands in contrast to colloquial usage, where a statement like “I want fries or a baked potato” implies that you want either fries or a baked potato, but you don’t want both (Figure 2.6).9

In addition to &&
and ||
, Ruby supports negation via the “not” operator !
(often pronounced “bang”), which just converts true
to false
and false
to true
(Listing 2.13).
!
.
>> !true
=> false
>> !false
=> true
We can use !
in a conditional as shown in Listing 2.14. Note that parentheses are required in this case, because otherwise we’re asking if !x.length
is equal to 0
.
!
operator in a conditional.
>> if !(x.length == 0)
>> "x is not empty."
>> else
>> "x is empty."
>> end
=> "x is not empty."
The code in Listing 2.14 is valid Ruby, as it simply negates the test x.length == 0
, yielding true
:
>> !(x.length == 0)
=> true
In this case, though, it’s more common to use !=
(“not equals”):
>> if x.length != 0
>> "x is not empty."
>> else
>> "x is empty."
>> end
=> "x is not empty"
Because we’re no longer negating the entire expression, we can omit the parentheses as before.
2.4.2 Bang bang
Not all booleans are the result of comparisons, and in fact every Ruby object has a value of either true
or false
in a boolean context. We can force Ruby to use such a boolean context with !!
(pronounced “bang bang”); because !
converts between true
and false
, using two exclamation points returns us back to the original boolean:
>> !!true
=> true
>> !!false
=> false
Using this trick allows us to see that a string like "foo"
is true
in a boolean context:
>> !!"foo"
=> true
As it happens, the empty string is also true
in a boolean context:10
>> !!""
=> true
In fact, even 0
is true
in Ruby:
>> !!0
=> true
The only Ruby object that’s false in a boolean context (other than false
itself) is nil
:
>> !!nil
=> false
2.4.3 Exercises
- If
x
is"foo"
andy
is""
(the empty string), what is the value ofx && y
? Verify using the “bang bang” notation thatx && y
is true in a boolean context. Hint: When applying!!
to a compound expression, wrap the whole thing in parentheses. - What is
x || y
? What is it in a boolean context? Rewrite Listing 2.15 to usex || y
, ensuring that the result is the same. (Hint: Switch the order of the strings.)