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.
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 |
|
|