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