Errata

Ruby Cookbook

Errata for Ruby Cookbook

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released. If the error was corrected in a later version or reprint the date of the correction will be displayed in the column titled "Date Corrected".

The following errata were submitted by our customers and approved as valid errors by the author or editor.

Color key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted By Date submitted Date corrected
Printed
Page xxv
2nd paragraph under "Installing the Software"

"DarwinParts" is mentioned at the end of the paragraph.
That's supposed to be "DarwinPorts".

Anonymous   
Printed
Page xxx
Matthew Palmer and Chetan Patil are two different people

there's a missing comma between their names.

Anonymous   
Printed
Page xxv
2nd paragraph under "Installing the Software"

"DarwinParts" is mentioned at the end of the paragraph.
That's supposed to be "DarwinPorts".

Anonymous   
Printed
Page xxx
Matthew Palmer and Chetan Patil are two different people

there's a missing comma between their names.

Anonymous   
Printed
Page 2
Last Line:

... of string escaping depending ...
should be:
... of string escaping, depending ...

Anonymous   
Printed
Page 2
Last Line:

... of string escaping depending ...
should be:
... of string escaping, depending ...

Anonymous   
Printed
Page 3
3/4 down the page

string.chr + string.chr + string.chr + string.chr + string.chr
should be:
string[3].chr + string[4].chr + string[5].chr + string[6].chr + string[7].chr

Anonymous   
Printed
Page 3
3/4 down the page

string.chr + string.chr + string.chr + string.chr + string.chr
should be:
string[3].chr + string[4].chr + string[5].chr + string[6].chr + string[7].chr

Anonymous   
Printed
Page 6
first paragraph

irb(main):003:0> data.each { |x| s << x << ' and a ' }

That's a bug. "s << x <<" should be "s << x.to_s <<"

Anonymous   
Printed
Page 6
first paragraph

irb(main):003:0> data.each { |x| s << x << ' and a ' }

That's a bug. "s << x <<" should be "s << x.to_s <<"

Anonymous   
Printed
Page 11
2nd paragraph under Solutions

You can reference any any binary ...
should be:
You can reference any binary ...

Anonymous   
Printed
Page 11
2nd paragraph under Solutions

You can reference any any binary ...
should be:
You can reference any binary ...

Anonymous   
Printed
Page 12

mneumonic
should be:
mnemonic
(occurs four times!)

Anonymous   
Printed
Page 12
next-to-last paragraph

"C-_x_" represents ....
.. should be ...
"C-x" represents ....

... and "M-_x_" represents ...
.. should be ...
... and "M-x" represents

This was an attempt at formatting. "x" is the variable X, not the letter 'x'.
The text should read "C-x" and "M-x" as stated, but "x" should be italicized.
The meaning is "C-whatever" and "M-whatever".

Anonymous   
Printed
Page 12

mneumonic
should be:
mnemonic
(occurs four times!)

Anonymous   
Printed
Page 12
next-to-last paragraph

"C-_x_" represents ....
.. should be ...
"C-x" represents ....

... and "M-_x_" represents ...
.. should be ...
... and "M-x" represents

This was an attempt at formatting. "x" is the variable X, not the letter 'x'.
The text should read "C-x" and "M-x" as stated, but "x" should be italicized.
The meaning is "C-whatever" and "M-whatever".

Anonymous   
Printed
Page 14
2nd paragraph under Solution

... of a particular in a ...
should be:
... of a particular character in a ...

Anonymous   
Printed
Page 14
2nd paragraph under Solution

... of a particular in a ...
should be:
... of a particular character in a ...

Anonymous   
Printed
Page 15
2nd line under Discussion

becase
should be:
because

Anonymous   
Printed
Page 15
2nd line under Discussion

becase
should be:
because

Anonymous   
Printed
Page 16
Discussion, 2nd paragraph, last sentence

... and each_bytes yields ..

should read

... and each_byte yields

Anonymous   
Printed
Page 16
Discussion, 2nd paragraph, last sentence

... and each_bytes yields ..

should read

... and each_byte yields

Anonymous   
Printed
Page 18
first paragraph, last line

... (there are some samples are in the Discussion).
should be:
... (there are some samples in the Discussion).

Anonymous   
Printed
Page 18
regular expression

