The Ruby Way

Errata

As always, a few errors slipped past us despite our best efforts. This is a list of all the known errors in The Ruby Way, 3rd Edition, and their corrections. Page numbers are accurate for the print and PDF versions of the book.

Section 1.2.5 (page 14)
The line
abort "#{temp} is not a valid number." if temp !~ /\A-?\d+\Z/
should instead read
if temp !~ /\A-?\d+\Z/
  abort "#{temp} is not a valid number."
end
Section 1.2.5 (page 16)
The line
The exclamation point as a prefix
should instead read
The exclamation point as a suffix
Section 1.2.6 (page 18)
The line statement1 if y == 3 should instead read statement1 if y == 37.0.
Section 1.2.6 (page 21)
The line
loop 3 is merely an indirect reference
should instead read
loop 5 is merely an indirect reference
Section 1.2.7 (page 24)
The line
we are allowed to use an else clause after all the rescue clauses
should instead read
we can use another rescue clause to catch any errors uncaught by previous rescue clauses
The following code sample should instead contain this code:
begin
  # Error-prone code...
rescue FirstErrorClass
  # ...
rescue SecondErrorClass
  # ...
rescue
  # Other StandardError exceptions...
end
Section 1.4.2 (page 40)
References to _idtoref should instead read _id2ref.
Section 2.9 (page 73)
The line s1 = str.ljust(13) # "Moby-Dick" should instead read s1 = str.ljust(13) # "Moby-Dick    ".
Section 2.4 (page 106)
The code example
/ghi\Z/ =~ string  # 8
/\Aabc/ =~ str2    # 8
should instead read:
/ghi\Z/ =~ string  # 8
/ghi\Z/ =~ str2    # 8
Section 5.14 (page 174)
Each instance of Prime.new should instead read Prime.instance.
Section 6.2.4 (page 201)
The line
The method member? is an alias.
should instead read:
The include? method is equivalent to to_a.include?, and the method member? is an alias.
Another method, cover?, checks for a different kind of inclusion—whether the value in question falls between the start and end of the range.
Section 6.2.4 (page 202)
The line
Once again, beware of string ranges:
should instead read
Beware when using cover? on string ranges:
The code sample should also read as follows:
s1 = "2".."5"
str = "28"
s1.include?(str) # false (as expected)
s1.cover?(str) # true (misleading!)
Section 7.10 (page 220)
The paragraph starting "Note that the Date class can work with dates prior to the epoch." should be deleted, as it is no longer true.
Section 7.19 (page 225)
The sentence
These are typically based on the UNIX epoch and therefore cannot represent times before 1970.
should instead read
These are typically based on the UNIX epoch, and indeed converting a Time before 1970 using to_i will produce a negative number
Section 7.24 (page 229)
The calendar function should instead be defined by this code:
def calendar(month, year)
  num_days = month_days(month, year)
  list = (1..num_days).to_a

  first_day = Time.mktime(year, month, 1)
  first_day.wday.times { list.unshift(nil) }

  list << nil until (list.size % 7).zero?
  list.each_slice(7).to_a
end
Section 8.1.4 (page 235)
The line
if d < e  # false
should instead read
if (d <=> e) < 0  # true
Section 8.1.16 (page 254)
The final paragraph, beginning "Bear in mind" should instead read
Bear in mind that + and even += will create a new array object, while concat and << methods will modify the existing array. Also bear in mind that << is unique in that it will add only a single new element (which may itself be an array):
In addition, the last line of the final code sample, which reads
a = a.concat(b) # [1,2,3,4]
should instead read
a.concat(b) # [1, 2, 3, 4]
Section 8.1.18 (page 255)
The comment
# ["green","blue","red"]
should instead read
# ["blue", "green", "red"]
Section 8.1.20 (page 256)
The line
with_index (mixed in from Enumerable)
should instead read
with_index (a method on the Enumerable::Enumerator object returned by each)
Section 8.1.26 (page 259)
The code example starting class ZeroArray < Array should instead read:
class ZeroArray < Array

  # When reading a value, return 0 for missing indexes
  def [](x)
    x > size ? 0 : super(x)
  end

  # When setting a value, fill missing indexes with zero
  def []=(index, value)
    (size...index).each{|i| self[i] = 0 }
    super(index, value)
  end

end