/(w+([-'.]w+)*/
should read:
/(w+([-'.]w+)*)/

Anonymous   
Printed
Page 18
first paragraph, last line

... (there are some samples are in the Discussion).
should be:
... (there are some samples in the Discussion).

Anonymous   
Printed
Page 18
regular expression

/(w+([-'.]w+)*/
should read:
/(w+([-'.]w+)*)/

Anonymous   
Printed
Page 21
Fifth code block (where code blocks are separated by double new lines)

Missing double quote in first gsub argument:

"Line one
Line two ".gsub(
", "
")

should read:

"Line one
Line two ".gsub("
", "
")

Anonymous   
Printed
Page 21
Fifth code block (where code blocks are separated by double new lines)

Missing double quote in first gsub argument:

"Line one
Line two ".gsub(
", "
")

should read:

"Line one
Line two ".gsub("
", "
")

Anonymous   
Printed
Page 22
2nd paragraph

In rare cases you may ...
should be:
In rare cases, you may ...

Anonymous   
Printed
Page 22
2nd paragraph

In rare cases you may ...
should be:
In rare cases, you may ...

Anonymous   
Printed
Page 24
bottom of page

nonASCII
should be:
non-ASCII

Anonymous   
Printed
Page 24
bottom of page

nonASCII
should be:
non-ASCII

Anonymous   
Printed
Page 25
6th paragraph, or 3rd paragraph after "Discussion" heading

CHANGE:

"String#count is a method that takes a strong of bytes ..."
TO:
"String#count is a method that takes a string of bytes ..."

CHANGE:

"... and counts how many times those bytes occurs in the string."
TO:
"... and counts how many times those bytes occur in the string."

Anonymous   
Printed
Page 25
6th paragraph, or 3rd paragraph after "Discussion" heading

CHANGE:

"String#count is a method that takes a strong of bytes ..."
TO:
"String#count is a method that takes a string of bytes ..."

CHANGE:

"... and counts how many times those bytes occurs in the string."
TO:
"... and counts how many times those bytes occur in the string."

Anonymous   
Printed
Page 27
10th non-empty line up from See Also

end
end

... should be ...

end
end

Anonymous   
Printed
Page 27
10th non-empty line up from See Also

end
end

... should be ...

end
end

Anonymous   
Printed
Page 41
Last paragraph

"Its extract_numbers method..." should be
"Its extract method..."

Anonymous   
Printed
Page 41
Last paragraph

"Its extract_numbers method..." should be
"Its extract method..."

Anonymous   
Printed
Page 43
Solution, second line

"and work as well as as floats"

should be

"and work as well as floats"

Anonymous   
Printed
Page 43
Solution, second line

"and work as well as as floats"

should be

"and work as well as floats"

Anonymous   
Printed
Page 47
2nd paragraph

"...and the toal number..." should be
"...and the total number..."

Anonymous   
Printed
Page 47
2nd paragraph

"...and the toal number..." should be
"...and the total number..."

Anonymous   
Printed
Page 53
In "logb1(x)", "logb2(x)", and "logb2(k)", "b1" and "b2"

are subscripted. This is almost correct, but the "1" and "2" in "b1"
and "b2" need to be sub-sub-scripted, because "1" and "2" are
themselves subscripts of "b". To represent it as ASCII art:

log
b
1

In "The log base k of x, or logk(x)", the k is correctly subscripted,
but the "(x)" appears to be _superscripted_. As with the "logb1(x)"
and the other earlier examples, the (x) needs to be normal text, on
the same level as "log". Again, ASCII art to the rescue:

log (x)
k

Anonymous   
Printed
Page 53
In "logb1(x)", "logb2(x)", and "logb2(k)", "b1" and "b2"

are subscripted. This is almost correct, but the "1" and "2" in "b1"
and "b2" need to be sub-sub-scripted, because "1" and "2" are
themselves subscripts of "b". To represent it as ASCII art:

log
b
1

In "The log base k of x, or logk(x)", the k is correctly subscripted,
but the "(x)" appears to be _superscripted_. As with the "logb1(x)"
and the other earlier examples, the (x) needs to be normal text, on
the same level as "log". Again, ASCII art to the rescue:

log (x)
k

Anonymous   
Printed
Page 55
In the phrase "log(10)*power", "power" is superscripted.

It should be normal text.

Anonymous   
Printed
Page 55
In the phrase "log(10)*power", "power" is superscripted.

It should be normal text.

Anonymous   
Printed
Page 61
third paragraph

"...you'll get the same results whether you multiply A by B and then the result by C, or multiply B by C and then the result by A."

would be less confusing if it read

"...you'll get the same results whether you multiply A by B and then the result by C, or multiply B by C and then A by the result."

since multiplication of matrices is non-abelian.

Anonymous   
Printed
Page 61
third paragraph

"...you'll get the same results whether you multiply A by B and then the result by C, or multiply B by C and then the result by A."

would be less confusing if it read

"...you'll get the same results whether you multiply A by B and then the result by C, or multiply B by C and then A by the result."

since multiplication of matrices is non-abelian.

Anonymous   
Printed
Page 73
in code comments

"is not in ASCII nor Unicode" is incorrect. As stated later in the
recipe, there is a Unicode character for a V with a bar over it.
Omit "nor Unicode".

Anonymous   
Printed
Page 73
in code comments

"is not in ASCII nor Unicode" is incorrect. As stated later in the
recipe, there is a Unicode character for a V with a bar over it.
Omit "nor Unicode".

Anonymous   
Printed
Page 88
2nd block of code, 8th line

t.wday # => 3 # Numeric day of week; Sunday
is 0

It should look like this:

t.wday # => 3 # Numeric day of week; Sunday
# # is 0

Anonymous   
Printed
Page 88
2nd block of code, 8th line

t.wday # => 3 # Numeric day of week; Sunday
is 0

It should look like this:

t.wday # => 3 # Numeric day of week; Sunday
# # is 0

Anonymous   
Printed
Page 107
1st paragraph

"...the dstination time zone's offset..." should be
"...the destination time zone's offset..."

Anonymous   
Printed
Page 107
1st paragraph

"...the dstination time zone's offset..." should be
"...the destination time zone's offset..."

Anonymous   
Printed
Page 109
4th Line under Discussion

"sysem" should be "system"

Anonymous   
Printed
Page 109
4th Line under Discussion

"sysem" should be "system"

Anonymous   
Printed
Page 125
4th paragraph

"Iterate over the array with Enumerable#each."
Change "Enumerable#each" to "Array#each".

Anonymous   
Printed
Page 125
1st paragraph under Discussion

... a code block fed to an method like...
should be
... a code block fed to a method like...

Anonymous   
Printed
Page 125
4th paragraph

"Iterate over the array with Enumerable#each."
Change "Enumerable#each" to "Array#each".

Anonymous   
Printed
Page 125
1st paragraph under Discussion

... a code block fed to an method like...
should be
... a code block fed to a method like...

Anonymous   
Printed
Page 135
3rd paragraph

'occurance' is a misspelling -- it should be 'occurrence'.

Anonymous   
Printed
Page 135
3rd paragraph

'occurance' is a misspelling -- it should be 'occurrence'.

Anonymous   
Printed
Page 136

The given implementation of SortedArray changes the semantics of some
of Array's methods. For instance, SortedArray#insert and
SortedArray#push only take one argument, and SortedArray#reverse!
returns nil instead of an array. This implementation works more like
Array.

Replace the first code sample with this:

class SortedArray < Array

def initialize(*args, &sort_by)
@sort_by = sort_by || Proc.new { |x, y| x <=> y }
super(*args)
sort!(&sort_by)
end

def insert(ignore, *values)
values.each do |v|
# The next line could be further optimized to perform a
# binary search.
insert_before = index(find { |x| @sort_by.call(x, v) == 1 })
super(insert_before ? insert_before : -1, v)
end
end

def <<(v)
insert(0, v)
self
end

def push(*values)
insert(0, *values)
end
alias unshift push

Replace the second code sample with this:

%w[ []= collect! flatten! ].each do |method_name|
class_eval %{
def #{method_name}(*args)
super
sort!(&@sort_by)
end
}
end

def reverse
Array.new(super) # Return a normal array.
end

def reverse!
self
end
end

Anonymous   
Printed
Page 136

The given implementation of SortedArray changes the semantics of some
of Array's methods. For instance, SortedArray#insert and
SortedArray#push only take one argument, and SortedArray#reverse!
returns nil instead of an array. This implementation works more like
Array.

Replace the first code sample with this:

class SortedArray < Array

def initialize(*args, &sort_by)
@sort_by = sort_by || Proc.new { |x, y| x <=> y }
super(*args)
sort!(&sort_by)
end

def insert(ignore, *values)
values.each do |v|
# The next line could be further optimized to perform a
# binary search.
insert_before = index(find { |x| @sort_by.call(x, v) == 1 })
super(insert_before ? insert_before : -1, v)
end
end

def <<(v)
insert(0, v)
self
end

def push(*values)
insert(0, *values)
end
alias unshift push

Replace the second code sample with this:

%w[ []= collect! flatten! ].each do |method_name|
class_eval %{
def #{method_name}(*args)
super
sort!(&@sort_by)
end
}
end

def reverse
Array.new(super) # Return a normal array.
end

def reverse!
self
end
end

Anonymous   
Printed
Page 144
example at bottom of page

In the 'to_s' method of class Card:
"#{@suit} of #{@rank}" should be
"#{@rank} of #{@suit}"

In the 'initialize' method of class Deck, the parameters in the call to Card.new should be reversed, ie.
"Card.new(rank, suit)" should be
"Card.new(suit, rank)"

Anonymous   
Printed
Page 144
example at bottom of page

In the 'to_s' method of class Card:
"#{@suit} of #{@rank}" should be
"#{@rank} of #{@suit}"

In the 'initialize' method of class Deck, the parameters in the call to Card.new should be reversed, ie.
"Card.new(rank, suit)" should be
"Card.new(suit, rank)"

Anonymous   
Printed
Page 150
1st paragraph of Discussion

"...extract one particular elements, or..." should be
"...extract one particular element, or...

Anonymous   
Printed
Page 150
1st paragraph of Discussion

"...extract one particular elements, or..." should be
"...extract one particular element, or...

Anonymous   
Printed
Page 154
irb example for Cartesian product (near bottom of page)

In the "Cartesian product" subsection of section 4.14 "Computing Set Operations and
Arrays" the result for the example [1,2,3].cartesian(["a",5,6]) is missing a final
closing square bracket.

Anonymous   
Printed
Page 154
irb example for Cartesian product (near bottom of page)

In the "Cartesian product" subsection of section 4.14 "Computing Set Operations and
Arrays" the result for the example [1,2,3].cartesian(["a",5,6]) is missing a final
closing square bracket.

Anonymous   
Printed
Page 157
3rd paragraph

"...This puts 4 is in a..." should be
"...This puts 4 in a..."

Anonymous   
Printed
Page 157
3rd paragraph

"...This puts 4 is in a..." should be
"...This puts 4 in a..."

Anonymous   
Printed
Page 195

The XOR operator alone toggles permission bits rather than clearing
them. To clear bits it must be combined with the AND operator.

Replace the last paragraph before the first code sample with this:

Use the XOR (^) and the AND (&) operators to remove permissions from a
bitmap. Use the OR operator, as seen above, to add permissions:

Replace this line of code:

new_permission = File.lstat("my_file").mode ^ File::O_R

with this:

new_permission = File.lstat("my_file").mode & (0777 ^ File::O_R)

Anonymous   
Printed
Page 195

The XOR operator alone toggles permission bits rather than clearing
them. To clear bits it must be combined with the AND operator.

Replace the last paragraph before the first code sample with this:

Use the XOR (^) and the AND (&) operators to remove permissions from a
bitmap. Use the OR operator, as seen above, to add permissions:

Replace this line of code:

new_permission = File.lstat("my_file").mode ^ File::O_R

with this:

new_permission = File.lstat("my_file").mode & (0777 ^ File::O_R)

Anonymous   
Printed
Page 203
2nd paragraph

"By passing '
' into IO#each or IO#readlines, you can handle the
newlines of files created on any recent operating system."

Should be changed to:

"By passing "
" into IO#each or IO#readlines, you can handle the
newlines of files created on many operating systems."

With a footnote:

"Technically, Unix uses "
", Mac OS X uses either "
" or " ", and
Windows uses "
", so to support all three systems, the logic will
be slightly more complex."

Anonymous   
Printed
Page 203
2nd paragraph

"By passing '
' into IO#each or IO#readlines, you can handle the
newlines of files created on any recent operating system."

Should be changed to:

"By passing "
" into IO#each or IO#readlines, you can handle the
newlines of files created on many operating systems."

With a footnote:

"Technically, Unix uses "
", Mac OS X uses either "
" or " ", and
Windows uses "
", so to support all three systems, the logic will
be slightly more complex."

Anonymous   
Printed
Page 240
top of page.

"decided refer to"
should be
"decided to refer to"

Anonymous   
Printed
Page 240
1st sentence of Solution

"By this time, you should familiar with..." should be
"By this time, you should be familiar with..."

Anonymous   
Printed
Page 240
top of page.

"decided refer to"
should be
"decided to refer to"

Anonymous   
Printed
Page 240
1st sentence of Solution

"By this time, you should familiar with..." should be
"By this time, you should be familiar with..."

Anonymous   
Printed
Page 242
Last sentence on the page

The last sentence on the page is missing a word (either "if" or "though")

"When you call it, it's exactly as the code block were a Proc object and you had invoked its call method."

should be:

"When you call it, it's exactly as if the code block were a Proc object and you had invoked its call method."

Anonymous   
Printed
Page 242
Last sentence on the page

The last sentence on the page is missing a word (either "if" or "though")

"When you call it, it's exactly as the code block were a Proc object and you had invoked its call method."

should be:

"When you call it, it's exactly as if the code block were a Proc object and you had invoked its call method."

Anonymous   
Printed
Page 253
the line that says

"...you can use the iterator method to build an Enumerable object"
should read
"...you can use the iterator method to build an Enumerator object".

Anonymous   
Printed
Page 253
. Discussion, line 2

"the simplest and most common data type, and the most common"
is redundant. Omit ", and the most common".

Anonymous   
Printed
Page 253
the line that says

"...you can use the iterator method to build an Enumerable object"
should read
"...you can use the iterator method to build an Enumerator object".

Anonymous   
Printed
Page 253
. Discussion, line 2

"the simplest and most common data type, and the most common"
is redundant. Omit ", and the most common".

Anonymous   
Printed
Page 259
2nd paragraph of text

"you can make a generation object of out of any piece of iteration code"

should be

"you can make a generation object out of any piece of iteration code"

Anonymous   
Printed
Page 259
2nd paragraph of text

"you can make a generation object of out of any piece of iteration code"

should be

"you can make a generation object out of any piece of iteration code"

Anonymous   
Printed
Page 260
[recipe 7.10] solution

def between_setup_and_cleanup
setup
begin
yield
finally
cleanup
end
end

There is no such keyword as "finally" in Ruby. Substitute "ensure" for "finally".

Anonymous   
Printed
Page 260
[recipe 7.10] solution

def between_setup_and_cleanup
setup
begin
yield
finally
cleanup
end
end

There is no such keyword as "finally" in Ruby. Substitute "ensure" for "finally".

Anonymous   
Printed
Page 267
2nd paragraph

The first sentence of this paragraph starts:
Strict languages enforce strong typing, usually at compile type:
It should say:
Strict languages enforce strong typing, usually at compile time:

Anonymous   
Printed
Page 267
2nd paragraph

The first sentence of this paragraph starts:
Strict languages enforce strong typing, usually at compile type:
It should say:
Strict languages enforce strong typing, usually at compile time:

Anonymous   
Printed
Page 269
1st sentence in 1st paragraph

change "...from an Java collection?)"
to "...from a Java collection?)"

Anonymous   
Printed
Page 269
1st sentence in 1st paragraph

change "...from an Java collection?)"
to "...from a Java collection?)"

Anonymous   
Printed
Page 271
line 2

"directly access to" should be "directly access"

Anonymous   
Printed
Page 271
line 2

"directly access to" should be "directly access"

Anonymous   
Printed
Page 297
Last paragraph

"...were recieved by the..." should be
"...were received by the..."

Anonymous   
Printed
Page 297
Last paragraph

"...were recieved by the..." should be
"...were received by the..."

Anonymous   
Printed
Page 317

The paragraph starting "Your module can define an initialize method..." gives an inaccurate
picture. Add a sentence after the first sentence in the paragraph, like this:

...sometimes that doesn't work. If a module defines initialize, a
class that includes the module will no longer be able to call its
superclass's initialize method. There may also be a mismatch of
arguments. For instance, Taggable...

Anonymous   
Printed
Page 317

The paragraph starting "Your module can define an initialize method..." gives an inaccurate
picture. Add a sentence after the first sentence in the paragraph, like this:

...sometimes that doesn't work. If a module defines initialize, a
class that includes the module will no longer be able to call its
superclass's initialize method. There may also be a mismatch of
arguments. For instance, Taggable...

Anonymous   
Printed
Page 327
last line of text just before Discussion

Spelling error: "unsed" should be "unused" followed by "Semidecidable module.

Anonymous   
Printed
Page 327
last line of text just before Discussion

Spelling error: "unsed" should be "unused" followed by "Semidecidable module.

Anonymous   
Printed
Page 330

Similarly to the 317 erratum; this sentence is wrong: "When you call
super from within a method (such as initialize), Ruby finds every
ancestor that defines a method with the same name, and calls it too."

It should be this: "When you call super from within a method (such as
initialize), Ruby finds the first ancestor which defines a method with
the same name, and calls that method. That method may decide to call
super itself, sending Ruby searching even further back in the ancestor
tree, and so on."

Anonymous   
Printed
Page 330

The Class.included_modules method is redundant, because the Module
class already implements that method. This greatly simplifies the
code. The major difference is that newly included modules are
unshifted onto the beginning of the included_modules data structure,
rather than being pushed onto the end.

Replace the first code sample with this:

class Class
alias_method :old_new, :new
def new(*args, &block)
obj = old_new(*args, &block)
self.included_modules.each do |mod|
mod.initialize if mod.respond_to?(:initialize)
end
obj
end
end

Remove the second code sample.

Remove the references to Initializable in the third code sample:

module A
def self.initialize
puts "A's initialized."
end
end

module B
def self.initialize
puts "B's initialized."
end
end

Replace the final code sample with this:

class BothAAndB
include A
include B
end

both = BothAAndB.new
# B's initialized.
# A's initialized.

(The only change there is that B is now initialized before A. If this
disturbs you, you can substitute reverse_each for each in the first
code sample.)

The text of the Solution needs to be substantially rewritten to
accommodate this simplification; the Discussion a little less
so. Here's a rewrite of those two sections:

==Solution==

A class knows which modules it's included: you can get a list by
calling its Module#included_modules method.

```
Array.included_modules # => [Enumerable, Kernel]
```

To take advantage of this information when an object is initialized,
we need to redefine @Class#new@. Fortunately, Ruby's flexibility lets
us makes changes to the built-in @Class@ class (though this should
never be done lightly). Our new implemenation will call a module-level
@initialize@ method for each included module:

```
class Class
alias_method :old_new, :new
def new(*args, &block)
obj = old_new(*args, &block)
self.included_modules.each do |mod|
mod.initialize if mod.respond_to?(:initialize)
end
obj
end
end
```

We've redefined the @Class#new@ method so that it iterates through all
the modules in @included_modules@, and calls the module-level
@initialize@ method of each.

==Discussion==

Let's define a couple of modules which define @initialize@ module
methods:

```
module A
def self.initialize
puts "A's initialized."
end
end

module B
def self.initialize
puts "B's initialized."
end
end
```

We can now define a class that mixes in both modules:

```
class BothAAndB
include A
include B
end

BothAAndB.included_modules # => [B, A, Kernel]
```

Instantiating the class instantiates the modules, with not a single
@super()@ call in sight!

```
both = BothAAndB.new
# B's initialized.
# A's initialized.
```

The goal of this recipe is very similar to [[73160]]. In that recipe,
you call @super()@ in a class's @initialize@ method to call a mixed-in
module's @initialize@ method. That recipe doesn't require any changes
to built-in classes, so it's often preferable to this one.

But consider a case like the @BothAAndB@ class above. Using the
techniques from [[73160]], you'd need to make sure that both @A@ and
@B@ had calls to @super@ in their @initialize@ methods, so that each
module would get initialized. This solution moves all of that work
into the built-in @Class@ class. The other drawback of the previous
technique is that the user of your module needs to know to call
@super()@ somewhere in their @initialize@ method. Here, everything
happens automatically.

This technique is not without its pitfalls. Anytime you redefine
critical built-in methods like @Class#new@, you need to be careful:
someone else may have already redefined it elsewhere in your
program.

Anonymous   
Printed
Page 330

Similarly to the 317 erratum; this sentence is wrong: "When you call
super from within a method (such as initialize), Ruby finds every
ancestor that defines a method with the same name, and calls it too."

It should be this: "When you call super from within a method (such as
initialize), Ruby finds the first ancestor which defines a method with
the same name, and calls that method. That method may decide to call
super itself, sending Ruby searching even further back in the ancestor
tree, and so on."

Anonymous   
Printed
Page 330

The Class.included_modules method is redundant, because the Module
class already implements that method. This greatly simplifies the
code. The major difference is that newly included modules are
unshifted onto the beginning of the included_modules data structure,
rather than being pushed onto the end.

Replace the first code sample with this:

class Class
alias_method :old_new, :new
def new(*args, &block)
obj = old_new(*args, &block)
self.included_modules.each do |mod|
mod.initialize if mod.respond_to?(:initialize)
end
obj
end
end

Remove the second code sample.

Remove the references to Initializable in the third code sample:

module A
def self.initialize
puts "A's initialized."
end
end

module B
def self.initialize
puts "B's initialized."
end
end

Replace the final code sample with this:

class BothAAndB
include A
include B
end

both = BothAAndB.new
# B's initialized.
# A's initialized.

(The only change there is that B is now initialized before A. If this
disturbs you, you can substitute reverse_each for each in the first
code sample.)

The text of the Solution needs to be substantially rewritten to
accommodate this simplification; the Discussion a little less
so. Here's a rewrite of those two sections:

==Solution==

A class knows which modules it's included: you can get a list by
calling its Module#included_modules method.

```
Array.included_modules # => [Enumerable, Kernel]
```

To take advantage of this information when an object is initialized,
we need to redefine @Class#new@. Fortunately, Ruby's flexibility lets
us makes changes to the built-in @Class@ class (though this should
never be done lightly). Our new implemenation will call a module-level
@initialize@ method for each included module:

```
class Class
alias_method :old_new, :new
def new(*args, &block)
obj = old_new(*args, &block)
self.included_modules.each do |mod|
mod.initialize if mod.respond_to?(:initialize)
end
obj
end
end
```

We've redefined the @Class#new@ method so that it iterates through all
the modules in @included_modules@, and calls the module-level
@initialize@ method of each.

==Discussion==

Let's define a couple of modules which define @initialize@ module
methods:

```
module A
def self.initialize
puts "A's initialized."
end
end

module B
def self.initialize
puts "B's initialized."
end
end
```

We can now define a class that mixes in both modules:

```
class BothAAndB
include A
include B
end

BothAAndB.included_modules # => [B, A, Kernel]
```

Instantiating the class instantiates the modules, with not a single
@super()@ call in sight!

```
both = BothAAndB.new
# B's initialized.
# A's initialized.
```

The goal of this recipe is very similar to [[73160]]. In that recipe,
you call @super()@ in a class's @initialize@ method to call a mixed-in
module's @initialize@ method. That recipe doesn't require any changes
to built-in classes, so it's often preferable to this one.

But consider a case like the @BothAAndB@ class above. Using the
techniques from [[73160]], you'd need to make sure that both @A@ and
@B@ had calls to @super@ in their @initialize@ methods, so that each
module would get initialized. This solution moves all of that work
into the built-in @Class@ class. The other drawback of the previous
technique is that the user of your module needs to know to call
@super()@ somewhere in their @initialize@ method. Here, everything
happens automatically.

This technique is not without its pitfalls. Anytime you redefine
critical built-in methods like @Class#new@, you need to be careful:
someone else may have already redefined it elsewhere in your
program.

Anonymous   
Printed
Page 339
Very top of the page

The code example that begins on page 338 and continues at the top of page 339. There are one too few end statements at the
top of page 339 to close all of the blocks in the example.

The code on those two pages should look like this:

class Object
def my_methods_only_no_mixins
self.class.ancestors.inject(methods) do |mlist, ancestor|
mlist = mlist - ancestor.instance_methods unless ancestor.is_a? Class
mlist
end
end
end

Anonymous   
Printed
Page 339
Very top of the page

The code example that begins on page 338 and continues at the top of page 339. There are one too few end statements at the
top of page 339 to close all of the blocks in the example.

The code on those two pages should look like this:

class Object
def my_methods_only_no_mixins
self.class.ancestors.inject(methods) do |mlist, ancestor|
mlist = mlist - ancestor.instance_methods unless ancestor.is_a? Class
mlist
end
end
end

Anonymous   
Printed
Page 356
3rd line of Discussion

instance_variable_set('foo', 4) should be instance_variable_set('@foo', 4)

Anonymous   
Printed
Page 356
3rd line of Discussion

instance_variable_set('foo', 4) should be instance_variable_set('@foo', 4)

Anonymous   
Printed
Page 444
Recipe 12.14,

"Representing Data as MIDI Music" includes a line of
code to scale the data in an array of numbers so that it fits within
the range (21..108). This is the sixth line of code on page 444:

midi_note = (midi_min + ((number-midi_min) * (midi_max-low)/high)).to_i

This line is incorrect. It should be:

midi_note = (midi_min + ((number-low) * ((midi_max-midi_min)/(high-low)))).to_i

Recipe 12.5, "Adding Graphical Context with Sparklines" defines a
method called "scale" on page 421. This method is correct, but only
works when you need to scale a data set from 0 to 100. This
generalization works for any scale, making it reusable in recipe
12.14:

def scale(data, bottom=0, top=100)
min, max = data.min.to_f, data.max.to_f
scale_ratio = (top-bottom)/(max-min)
data.collect { |x| bottom + (x-min) * scale_ratio}
end

Omit the to_f calls if you want your data to be scaled using integer arithmetic.

Define that method, and you can write the loop on page 444 like this:

scale(self, midi_min, midi_max).each do |number|
midi_note = number.to_i

Anonymous   
Printed
Page 444
Recipe 12.14,

"Representing Data as MIDI Music" includes a line of
code to scale the data in an array of numbers so that it fits within
the range (21..108). This is the sixth line of code on page 444:

midi_note = (midi_min + ((number-midi_min) * (midi_max-low)/high)).to_i

This line is incorrect. It should be:

midi_note = (midi_min + ((number-low) * ((midi_max-midi_min)/(high-low)))).to_i

Recipe 12.5, "Adding Graphical Context with Sparklines" defines a
method called "scale" on page 421. This method is correct, but only
works when you need to scale a data set from 0 to 100. This
generalization works for any scale, making it reusable in recipe
12.14:

def scale(data, bottom=0, top=100)
min, max = data.min.to_f, data.max.to_f
scale_ratio = (top-bottom)/(max-min)
data.collect { |x| bottom + (x-min) * scale_ratio}
end

Omit the to_f calls if you want your data to be scaled using integer arithmetic.

Define that method, and you can write the loop on page 444 like this:

scale(self, midi_min, midi_max).each do |number|
midi_note = number.to_i

Anonymous   
Printed
Page 503

You can get cert validation in HTTPS even if you don't know where on
disk your certificates are located.

Replace the last paragraph on the page with this:

The OpenSSL library should know where your certificates are installed
on your computer. If you create an @OpenSSL::X509::Store@ object that
uses the default paths, you should be able to attach it to your
request object and then set the request's @verify_mode@ to
@OpenSSL::VERIFY_PEER@. Now OpenSSL can verify that you're really
talking to the web server you think you are, and not to an imposter:

Replace the code sample (spilling over onto the next page) with this:

request = Net::HTTP.new(uri.host, uri.port)
request.use_ssl = true
request.cert_store = OpenSSL::X509::Store.new
request.cert_store.set_default_paths
request.verify_mode = OpenSSL::SSL::VERIFY_PEER
response = request.get("/")
# => #<Net::HTTPOK 200 OK readbody=true>

Anonymous   
Printed
Page 503

You can get cert validation in HTTPS even if you don't know where on
disk your certificates are located.

Replace the last paragraph on the page with this:

The OpenSSL library should know where your certificates are installed
on your computer. If you create an @OpenSSL::X509::Store@ object that
uses the default paths, you should be able to attach it to your
request object and then set the request's @verify_mode@ to
@OpenSSL::VERIFY_PEER@. Now OpenSSL can verify that you're really
talking to the web server you think you are, and not to an imposter:

Replace the code sample (spilling over onto the next page) with this:

request = Net::HTTP.new(uri.host, uri.port)
request.use_ssl = true
request.cert_store = OpenSSL::X509::Store.new
request.cert_store.set_default_paths
request.verify_mode = OpenSSL::SSL::VERIFY_PEER
response = request.get("/")
# => #<Net::HTTPOK 200 OK readbody=true>

Anonymous   
Printed
Page 504
line 5

just get rid of the parentheses: change
"www.donotcall.gov (http://www.donotcall.gov/)"
to
"https://www.donotcall.gov/"

Anonymous   
Printed
Page 504 & 553

Query strings in fetched URLs are stripped (14.3, 14.20)

The code in Recipes 14.3 and 14.20 can't be used to request a URL that
contains a query string: the query string gets stripped. For instance,
if you tell it to retrieve "http://www.google.com/search?q=ruby", it
will actually retrieve "http://www.google.com/search".

The easiest way to solve this problem would be to pass the method that
makes the HTTP request the result of @URI#path_query@, instead of the
result of @URI#path@. Unfortunately, the @URI#path_query@ method is
private.

You have a couple options. You can modify the @URI@ class to make
@URI#path_query@ public. You can use the @send@ (Ruby 1.8) or
@funcall@ (Ruby 1.9) tricks to call the private method. You can also
duplicate the functionality of @URI@ (either within @URI@ or outside
it). This is the solution I've chosen.

If you want to create a new method that works like @URI#path_query@,
here's a simple standalone implementation:

```
def path_query(uri)
uri.path + (uri.query ? ('?' + uri.query) : '')
end
```

Otherwise, you can make the following in-place changes:

For 14.3, the first code chunk on page 505 contains this line:

```
return http.get(uri.path, headers)
```

Change it to look like this:

```
path_query = uri.path + (uri.query ? ('?' + uri.query) : '')
return http.get(path_query, headers)
```

Though it's not presented as reusable code, the first code chunk on
page 506 has the same problem. Change this:

```
request = Net::HTTP::Get.new(uri.path)
```

to this:

```
path_query = uri.path + (uri.query ? ('?' + uri.query) : '')
request = Net::HTTP::Get.new(path_query)
```

For 14.20, there's a code chunk on page 553 that looks like this:

```
response = request.send(action, uri.path, data)
```

Change it to this:

```
path_query = uri.path + (uri.query ? ('?' + uri.query) : '')
response = request.send(action, path_query, data)
```

A shorter solution is to send the whole URI: that is, the result of
@URI#to_s@. This worked well in my tests, but the HTTP 1.1 spec
(RFC2616) strongly implies that this format is only acceptable when
you're talking to an HTTP proxy.

Anonymous   
Printed
Page 504
line 5

just get rid of the parentheses: change
"www.donotcall.gov (http://www.donotcall.gov/)"
to
"https://www.donotcall.gov/"

Anonymous   
Printed
Page 504 & 553

Query strings in fetched URLs are stripped (14.3, 14.20)

The code in Recipes 14.3 and 14.20 can't be used to request a URL that
contains a query string: the query string gets stripped. For instance,
if you tell it to retrieve "http://www.google.com/search?q=ruby", it
will actually retrieve "http://www.google.com/search".

The easiest way to solve this problem would be to pass the method that
makes the HTTP request the result of @URI#path_query@, instead of the
result of @URI#path@. Unfortunately, the @URI#path_query@ method is
private.

You have a couple options. You can modify the @URI@ class to make
@URI#path_query@ public. You can use the @send@ (Ruby 1.8) or
@funcall@ (Ruby 1.9) tricks to call the private method. You can also
duplicate the functionality of @URI@ (either within @URI@ or outside
it). This is the solution I've chosen.

If you want to create a new method that works like @URI#path_query@,
here's a simple standalone implementation:

```
def path_query(uri)
uri.path + (uri.query ? ('?' + uri.query) : '')
end
```

Otherwise, you can make the following in-place changes:

For 14.3, the first code chunk on page 505 contains this line:

```
return http.get(uri.path, headers)
```

Change it to look like this:

```
path_query = uri.path + (uri.query ? ('?' + uri.query) : '')
return http.get(path_query, headers)
```

Though it's not presented as reusable code, the first code chunk on
page 506 has the same problem. Change this:

```
request = Net::HTTP::Get.new(uri.path)
```

to this:

```
path_query = uri.path + (uri.query ? ('?' + uri.query) : '')
request = Net::HTTP::Get.new(path_query)
```

For 14.20, there's a code chunk on page 553 that looks like this:

```
response = request.send(action, uri.path, data)
```

Change it to this:

```
path_query = uri.path + (uri.query ? ('?' + uri.query) : '')
response = request.send(action, path_query, data)
```

A shorter solution is to send the whole URI: that is, the result of
@URI#to_s@. This worked well in my tests, but the HTTP 1.1 spec
(RFC2616) strongly implies that this format is only acceptable when
you're talking to an HTTP proxy.

Anonymous   
Printed
Page 543
Recipe 14.18 was contributed by Chetan Patil, not Mauro Cicio.

Anonymous   
Printed
Page 543
Recipe 14.18 was contributed by Chetan Patil, not Mauro Cicio.

Anonymous   
Printed
Page 568

The paragraph at the bottom ("The @budget variable...") starts off
well-intentioned but rapidly becomes wrong. When the render method is
called the page is rendered with the current value of
@budget. Execution of the method continues, and the value of @budget
may change afterwards, but the page has already been rendered with the
old value. There is no "envelope".

Replace that paragraph (which spills over onto the next page) with the following:

The @budget variable gets set because execution of the current action
does not stop when you call render. Once render returns (having
rendered the page using the old value of @budget), the rest of the
index method runs and the value of @budget is changed.

Anonymous   
Printed
Page 568

The paragraph at the bottom ("The @budget variable...") starts off
well-intentioned but rapidly becomes wrong. When the render method is
called the page is rendered with the current value of
@budget. Execution of the method continues, and the value of @budget
may change afterwards, but the page has already been rendered with the
old value. There is no "envelope".

Replace that paragraph (which spills over onto the next page) with the following:

The @budget variable gets set because execution of the current action
does not stop when you call render. Once render returns (having
rendered the page using the old value of @budget), the rest of the
index method runs and the value of @budget is changed.

Anonymous   
Printed
Page 585
1st paragraph

"... have access to a method called sessions that returns ..."
should be
"... have access to a method called session that returns ..."

Anonymous   
Printed
Page 585
1st paragraph

"... have access to a method called sessions that returns ..."
should be
"... have access to a method called session that returns ..."

Anonymous   
Printed
Page 630
2nd paragraph of Solution

Refers to Recipe 16.7 as where the getQuote method was manually defined.
Should instead refer to Recipe 16.5.

Anonymous   
Printed
Page 630
2nd paragraph of Solution

Refers to Recipe 16.7 as where the getQuote method was manually defined.
Should instead refer to Recipe 16.5.

Anonymous   
Printed
Page 671

This code won't work and gives an inaccurate picture of what the
Logger class does:

# Keep data for today and the past 20 days
Logger.new('application.log', 20, 'daily')

If the second argument is a number, that number is the number of
logfiles to keep. In that case, the third argument is supposed to be
the maximum size of the logfile.

Replace the bad example with this one:

# Keep up to five logs, each of up to 100 megabytes in size.
Logger.new("application.log", 5, 100 * 1024 * 1024)

Anonymous   
Printed
Page 671
No need to override Logger::Formatter to change the log format (17.5)

ISBN: 0-596-00797-3On page 671 I show how to customize the logger message by overriding
the @Logger::Formatter#call@ method. A less disruptive way to
customize the message is to subclass @Logger#Formatter@ and override
@call@ in the subclass. On page 671, replace the last code fragment
with this:

```
class MyLogger < Logger::Formatter
def initialize()
self.datetime_format=("%Y-%m-%d %H:%M:%S")
end
def call(severity, time, progname, msg)
Format % [severity, format_datetime(time), progname, msg]
end
end

$LOG.formatter = MyLogger.new
$LOG.error('This is much shorter.')
# ERROR [2006-03-31 19:35:01] This is much shorter.
```

To get a @Logger@ object to use a custom formatter, you need to set
its @formatter@ member, as seen above.

Anonymous   
Printed
Page 671

This code won't work and gives an inaccurate picture of what the
Logger class does:

# Keep data for today and the past 20 days
Logger.new('application.log', 20, 'daily')

If the second argument is a number, that number is the number of
logfiles to keep. In that case, the third argument is supposed to be
the maximum size of the logfile.

Replace the bad example with this one:

# Keep up to five logs, each of up to 100 megabytes in size.
Logger.new("application.log", 5, 100 * 1024 * 1024)

Anonymous   
Printed
Page 671
No need to override Logger::Formatter to change the log format (17.5)

ISBN: 0-596-00797-3On page 671 I show how to customize the logger message by overriding
the @Logger::Formatter#call@ method. A less disruptive way to
customize the message is to subclass @Logger#Formatter@ and override
@call@ in the subclass. On page 671, replace the last code fragment
with this:

```
class MyLogger < Logger::Formatter
def initialize()
self.datetime_format=("%Y-%m-%d %H:%M:%S")
end
def call(severity, time, progname, msg)
Format % [severity, format_datetime(time), progname, msg]
end
end

$LOG.formatter = MyLogger.new
$LOG.error('This is much shorter.')
# ERROR [2006-03-31 19:35:01] This is much shorter.
```

To get a @Logger@ object to use a custom formatter, you need to set
its @formatter@ member, as seen above.

Anonymous   
Printed
Page 772

There is in fact a simple way to avoid deadlocking a thread with
itself: use the standard library's Monitor class instead of using Mutex.

Change "The second problem is harder to solve: a thread..." to "The second problem is that a thread..."

Replace "Short of hacking Mutex..." with "You can avoid this problem by using the Monitor class instead of Mutex."

Add the following code sample to the end of the Discussion:

require 'monitor'
$lock = Monitor.new
Thread.new do
$lock.synchronize { $lock.synchronize { puts 'I synchronized twice!' } }
end

Anonymous   
Printed
Page 772

There is in fact a simple way to avoid deadlocking a thread with
itself: use the standard library's Monitor class instead of using Mutex.

Change "The second problem is harder to solve: a thread..." to "The second problem is that a thread..."

Replace "Short of hacking Mutex..." with "You can avoid this problem by using the Monitor class instead of Mutex."

Add the following code sample to the end of the Discussion:

require 'monitor'
$lock = Monitor.new
Thread.new do
$lock.synchronize { $lock.synchronize { puts 'I synchronized twice!' } }
end

Anonymous   
Printed
Page 775
last line of code in Solution

age is inconsistent. :age=>"26" should be :age=>"27"

Anonymous   
Printed
Page 775
last line of code in Solution

age is inconsistent. :age=>"26" should be :age=>"27"

Anonymous   
Printed
Page 822

The file_mark and file_free functions should be defined above the file_allocate function.
Move the function bodies up so that the first code sample looks like this:

...
static void
file_mark(struct file *f)

Anonymous   
Printed
Page 822

The file_mark and file_free functions should be defined above the file_allocate function.
Move the function bodies up so that the first code sample looks like this:

...
static void
file_mark(struct file *f)

Anonymous   
Printed
Page 829

Cut the three paragraphs starting "There are some limitations you
should be aware of, though." The first limitation doesn't apply in
current versions (though the RubyInline README still talks about it),
and the second was described in a confusing way.

Replace the two paragraphs starting "Second, if you're..." with the
following:

When it comes time to distribute your program, RubyInline lets you
package a precompiled extension as a RubyGem (see the RubyInline docs
on the @inline_package@ script for details). If you don't distribute
a precompiled extension, your users will need to compile it
themselves. This means they'll need to have the Ruby development
libraries installed, along with a compiler to actually build the
extension.

Anonymous   
Printed
Page 829

Cut the three paragraphs starting "There are some limitations you
should be aware of, though." The first limitation doesn't apply in
current versions (though the RubyInline README still talks about it),
and the second was described in a confusing way.

Replace the two paragraphs starting "Second, if you're..." with the
following:

When it comes time to distribute your program, RubyInline lets you
package a precompiled extension as a RubyGem (see the RubyInline docs
on the @inline_package@ script for details). If you don't distribute
a precompiled extension, your users will need to compile it
themselves. This means they'll need to have the Ruby development
libraries installed, along with a compiler to actually build the
extension.

Anonymous