num = ZeroArray.new
num[1] = 1  # => [0, 1]
num[2] = 4  # => [0, 1, 4]
num[5] = 25 # => [0, 1, 4, 0, 0, 25]
Section 8.2.1 (page 261)
The final four lines of the first code sample should instead read:
c1 = {"flat" => 3,"curved" => 2}
# For a1 and b1, there must be an even number of elements.
(The {"flat", 3, "curved", 2} Hash creation syntax was removed in Ruby 1.9.)
Section 8.2.3 (page 262)
The line
Hash has class methods [] and []=
should instead read
Hash has instance methods [] and []=
Section 8.2.6 (page 265)
The line
There is no predictable way to tell which one will be used
should instead read
The last-inserted key will be used as the inverted value
Section 8.2.11 (page 268)
The line
An alias for merge is update
should instead read
The merge method creates a new hash, while the merge! method changes the receiver in place. The update method is an alias for merge!
Section 8.3.1 (page 274)
The line
The inject method comes to Ruby via Smalltalk.
should instead read
The inject method (also aliased as reduce) comes to Ruby via Smalltalk.
Additionally, the line
Obviously, this is equivalent to the following piece of code
and the following code example should instead read:
Instead of passing a block, it is also possible to pass a symbol representing the method that should be used on each item. The previous example could instead have been written this way:
sum = nums.inject(:+)
Or even written without using inject, like this:
sum = 0
nums.each {|n| sum += n }
Section 8.3.6 (page 279)
The line
hands << dealer.next } # not actually how a human deals
should instead read
hands << dealer.next # not actually how a human deals
Section 8.4.3 (page 283)
The line
acc[n] += 1 } # Better
should instead read
acc[n] += 1 # Better
Section 8.4.4 (page 283)

The section beginning "The reduce method" and ending "it’s impossible to specify both a binary operator symbol and a block together" should be removed entirely, since this functionality was covered in Section 8.3.1.

In addition, the line of code

arr1 = hash.drop(2) # [[4,8], [5,10], [7 => 14]]
should instead read
arr1 = hash.drop(2) # [[4, 8], [5, 10], [7, 14]]

Finally, the line of code

arr2 = hash.take_while {|k,v| v <= 8 } # [[5,10], [7 => 14]]
should instead read
arr2 = hash.drop_while {|k,v| v <= 8 } # [[5, 10], [7, 14]]

Section 9.1.1 (page 289)
The line
The unary minus
should instead read
The minus method
Section 9.2 (page 292)
The line
The corresponding instance methods in the Array class are called unpush and shift, respectively.
should instead read
The corresponding instance methods on Array are either push and shift, or unshift and pop. The first pair adds to the end of the array and removes from the front, while the second pair adds to the front of the array and removes from the end.
Section 9.2.2 (page 294)
If the given string happens to contain more closing symbols than opening symbols, the paren_match function will raise an exception instead of returning false as it should. To resolve this, change this line of code
top = stack.peek
to instead be these two lines
top = stack.peek
return false if top.nil?
Section 10.1.18 (page 320)
Several instances of $defout and STDOUT should all instead read $stdout.
Section 10.1.19 (page 334-335)
The sentence
Be aware that FileUtils used to mix functionality directly into the File class by reopening it; now these methods stay in their own module.
should instead read
Most of the functionality in the FileUtils module is named after the UNIX utility that provides the same function, as in mv and cp.
In addition, the text
The copy method will copy a file to a new name or location. It has an optional flag parameter to write error messages to standard error. The UNIX-like name cp is an alias:
should instead read
The cp method (aliased copy) will copy a file to a new name or location. The related method cp_r will copy directories recursively.
Lastly, the text
A file may be moved with the move method (alias mv). Like copy, it also has an optional verbose flag:
should instead read
A file or directory may be moved with the mv method, which is aliased as move.
Section 10.1.21 (page 337)
The code arr.size should instead read str.size. The sentence beginning "Obviously because" should instead begin "Because".
Section 10.1.24 (page 339)
The comment "ABC" should all instead read "ABC\n".
Section 10.1.34 (page 343)
The sentence
In Ruby code, we can do this by using the FileUtils.makedirs method:
should instead read
In Ruby code, we can do this by using the FileUtils.mkdir_p method, also aliased as mkpath.
In the code sample below, the text FileUtils.mkpath should instead read FileUtils.mkdir_p.
Section 10.1.35 (page 343)
At the end of the section, this text should be added:
Note that both Pathname#rmtree and FileUtils.rm_r delete files that lack write permissions, making them equivalent to the shell command rm -rf. The similarly-named method FileUtils.rm_rf instead suppresses any StandardError exceptions raised while the removal is running.
Section 10.2.6 (page 351)
The line CSV.open('data.csv', 'r') do |row| should instead read CSV.open('data.csv', 'r').each do |row|.
Section 11.2.5 (page 404)
When introducing lambdas, "lamdba" should instead read "lambda". The code sample at the bottom of the page demonstrating the difference between a Proc and a lambda should instead contain this code:
def philippe_proc
  Proc.new do
    return "Too soon, Philippe!"
  end.call
  "Good morning, everyone."
end

def philippe_lambda
  lambda do
    return "Too soon, Philippe!"
  end.call
  "Good morning, everyone."
end

p philippe_proc   # => "Too soon, Philippe!"
p philippe_lambda # => "Good morning, everyone."
Section 11.2.12 (page 414-415)
The text
In AOP, programmers to deal with
should instead read
In AOP, programmers deal with
Section 11.3.6 (page 426)
Instead of
undef_method will literally cause the method to be undefined (removing it from superclasses as well)
the text should read
undef_method will force a NoMethodError if that method is called later (preventing that method from being found in superclasses or modules)
At the end of the accompanying code sample, the code
x = Child.new
x.alpha          # parent alpha
x.beta           # Error!
should instead read
Child.new.alpha  # parent alpha
Child.new.beta   # Error!
Parent.new.beta  # parent beta
Section 11.3.8 (page 429)
Both Object#method_missing and "the method_missing method defined in class Object" should instead read BasicObject#method_missing.
Section 11.3.9 (page 431-432)
The table row "Cannot load a file whose name is tainted string starting with ~." should be checked for the column 3.

The table rows starting "Cannot use Process" should both be checked in column 3.
Section 12.2.6 (page 467)
The line
At the time of this writing, Ruby/Tk is based on the most recent release, Tk 8.5.
should instead read
Ruby/Tk supports Tk 8.4, 8.5, and 8.6, which is the most recent release of Tk at the time of this writing.
Section 12.4 (page 480)
The line
Qt is distributed via dual license—either the GPL or a purchased commercial license for proprietary work.
should instead read
Qt is distributed via dual license—either LGPLv3 or a purchased commercial license for proprietary work.
Section 13.1.2 (page 498)
The line
Here is an example of thread-local data in action:
should instead read
(Technically, these are not thread-local but fiber-local variables. We'll talk about the cases where this matters soon, in section 13.3.) Here is an example of thread-local data in action:
Section 13.3 (page 530)
At the end of section 13.3, this section should be added:
Section 13.3.1 Fiber-local variables

Back in section 13.1.2, we mentioned that the Thread methods [] and []= manipulate fiber-local variables instead of true thread-local variables. If a thread does not explicitly run additional fibers, those methods provide variables that are functionally identical to thread-local variables.

However, in a thread that executes multiple fibers there is another pair of methods providing access to variables that are shared across all fibers on a single Thread, thread_variable_get and thread_variable_set. Any time you need to set a variable inside one fiber and access it from another fiber that is run on the same thread, you will need to use thread_variable_set and thread_variable_get instead. For more discussion of the differences between thread-local and fiber-local variables, see the Ruby API documentation for Thread#[] and Thread#thread_variable_get.

Section 14.1.2 (page 533-534)

In the second text paragraph, kernel should instead read Kernel.

The line alias old_execute ' should instead read alias old_execute `.

In addition, the line def '(cmd) should instead read def `(cmd).

Section 14.4.1 (page 545-546)
The line
(You would use a semicolon rather than a colon on Windows.)
should instead read
(We use File::PATH_SEPARATOR for compatibility across operating systems—the separator is a colon on UNIX, but a semicolon on Windows.)

In addition, the line dirs = mypath.split(":") should read dirs = mypath.split(File::PATH_SEPARATOR).

Also, the line
Second, an environment variable set by a child is not propagated back up to the parent:
should instead read
After the fork, environment changes are not shared—any changes made in either the parent or child will not apply to the other.
Finally, the line
There is a consequence of the fact that parent processes don’t know about their children’s variables. Because a Ruby program is typically run in a subshell, any
should read
A direct application of this principle is the way that a Ruby program (as a child process) cannot change the environment variables of the shell it was run from (a parent process). Any

open sans font by ascender fonts. icons by the noun project. site design by saliorhg.