Errata

Ruby Cookbook

Errata for Ruby Cookbook

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released.

The following errata were submitted by our customers and have not yet been approved or disproved by the author or editor. They solely represent the opinion of the customer.

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

Version Location Description Submitted by Date submitted
ePub Chapter 7. 1st and 4th paragraph.

There are a couple links in the 1st and 4th paragraph to rubycentral.com in this chapter that aren’t resolving: https://www.safaribooksonline.com/library/view/ruby-cookbook/0596523696/pr03s07.html

Anonymous  May 11, 2017 
Printed Page 1.18
mgsub

mgsub doesn't seem like grouping with ():

ex.

"red white blue".gsub(/(white)/,'x\1x')
=> red xwhitex blue

"red white blue".mgsub([[/(white)/,'x\1x']])
=> red x\\1x blue

Anonymous  Jun 05, 2008 
Printed Page 7,8,9
.each_simultaneously do |e|

sleep(5)
print "Completed operation for #{e}!
"
end.each { |t| t.join } # THIS IS THE CHANGED LINE
...[rest of code excerpt]...

Anonymous   
Printed Page 7,8,9
.each_simultaneously do |e|

sleep(5)
print "Completed operation for #{e}!
"
end.each { |t| t.join } # THIS IS THE CHANGED LINE
...[rest of code excerpt]...

Anonymous   
Printed Page 10
line immediately above "Discussion"

There should be a space between "wrong" and the period that follows it

s.split(//).reverse!.join(' ') # => "These words are in the wrong . order"

Anonymous   
Printed Page 10
line immediately above "Discussion"

There should be a space between "wrong" and the period that follows it

s.split(//).reverse!.join(' ') # => "These words are in the wrong . order"

Anonymous   
Printed Page 18
2nd code example under Discussion heading

For the example describing a pattern match for anything that isn't whitespace. It should be a lowercase 's'. So the example should read:

/[^s]+/

The capital 'S' which the book uses results in only whitespace since you're negating the 'S' match. A simpler alternative would be:

/S+/

Anonymous   
Printed Page 18
2nd code example under Discussion heading

For the example describing a pattern match for anything that isn't whitespace. It should be a lowercase 's'. So the example should read:

/[^s]+/

The capital 'S' which the book uses results in only whitespace since you're negating the 'S' match. A simpler alternative would be:

/S+/

Anonymous   
Printed Page 25
first paragraph under heading See Also

Paragraph refers to chapter 11.2 for details on the use of the iconv library.
Nevertheless iconv is not mentioned in chapter 11.2.

Anonymous   
Printed Page 25
first paragraph under heading See Also

Paragraph refers to chapter 11.2 for details on the use of the iconv library.
Nevertheless iconv is not mentioned in chapter 11.2.

Anonymous   
Printed Page 32-33
Recipe 1.18 mgsub

mgsub doesn't seem like grouping with ():

ex.

"red white blue".gsub(/(white)/,'x1x')
=> red xwhitex blue

"red white blue".mgsub([[/(white)/,'x1x']])
=> red x\1x blue

Anonymous   
PDF Page 42
4th line

Change
:to_f => /([+-]?([0-9]*\.)?[0-9]+(e[+-]?[0-9]+)?)/i,

to

:to_f => /([+-]?(?:[0-9]*\.)?[0-9]+(?:e[+-]?[0-9]+)?)/i,


The addition of the ?: in the second and third group prevents them from being capture groups so repeating the body of the regular expression multiple times such as

/([+-]?(?:[0-9]*\.)?[0-9]+(?:e[+-]?[0-9]+)?) ([+-]?(?:[0-9]*\.)?[0-9]+(?:e[+-]?[0-9]+)?)/i

to parse two floating point numbers will return the float strings in $1 and $2 as expected, otherwise without the ?: will return the floats in $1 and $3 - much less intuitive.

Anonymous  Nov 05, 2008 
ePub Page 52
Top of page

Code should read:

def random_word
letters = { ?v => 'aeiou',
?c => 'bcdfghjklmnprstvwyz' }
word = ''
'cvcvcvc'.each_char do |x|
source = letters[x]
word << source[rand(source.length)].chr
end
return word
end

puts random_word # => "josuyip"
puts random_word # => "haramic"

Anonymous  Nov 27, 2013 
Printed Page 53
end of Solution

In the book, the formula to convert a logarithm from base b1 to base b2 looks like this:

log_b1(x) = log_b2(x) / log_b2(k)

however, the 'k' should be 'b1' as in

log_b1(x) = log_b2(x) / log_b2(b1)

See also http://en.wikipedia.org/wiki/Logarithm#Change_of_base

Anonymous   
Printed Page 53
Discussion, line 2-4

The sentence reads:

"That is, Math.log10(1000)==3.0 because 10 cubed is 1000.Math.log(Math::E)==1 because e to the first power is e."

A space between '1000.' and 'Math.log(Math::E)' is missing - the above are actually two sentences.

Anonymous   
Printed Page 53
end of Solution

In the book, the formula to convert a logarithm from base b1 to base b2 looks like this:

log_b1(x) = log_b2(x) / log_b2(k)

however, the 'k' should be 'b1' as in

log_b1(x) = log_b2(x) / log_b2(b1)

See also http://en.wikipedia.org/wiki/Logarithm#Change_of_base

Anonymous   
Printed Page 53
Discussion, line 2-4

The sentence reads:

"That is, Math.log10(1000)==3.0 because 10 cubed is 1000.Math.log(Math::E)==1 because e to the first power is e."

A space between '1000.' and 'Math.log(Math::E)' is missing - the above are actually two sentences.

Anonymous   
Printed Page 53
Sentence "To calculated a logarithm...", under "Solution"

Argument of divisor should be "b1" ("b sub 1"), not "k". Should read:
log x = log x / log b1
b b b
1 2 2

Cary Swoveland  Jul 22, 2010 
Printed Page 55
2.8 first code example

(Note: Since I'm reading the book via the Safari bookshelf, I have no idea which page it is; but it is anyway chapater 2.8, titled

"Recipe 2.8. Finding Mean, Median, and Mode"

The code example

def mean(array)

array.inject(array.inject(0) { |sum, x| sum += x } / array.size.to_f

end

has two problems, one a typo (which you likely have already noticed), and a more subtle pedagogic one:

The correct way to write the function would be IMO

def mean(array)
array.inject(0) { |sum,x| sum+x } / array.size.to_f
end

Apart from the obvious correction of the typo at the beginning of
the statement, note that I changed sum+=x to sum+x. While both
expressions would work here, sum+=x silently implies that the
incject function *should* modify the running variable (here: sum).
This is not true. Changing sum within the block has no effect
outside. Array#incject only takes the return value of the block.
In this case, sum+x and sum+=x yield the same return value, but
sum+x communicates this fact in a clearer way.

Anonymous   
Printed Page 55
2.8 first code example

(Note: Since I'm reading the book via the Safari bookshelf, I have no idea which page it is; but it is anyway chapater 2.8, titled

"Recipe 2.8. Finding Mean, Median, and Mode"

The code example

def mean(array)

array.inject(array.inject(0) { |sum, x| sum += x } / array.size.to_f

end

has two problems, one a typo (which you likely have already noticed), and a more subtle pedagogic one:

The correct way to write the function would be IMO

def mean(array)
array.inject(0) { |sum,x| sum+x } / array.size.to_f
end

Apart from the obvious correction of the typo at the beginning of
the statement, note that I changed sum+=x to sum+x. While both
expressions would work here, sum+=x silently implies that the
incject function *should* modify the running variable (here: sum).
This is not true. Changing sum within the block has no effect
outside. Array#incject only takes the return value of the block.
In this case, sum+x and sum+=x yield the same return value, but
sum+x communicates this fact in a clearer way.

Anonymous   
Printed Page 91
class Date def Date.now...

class Date
def Date.now
return Date.jd(Datetime.now.jd)
end
end
puts Date.now

does not work. It generates the following error message:
`now': stack level too deep (SystemStackError)

Remedy: change to

class Date
def Date.new
Date.jd(DateTime.now.jd)
end
end

puts Date.new

Anonymous   
Printed Page 91
class Date def Date.now...

class Date
def Date.now
return Date.jd(Datetime.now.jd)
end
end
puts Date.now

does not work. It generates the following error message:
`now': stack level too deep (SystemStackError)

Remedy: change to

class Date
def Date.new
Date.jd(DateTime.now.jd)
end
end

puts Date.new

Anonymous   
Printed Page 111
Bottom code fragment

to_local_time is incorrect, and will return a date several years off.

Original:

def to_local_time
to_time(new_offset(DateTime.now.offset-offset), :local)
end

Counter-example:

Consider the call DateTime.now.to_local_time, for any time zone, such as PST.

This results in
to_time(new_offset(0), :local)
which invokes Time.local with offset(0) H:M:S.

This should be:

def to_local_time
to_time(new_offset(DateTime.now.offset), :local)
end

Anonymous   
Printed Page 111
Bottom code fragment

to_local_time is incorrect, and will return a date several years off.

Original:

def to_local_time
to_time(new_offset(DateTime.now.offset-offset), :local)
end

Counter-example:

Consider the call DateTime.now.to_local_time, for any time zone, such as PST.

This results in
to_time(new_offset(0), :local)
which invokes Time.local with offset(0) H:M:S.

This should be:

def to_local_time
to_time(new_offset(DateTime.now.offset), :local)
end

Anonymous   
Printed Page 112
code at bottom of page in normalize_time_types(array)

On the next to last line on the page, in the line of code that's part of the rescue statement
convert_to = DateTime
is superfluous. The variable convert_to is never referenced.

Anonymous   
Printed Page 112
code at bottom of page in normalize_time_types(array)

On the next to last line on the page, in the line of code that's part of the rescue statement
convert_to = DateTime
is superfluous. The variable convert_to is never referenced.

Anonymous   
Printed Page 138
code related to the else clauses in the revised class []= for SortedArray

The following code in the two "else" clauses that contain the comment "# Not supported. Delegate to superclass; will probably give an error." is incorrect:

else
# Not supported...
super
sort!(&sort_by)
end
else
# Not supported...
super
sort!(&sort_by)
end

should be:

else
# Not supported...
super
sort!(&@sort_by)
end
else
# Not supported...
super
sort!(&@sort_by)
end

This is no big deal since the code should never execute.

Anonymous   
Printed Page 138
code related to the else clauses in the revised class []= for SortedArray

The following code in the two "else" clauses that contain the comment "# Not supported. Delegate to superclass; will probably give an error." is incorrect:

else
# Not supported...
super
sort!(&sort_by)
end
else
# Not supported...
super
sort!(&sort_by)
end

should be:

else
# Not supported...
super
sort!(&@sort_by)
end
else
# Not supported...
super
sort!(&@sort_by)
end

This is no big deal since the code should never execute.

Anonymous   
Printed Page 139
line 6 from bottom of page 139

stripes = SortedArray.new(["aardwolf", "zebrafish"])
should be
stripes = FrozenCopySortedArray.new(["aardwolf", "zebrafish"])

then if you ran
stripes[1].upcase!

you will get the following error message:
`upcase!': can't modify frozen string (TypeError)

Anonymous   
Printed Page 139
line 6 from bottom of page 139

stripes = SortedArray.new(["aardwolf", "zebrafish"])
should be
stripes = FrozenCopySortedArray.new(["aardwolf", "zebrafish"])

then if you ran
stripes[1].upcase!

you will get the following error message:
`upcase!': can't modify frozen string (TypeError)

Anonymous   
Printed Page 141
1st paragraph under "Discussion"

The author refers to a variable, total, in the example above. However,
the example uses the variable sum. Also, the first sentence of the
paragraph reads:

"...we didn't need to define the variable total variable outside the scope of
iteration."

One of the instances of the word variable needs to be removed, and the
sentence should be adjusted, depending on which instance is removed.

Anonymous   
Printed Page 141
1st paragraph under "Discussion"

The author refers to a variable, total, in the example above. However,
the example uses the variable sum. Also, the first sentence of the
paragraph reads:

"...we didn't need to define the variable total variable outside the scope of
iteration."

One of the instances of the word variable needs to be removed, and the
sentence should be adjusted, depending on which instance is removed.

Anonymous   
Printed Page 144
Discussion section

The Array#shuffle! method implements the Fisher-Yates shuffle algorithm (Knuth _The Art of Computer Programming_ vol. 2 pg. 145). The implementation works, yet it has a small superfluous step. Namely, we do not need to iterate over "each" index. Once j is equal to the final value (n-1) there is only 1 array element left, and there is no point swapping it with itself.

Here is a version that closely follows Knuth's description (although applied to 0-based indexing):
class Array
def shuffle!
(length-1).downto(1) do |i|
j = rand(i + 1)
self[j], self[i] = self[i], self[j]
end
self
end
end

And here is a slight variant that avoids unnecessary swaps:
class Array
def shuffle!
(length-1).downto(1) do |i|
j = rand(i + 1)
self[j], self[i] = self[i], self[j] unless i == j
end
self
end
end

David Sletten  Jul 04, 2010 
Printed Page 146
line 5 and 6 from bottom of page 146

The SortedList class from Recipe 4.7 is used for this task. The
min_n method below create a SortedList "stable" that .....

The "SortedList" of lines 5 and 6 should be "SortedArray".
If you look at Recipe 4.7 and source code of page 147, there is a
"SortedArray" class instead of "SortedList" class.

Anonymous   
Printed Page 146
line 5 and 6 from bottom of page 146

The SortedList class from Recipe 4.7 is used for this task. The
min_n method below create a SortedList "stable" that .....

The "SortedList" of lines 5 and 6 should be "SortedArray".
If you look at Recipe 4.7 and source code of page 147, there is a
"SortedArray" class instead of "SortedList" class.

Anonymous   
Printed Page 148
Comment line describing result after running hash.update (8th line from bottom of page)

# => {5=>"five", 1=>"ontwo", 2=>"two", 3=>"three", 4=>"four"}

should be

# => {5=>"five", 1=>"one", 2=>"two", 3=>"three", 4=>"four"}

NOTE: "ontwo" after 1=> is a typo

Anonymous   
Printed Page 148
Comment line describing result after running hash.update (8th line from bottom of page)

# => {5=>"five", 1=>"ontwo", 2=>"two", 3=>"three", 4=>"four"}

should be

# => {5=>"five", 1=>"one", 2=>"two", 3=>"three", 4=>"four"}

NOTE: "ontwo" after 1=> is a typo

Anonymous   
Printed Page 151
middle of the pages

"then use use Array#compress! to remove them."

has too many uses.

Anonymous   
Printed Page 151
last paragraph (before code)

There's no method called Array#compress!. The correct method for removing nil values is Array#compact!

Anonymous   
Printed Page 151
code at bottom of page defining strip_values_at!

There's a misplaced "end"

The code in the book is:

class Array
def strip_values_at!(*args)

# For each mentioned index, replace its value with a dummy object

values = []
dummy = Object.new
args.each do | i |
if i < size
values << self[i]
self[i] = dummy
end

#Strip out the dummy objects
delete(dummy)
return values
end
end
end

Correct code should be:

class Array
def strip_values_at!(*args)

# For each mentioned index, replace its value with a dummy object

values = []
dummy = Object.new
args.each do | i |
if i < size
values << self[i]
self[i] = dummy
end
end

# Strip out the dummy objects

delete(dummy)
return values
end
end

Anonymous   
Printed Page 151
middle of the pages

"then use use Array#compress! to remove them."

has too many uses.

Anonymous   
Printed Page 151
last paragraph (before code)

There's no method called Array#compress!. The correct method for removing nil values is Array#compact!

Anonymous   
Printed Page 151
code at bottom of page defining strip_values_at!

There's a misplaced "end"

The code in the book is:

class Array
def strip_values_at!(*args)

# For each mentioned index, replace its value with a dummy object

values = []
dummy = Object.new
args.each do | i |
if i < size
values << self[i]
self[i] = dummy
end

#Strip out the dummy objects
delete(dummy)
return values
end
end
end

Correct code should be:

class Array
def strip_values_at!(*args)

# For each mentioned index, replace its value with a dummy object

values = []
dummy = Object.new
args.each do | i |
if i < size
values << self[i]
self[i] = dummy
end
end

# Strip out the dummy objects

delete(dummy)
return values
end
end

Anonymous   
Printed Page 153
3rd set of code (from either the top or the bottom of the page)

The following code is incorrect:
array = [1, 2, 3]
set = [3, 4, 5].to_s
array & set
set & array

It should be the following:
array = [1, 2, 3]
set = [3, 4, 5].to_set
array & set
set & array

NOTE: to_s should be to_set

Anonymous   
Printed Page 153
3rd set of code (from either the top or the bottom of the page)

The following code is incorrect:
array = [1, 2, 3]
set = [3, 4, 5].to_s
array & set
set & array

It should be the following:
array = [1, 2, 3]
set = [3, 4, 5].to_set
array & set
set & array

NOTE: to_s should be to_set

Anonymous   
Printed Page 155
4th to last line of code on the page (a comment)

The comment is missing a closing quotation mark for "String"

The text should be:
# Divide the set into the "String" subset, the "Fixnum" subset, and
# the "Float" subset

Anonymous   
Printed Page 155
4th to last line of code on the page (a comment)

The comment is missing a closing quotation mark for "String"

The text should be:
# Divide the set into the "String" subset, the "Fixnum" subset, and
# the "Float" subset

Anonymous   
Printed Page 156
2nd section of code above Discussion

The result for the operation to divide the set into sets of adjacent numbers is wrong. There was no element "-3" in Set s. There was a "-2".

The incorrect results in the book are:

#<Set: {#<Set: {1, 2, 3}>,
#<Set: {-1}>,
#<Set: {-4, -3}>}>

The correct results should be:

#<Set: {#<Set: {1, 2, 3}>,
#<Set: {-4}>,
#<Set: {-2, -1}>}>

Anonymous   
Printed Page 156
section of code immediate preceding Discussion

The following assignment statement should precede the code that uses Set#classify;
s = Set.new([1, 2, 3, 'a', 'b', 'c', -1.0, -2.0, -3.0])

Anonymous   
Printed Page 156
2nd section of code above Discussion

The result for the operation to divide the set into sets of adjacent numbers is wrong. There was no element "-3" in Set s. There was a "-2".

The incorrect results in the book are:

#<Set: {#<Set: {1, 2, 3}>,
#<Set: {-1}>,
#<Set: {-4, -3}>}>

The correct results should be:

#<Set: {#<Set: {1, 2, 3}>,
#<Set: {-4}>,
#<Set: {-2, -1}>}>

Anonymous   
Printed Page 156
section of code immediate preceding Discussion

The following assignment statement should precede the code that uses Set#classify;
s = Set.new([1, 2, 3, 'a', 'b', 'c', -1.0, -2.0, -3.0])

Anonymous   
Printed Page 160
Last code example on page

The following has an error (missing colons in key labels):

h = { :one_squared => 1, two_squared => 4, three_squared => 9,
:four_squared => 16 }

The corrected code should be:

h = { :one_squared => 1, :two_squared => 4, :three_squared => 9,
:four_squared => 16 }

Anonymous   
Printed Page 160
Last code example on page

The following has an error (missing colons in key labels):

h = { :one_squared => 1, two_squared => 4, three_squared => 9,
:four_squared => 16 }

The corrected code should be:

h = { :one_squared => 1, :two_squared => 4, :three_squared => 9,
:four_squared => 16 }

Anonymous   
Printed Page 165
2nd section of code up from the bottom of the page

The example in the book is:

squares = [1, 1, 2, 3, 4, 9]
results = {}
squares.into_hash(results) # => {1=>1, 2=>3, 4=>9}

It would seem that this is a typo and that the correct version should be:

squares = [1, 1, 2, 4, 3, 9]
results = {}
squares.into_hash(results) # => {1=>1, 2=>4, 3=>9}

Anonymous   
Printed Page 165
Last section of code on page

The values used for the hash named cubes causes confusion.
cubes = { 3 => 27, 4 => 256, 5 => 3125 }

I recommend using one of the following instead:
cubes = { 3 => 27, 4 => 64, 5 => 125 }
cubes = { 3 => 3 ** 3, 4 => 4 ** 3, 5 => 5 ** 3 }

Anonymous   
Printed Page 165
2nd section of code up from the bottom of the page

The example in the book is:

squares = [1, 1, 2, 3, 4, 9]
results = {}
squares.into_hash(results) # => {1=>1, 2=>3, 4=>9}

It would seem that this is a typo and that the correct version should be:

squares = [1, 1, 2, 4, 3, 9]
results = {}
squares.into_hash(results) # => {1=>1, 2=>4, 3=>9}

Anonymous   
Printed Page 165
Last section of code on page

The values used for the hash named cubes causes confusion.
cubes = { 3 => 27, 4 => 256, 5 => 3125 }

I recommend using one of the following instead:
cubes = { 3 => 27, 4 => 64, 5 => 125 }
cubes = { 3 => 3 ** 3, 4 => 4 ** 3, 5 => 5 ** 3 }

Anonymous   
Printed Page 165
class Array...end

The into_hash method seems like an
'anti recipe' because one can just use

results = Hash[*squares]

To convert an array with key-value pairs into a hash

David Ongaro  Mar 01, 2009 
Printed Page 167
1st section of code on page

There is no value shown for the 8th line of code:
h.delete(5)

The actual value returned is nil:
h.delete(5) # => nil

Anonymous   
Printed Page 167
1st section of code on page

There is no value shown for the 8th line of code:
h.delete(5)

The actual value returned is nil:
h.delete(5) # => nil

Anonymous   
Printed Page 199
Next to last line of code on page

d.reject { |f| f[0] == '.' } generates the following output:

[".", "..", ".hidden_file", "ruby_script.rb", "subdirectory", "text_file"]

The problem is that f[0] returns a Fixnum, not a string.

Any of the following will work properly:

d.reject { |f| f[0] == ?. }
d.reject { |f| f[0..0] == '.' }
d.reject { |f| f[/^./] == '.' }
d.reject { |f| f =~ [/^./ }

Anonymous   
Printed Page 199
Next to last line of code on page

d.reject { |f| f[0] == '.' } generates the following output:

[".", "..", ".hidden_file", "ruby_script.rb", "subdirectory", "text_file"]

The problem is that f[0] returns a Fixnum, not a string.

Any of the following will work properly:

d.reject { |f| f[0] == ?. }
d.reject { |f| f[0..0] == '.' }
d.reject { |f| f[/^./] == '.' }
d.reject { |f| f =~ [/^./ }

Anonymous   
Printed Page 206
The last two lines of code at top of page (just above "See Also")

First, IO.sync, as it appears in the code, is invoking a class method. IO#sync is an instance method.

Second, in order for the output to go straight into the OS buffer, you'd want to set IO#sync = true (not false).

Either of the following corrects what's in the book:
file = open('output', 'w')
file.sync = true
file << 'This is going straight into the OS buffer.'
file.close

open('output', 'w') do | f |
f.sync = true
f << 'This is going straight into the OS buffer.'
end

Anonymous   
Printed Page 206
The last two lines of code at top of page (just above "See Also")

First, IO.sync, as it appears in the code, is invoking a class method. IO#sync is an instance method.

Second, in order for the output to go straight into the OS buffer, you'd want to set IO#sync = true (not false).

Either of the following corrects what's in the book:
file = open('output', 'w')
file.sync = true
file << 'This is going straight into the OS buffer.'
file.close

open('output', 'w') do | f |
f.sync = true
f << 'This is going straight into the OS buffer.'
end

Anonymous   
Printed Page 217
section 6.13

Here's the offending recipe:

#
# original recipe
#
open( 'output', 'w' ) do |f|
flock(f, File::LOCK_EX) do |f|
f << "Kiss me, I've got a write lock on a file!"
end
end

The recipe truncates the file before obtaining the lock. Some other process could have LOCK_EX and be writing into
the file. If the reader realizes this can happen, he'll wonder if the any of the recipe is correct.

I solve this problem by creating a zero-length companion file which takes such rough treatment without ill effect:

#
# suggested changed recipe
#
open( 'output.lck', 'a' ) do |f|
flock( f, File::LOCK_EX)
open( 'output', 'w' ) do |g|
g << "Kiss me, I've got the write lock for this file!"
end
end

How this works:
1. Open an append-write handle on the companion, which creates the companion if it does not already exist.
Derive companion name from the file you really want to write.
2. Request and wait for an exclusive lock on the companion
3. Open a write handle on the actual file.
4. Write to the actual file
5. Close the write handle on the actual file
6. Close the append handle on the companion file. This releases the lock.

General comments:

1. I like cookbook recipes which don't strain credulity. Using the same block variable |f| in two nested
blocks works, but distracts the reader.

2. open(...) do |h| ... end passes the handle created by open() into the block. This is idiosyncratic to Ruby and should be noted.

3. The fact that leaving the block closes the handle is idiosyncratic to Ruby and should be noted.

4. It is not widely appreciated that closing a handle on a file terminates any flock lock you may have on the file through that handle. This is a feature of the OS and not Ruby. But if we note this, reader will completely understand how this recipe works and believe it. He will appreciate a bit of how Ruby is set up for brevity.

Anonymous   
PDF Page 217
6.11 Choosing Randomly from a Weighted List, Solution paragraph

The choose_weighted() function has an off-by-one error that renders the results incorrect. For small integer weights, the values are egregiously wrong.

The line:
return item if target <= weight

Should instead read:
return item if target < weight

Using less-than-or-equal-to instead of strictly less-than creates an off-by-one error since rand() produces outputs that are zero-indexed.

For example, if you call choose_weighted() with argument {'A' => 1, 'B' => 1} you would expect it to return 'A' and 'B' each half the time. What actually occurs is that it returns 'A' uniformly and never returns 'B'.

This is because the sum of the weights is 2, so rand(2) returns either 0 or 1. Both 0 and 1 are <= the weight 1, so the function always returns 'A'. This is incorrect.

The problem is less severe but still present in the floating point version of the function, which is unlikely to encounter the boundary case because the space of floating point numbers is so large.

Andy Brody  Apr 25, 2018 
Printed Page 227
top line in the set of code at the bottom of the page

Throughout the examples in 6.17, you add "b" to the file mode which is needed when you open a binary file on Windows. In the first set line of code after Discussion, you omit this.

Change
f = open('binary')
to
f = open('binary', 'rb')

Anonymous   
Printed Page 227
top line in the set of code at the bottom of the page

Throughout the examples in 6.17, you add "b" to the file mode which is needed when you open a binary file on Windows. In the first set line of code after Discussion, you omit this.

Change
f = open('binary')
to
f = open('binary', 'rb')

Anonymous   
Printed Page 231
1st line of code in 6.18

The first line of code looks like Java code instead of Ruby code

The following line of code
import 'fileutils'
should be changed to
require 'fileutils'

Anonymous   
Printed Page 231
1st line of code in 6.18

The first line of code looks like Java code instead of Ruby code

The following line of code
import 'fileutils'
should be changed to
require 'fileutils'

Anonymous   
Printed Page 232
Last two sections of code on the page

The following code:
open(filename, File::TRUNC) do | f |
generates the following error
in `initialize': Invalid argument - truncate.txt (Errno::EINVAL)

The same open statement appears twice in the text.

Note: The value of the constatn File::TRUNC is 512.

Anonymous   
Printed Page 232
Last two sections of code on the page

The following code:
open(filename, File::TRUNC) do | f |
generates the following error
in `initialize': Invalid argument - truncate.txt (Errno::EINVAL)

The same open statement appears twice in the text.

Note: The value of the constatn File::TRUNC is 512.

Anonymous   
Printed Page 245
Code at bottom of page

If you want to pick numbers between min and max, inclusive, then you should change

limit.times { yield min+rand(max+1) }
to
limit.times { yield min+rand(max) }

The implementation defined in the book chooses numbers between 1 and 50 (rand(49) returns a value between 0 and 48)

The code still has the problem that it can pick the same number more than once.

Anonymous   
Printed Page 245
Code at bottom of page

If you want to pick numbers between min and max, inclusive, then you should change

limit.times { yield min+rand(max+1) }
to
limit.times { yield min+rand(max) }

The implementation defined in the book chooses numbers between 1 and 50 (rand(49) returns a value between 0 and 48)

The code still has the problem that it can pick the same number more than once.

Anonymous   
Printed Page 249
1st sample code block

When the each method recurses with the line "child.each { |e| yield e }" it is wrapping the original block with multiple successive blocks as it recurses deeper into the tree. This seems very inefficient. This can easily be seen by putting a print statement into the block so that the printed output clearly shows this wrapping...

child_node.each { |e| puts "yield #{e}"; yield e }

I believe a better version of the each method would use a named block thusly...

def each(&block)
block.call(@value)
@children.each do |child_node|
child_node.each(&block)
end
end

The evolution of this solution was very instructive for me and might be worth discussing in the next version of the cook book as a way of reinforcing how yield and blocks/closures work.

Anonymous   
Printed Page 249
1st sample code block

When the each method recurses with the line "child.each { |e| yield e }" it is wrapping the original block with multiple successive blocks as it recurses deeper into the tree. This seems very inefficient. This can easily be seen by putting a print statement into the block so that the printed output clearly shows this wrapping...

child_node.each { |e| puts "yield #{e}"; yield e }

I believe a better version of the each method would use a named block thusly...

def each(&block)
block.call(@value)
@children.each do |child_node|
child_node.each(&block)
end
end

The evolution of this solution was very instructive for me and might be worth discussing in the next version of the cook book as a way of reinforcing how yield and blocks/closures work.

Anonymous   
Printed Page 251
The heading reads Discussion, but probably should be Solution.

Anonymous   
Printed Page 271
3rd paragraph

The book says:
"Calling attr_accessor :instance_variable generates both the getter method
speaks_english and the setter method speaks_english="

I believe it should say:
"Calling attr_accessor :speaks_english generates both the getter method
speaks_english and the setter method speaks_english="

Anonymous   
Printed Page 271
3rd paragraph

The book says:
"Calling attr_accessor :instance_variable generates both the getter method
speaks_english and the setter method speaks_english="

I believe it should say:
"Calling attr_accessor :speaks_english generates both the getter method
speaks_english and the setter method speaks_english="

Anonymous   
Printed Page 279
Second paragraph under "Solution"

"...by passing the coordinates of its top-left and bottom-left corners..."
should be
"...by passing the coordinates of its top-left and bottom-right corners..."

Anonymous   
Printed Page 279
Second paragraph under "Solution"

"...by passing the coordinates of its top-left and bottom-left corners..."
should be
"...by passing the coordinates of its top-left and bottom-right corners..."

Anonymous   
Printed Page 281
2nd paragraph after Solution

ArgumentException should be ArgumentError (or ArgumentError exception)

Anonymous   
Printed Page 281
2nd paragraph after Solution

ArgumentException should be ArgumentError (or ArgumentError exception)

Anonymous   
Printed Page 285
2nd paragraph

The text refers to a class called CardinalNumber. The code defines a class called OrdinalNumber. The text should change CardinalNumber to OrdinalNumber (occurs 3 times).

The comment at the beginning of the code states:

# An integer represented as an ordinal number (1st, 2nd, 3rd...), as
# opposed to an ordinal number (1, 2, 3...)

It should be changed to:

# An integer represented as an ordinal number (1st, 2nd, 3rd...), as
# opposed to an cardinal number (1, 2, 3...)

Anonymous   
Printed Page 285
code

There are two problems with the code:

1. The line below check = abs should be changed from:
if to_check == 11 or to_check == 12
to
if check == 11 or check == 12 or check == 13

2. There's a line missing from the case expression.
Change from:

case check % 10
when 1 then suffix = "st"
when 2 then suffix = "nd"
else suffix = "th"
end

to:

case check % 10
when 1 then suffix = "st"
when 2 then suffix = "nd"
when 3 then suffix = "rd"
else suffix = "th"
end

You might also want to add to the bottom of the code:

OrdinalNumber.new(3).to_s # => "3rd"
OrdinalNumber.new(13).to_s # => "13th"
OrdinalNumber.new(123).to_s # => "123rd"

Anonymous   
Printed Page 285
2nd paragraph

The text refers to a class called CardinalNumber. The code defines a class called OrdinalNumber. The text should change CardinalNumber to OrdinalNumber (occurs 3 times).

The comment at the beginning of the code states:

# An integer represented as an ordinal number (1st, 2nd, 3rd...), as
# opposed to an ordinal number (1, 2, 3...)

It should be changed to:

# An integer represented as an ordinal number (1st, 2nd, 3rd...), as
# opposed to an cardinal number (1, 2, 3...)

Anonymous   
Printed Page 285
code

There are two problems with the code:

1. The line below check = abs should be changed from:
if to_check == 11 or to_check == 12
to
if check == 11 or check == 12 or check == 13

2. There's a line missing from the case expression.
Change from:

case check % 10
when 1 then suffix = "st"
when 2 then suffix = "nd"
else suffix = "th"
end

to:

case check % 10
when 1 then suffix = "st"
when 2 then suffix = "nd"
when 3 then suffix = "rd"
else suffix = "th"
end

You might also want to add to the bottom of the code:

OrdinalNumber.new(3).to_s # => "3rd"
OrdinalNumber.new(13).to_s # => "13th"
OrdinalNumber.new(123).to_s # => "123rd"

Anonymous   
Printed Page 286
code in the center of the page

change
a = AppendOnlyArray
to
a = AppendOnlyArray.new

Anonymous   
Printed Page 286
code in the center of the page

change
a = AppendOnlyArray
to
a = AppendOnlyArray.new

Anonymous   
Printed Page 287
Under See Also

Change CardinalNumber to OrdinalNumber

Anonymous   
Printed Page 287
Under See Also

Change CardinalNumber to OrdinalNumber

Anonymous   
Printed Page 303
Recipe 8.15

5th coding example reads

API_KEY = "100f7vo4gg".freeze

API_KEY[0] = 4
# TypeError: can't modify frozen string

API_KEY = "400f7vo4gg"
# warning: already initialized constant API_KEY

From the text, the reader is lead to conclude that the warning "already initialized constant" results from the fact that API_KEY has been frozen in the first line.

This is not correct (at least not in Ruby 1.8.6). The warning would be issued even if API_KEY would not have been assigned a frozen string before. It comes from the fact that the variable name starts with an uppercase letter (and hence designates what Ruby calls a "constant"). Indeed, if we would have used variables starting with a lower case letter, i.e.

api_key="....".freeze
api_key[0]=4
api_key="...."

We would not get any warning (although of course, we would still get the type error in the second line).

Anonymous   
PDF Page 322
Bottom of page

String does not include Enumerable as the book claims it does.

[29] pry(main)> "asdf".is_a? Enumerable
=> false
[30] pry(main)> String.is_a? Enumerable
=> false

Rylee Fowler  May 13, 2014 
Printed Page 326
code above "See Also"

The last line of code before the final end is:
RubyString.new("This is a built-in string, not a StringTheory2::String")

This code should be moved below the end, and changed to:
StringTheory2::RubyString.new("This is a built-in string, not a StringTheory2::String")

The full example is:

module StringTheory2
RubyString = String
class String
def initialize(length=1.0e-33)
@length = length
end
end
end

StringTheory2::RubyString.new("This is a built-in string, not a StringTheory2::String")
#=>"This is a built-in string, not a StringTheory2::String"

Anonymous   
Printed Page 326
code above "See Also"

The last line of code before the final end is:
RubyString.new("This is a built-in string, not a StringTheory2::String")

This code should be moved below the end, and changed to:
StringTheory2::RubyString.new("This is a built-in string, not a StringTheory2::String")

The full example is:

module StringTheory2
RubyString = String
class String
def initialize(length=1.0e-33)
@length = length
end
end
end

StringTheory2::RubyString.new("This is a built-in string, not a StringTheory2::String")
#=>"This is a built-in string, not a StringTheory2::String"

Anonymous   
Printed Page 339
Text under Problem

There's a word missing.

change "You want to the name of a method..."
to "You want to put the name of a method..."

Anonymous   
Printed Page 339
Text under Problem

There's a word missing.

change "You want to the name of a method..."
to "You want to put the name of a method..."

Anonymous   
Printed Page 339
Last paragraph

The last paragraph on the page describes the Method#arity method, but the example that follows does not illustrate it.

Celso Frazao  Mar 10, 2009 
Printed Page 339
Last paragraph

The last paragraph on the page describes the Method#arity method, but the example that follows does not illustrate it.

Celso Frazao  Mar 10, 2009 
Printed Page 341
code at the top of the page

Although it doesn't change anything, I think the 5th line of code at the top of the page should be changed from:

Welcomer.method("an_instance_method")

to

Welcomer.method("an_instance_method").call

Anonymous   
Printed Page 341
code at the top of the page

Although it doesn't change anything, I think the 5th line of code at the top of the page should be changed from:

Welcomer.method("an_instance_method")

to

Welcomer.method("an_instance_method").call

Anonymous   
Printed Page 351
code appearing inside the Problem section

The following code:
class RGBColor(red=0, green=0, blue=0)
@red = red
@green = green
@blue = blue
end
generates the following syntax errors:
syntax error, unexpected '
', expecting tCOLON2 or '[' or '.'
syntax error, unexpected kEND, expecting $end

I believe the correct syntax for the above code is the following:
class RGBColor
def initialize(red=0, green=0, blue=0)
@red = red
@green = green
@blue = blue
end
end

Anonymous   
Printed Page 351
code appearing inside the Problem section

The following code:
class RGBColor(red=0, green=0, blue=0)
@red = red
@green = green
@blue = blue
end
generates the following syntax errors:
syntax error, unexpected '
', expecting tCOLON2 or '[' or '.'
syntax error, unexpected kEND, expecting $end

I believe the correct syntax for the above code is the following:
class RGBColor
def initialize(red=0, green=0, blue=0)
@red = red
@green = green
@blue = blue
end
end

Anonymous   
Printed Page 368
5th line from the top of the page

There's a blank space missing between the word "must" and "satisfy".

change at the end of the 5th line of code

".... '#{method}' must" +
to
".... '#{method}' must " +

Anonymous   
Printed Page 368
5th line from the top of the page

There's a blank space missing between the word "must" and "satisfy".

change at the end of the 5th line of code

".... '#{method}' must" +
to
".... '#{method}' must " +

Anonymous   
Printed Page 373
Code at the bottom of the page

The following is wrong:

invalid_xml = %{
<groceries>
<bread>Wheat
}
(valid_xml?(invalid_xml) == nil)

According to the book, the above is supposed to return
# => false, # That is, it is "valid"

That is wrong. The valid_xml?(invalid_xml) returns nil.

As such, the following statement does not work:

REXML::Document.new(invalid_xml).write

However, the following does work

REXML::Document.new(good_xml).write

Anonymous   
Printed Page 373
Code at the bottom of the page

The following is wrong:

invalid_xml = %{
<groceries>
<bread>Wheat
}
(valid_xml?(invalid_xml) == nil)

According to the book, the above is supposed to return
# => false, # That is, it is "valid"

That is wrong. The valid_xml?(invalid_xml) returns nil.

As such, the following statement does not work:

REXML::Document.new(invalid_xml).write

However, the following does work

REXML::Document.new(good_xml).write

Anonymous   
Printed Page 379
1st line of code under Discussion

The following code is incorrect:

red_fish = doc.children[0].children[3].children[1].children[1]

The correct code is the following:

red_fish = doc.children[1].children[3].children[1].children[1]

Note: doc.children[0] refers to the text '
' (i.e., the newline between "%{" and "<acquarium>" specified in the assignment to the variable xml on the top of page 378)

Anonymous   
Printed Page 379
1st line of code under Discussion

The following code is incorrect:

red_fish = doc.children[0].children[3].children[1].children[1]

The correct code is the following:

red_fish = doc.children[1].children[3].children[1].children[1]

Note: doc.children[0] refers to the text '
' (i.e., the newline between "%{" and "<acquarium>" specified in the assignment to the variable xml on the top of page 378)

Anonymous   
Printed Page 380
3rd line of code from top of page

doc.children[0] is wrong

doc.children[1] is correct

doc.children[0] returns => "
"
doc.children[1] returns => <acquarium> ... </>

Anonymous   
Printed Page 380
3rd line of code from top of page

doc.children[0] is wrong

doc.children[1] is correct

doc.children[0] returns => "
"
doc.children[1] returns => <acquarium> ... </>

Anonymous   
Printed Page 391
Second line

second line:

require

causes exception;

ArgumentError: wrong number of arguments (0 for 1)

Probably a misprint - should be eliminated????

Anonymous   
Printed Page 391
Second line

second line:

require

causes exception;

ArgumentError: wrong number of arguments (0 for 1)

Probably a misprint - should be eliminated????

Anonymous   
Printed Page 450
Last paragraph of the introduction of chapter 13 (URL/references)

The 2nd URL seems to be:

http://wiki.rubyonrails.org/rails/pages/ActiveRecord

instead of:

http://wiki.rubyonrails.org/show/ActiveRecord

9460) Heading reads Discussion, but probably should be Solution.

Anonymous   
Printed Page 508
2nd paragraph (code listing)

The code gives an example how one could run many DNS queires in parallel. Unfortunately this code doesn't close the connection. This is now Problem in the example, but could lead to serious problems, if run for more then the 26 queries in the example.

Anonymous   
Printed Page 508
2nd paragraph (code listing)

The code gives an example how one could run many DNS queires in parallel. Unfortunately this code doesn't close the connection. This is now Problem in the example, but could lead to serious problems, if run for more then the 26 queries in the example.

Anonymous   
Printed Page 558
Invocation line after $ cd status

Invocation "./script/generate controller status index" should be
"ruby script/generate controller status index"

Similar error on p.559 where "./script/server" should be
"ruby script/server"

Anonymous   
Printed Page 558
Invocation line after $ cd status

Invocation "./script/generate controller status index" should be
"ruby script/generate controller status index"

Similar error on p.559 where "./script/server" should be
"ruby script/server"

Anonymous   
Printed Page 575
4th paragraph (code)

In the last code of recipe 15.7, "require 'active_support/core_ext'" should be replaced by "require 'active_support'".

If "core_ext" is directly loaded, the search path will not be correctly set and "xml_simple" will not be found. Actually "active_support" will automatically load "active_support/core_ext".

So the correct requires are:

require 'rubygems'
require 'active_support'

Anonymous   
Printed Page 575
4th paragraph (code)

In the last code of recipe 15.7, "require 'active_support/core_ext'" should be replaced by "require 'active_support'".

If "core_ext" is directly loaded, the search path will not be correctly set and "xml_simple" will not be found. Actually "active_support" will automatically load "active_support/core_ext".

So the correct requires are:

require 'rubygems'
require 'active_support'

Anonymous   
Printed Page 577
Third block of code on the page (creation of the view for user/login).

Note, I am Using Ruby 1.9.1 with Rails 2.3.4. This was tested and verified on Mac OS X Snow Leopard (I also tried the Apple provided Ruby 1.8.7) and Fedora 11 using both the Mongrel and WEBrick servers. The project has the complete code from recipes 15.6 and 15.8.

First Bug;
Near the bottom of the page where the reader is creating the view for mywebapp/user/login, "flash" is incorrectly printed as "@flash" four times. This results in Ruby recognizing flash (in the view) as a variable and not a method of ActionController. Since there is no defined variable @flash this error results in an unexpected nil object error for the login action. The correction is simple, remove the '@' before each "flash" and the project runs as expected, or it would if not for a second bug...

The actual code is as follows:
<% if @flash[:message] %><div><%= @flash[:message] %></div><% end %>
<% if @flash[:error] %><div><%= @flash[:error] %></div><% end %>

The code should read:
<% if flash[:message] %><div><%= flash[:message] %></div><% end %>
<% if flash[:error] %><div><%= flash[:error] %></div><% end %>

This also invalidates the explanation paragraph directly below as it is incorrect in referring to the "instance variable" @flash as a "hashlike object" which does not actually exist as we never declared an "@flash" rather, the "flash" method of ActionController is a "hashlike object"...

Second Bug:
After performing the corrections in the first bug, Ruby also chokes on the lines "<%= form_tag :action => 'process_login' %>" and "<% end_form_tag %>". For both lines, you no longer need the equals sign after the opening eRB tag. "end_form_tag" was removed in Rails 2, "end" is simply used. You also need a "do" after the form tag declaration.

The code is as follows:

<%= form_tag :action => 'process_login' %>
Username: <%= text_field "user", "username" %>&#x00A;
Password: <%= text_field "user", "password" %>&#x00A;
<%= submit_tag %>
<%= end_form_tag %>

The code should read:

<% form_tag :action => 'process_login' do %>
Username: <%= text_field "user", "username" %>&#x00A;
Password: <%= text_field "user", "password" %>&#x00A;
<%= submit_tag %>
<% end %>

Ruby 1.9.1, after fixing both errors, will still give the error "undefined method `^' for "?":String" (note it will not be a question mark but a letter). This is a Ruby 1.9.1 incompatibility issue. It has to do with Ruby now returning a character instead of the character code for string[index]. After a little exploration I came up with a simple fix. On a Mac and Linux system with an unmodified install prefix (assumes a source compile) the path to culprit file is "/usr/local/lib/ruby/gems/1.9.1/gems/activesupport-2.3.4/lib/active_support/message_verifier.rb". Modify line 46 to read "result |= a[i].ord ^ b[i].ord" and everything will work fine.

MasterStarman  Nov 02, 2009 
Printed Page 579
Last code block.

It should be mentioned that if using Ruby 1.9 you need to require "digest/sha1" instead of just "sha1".

MasterStarman  Nov 02, 2009 
Printed Page 595,596,597
all code containing 'form_tag' and 'form_for'

form_tag and form_for always go inside "<% %>" tags AFAIK. They used to go
into "<%= %>" tags, tho I don't remember when this change took place (somewhere
between AWDR v. 1 and v.2)

Anonymous   
Printed Page 595,596,597
all code containing 'form_tag' and 'form_for'

form_tag and form_for always go inside "<% %>" tags AFAIK. They used to go
into "<%= %>" tags, tho I don't remember when this change took place (somewhere
between AWDR v. 1 and v.2)

Anonymous   
Printed Page 616
Recipe 14.5

Recipe 14.5 doesn't work. Executing puts SimpleMailer.create_simple_message('lucas@example.com') returns
undefined method 'create_simple_message'

Enrique Condes  Mar 29, 2013 
Printed Page 630
Recipe 16.7. Using a WSDL File to Make SOAP Calls Easier

I got this book to help with SOAP/WSDL issues in Ruby, and Recipe 16.7 is great,
but Google is no longer issuing keys for this API, so I can't try it out to try
to figure out why it is working and my code isn't.

Is an update to this recipe going to be posted using some other web service?
(e.g. Amazon)

Anonymous   
Printed Page 630
Recipe 16.7. Using a WSDL File to Make SOAP Calls Easier

I got this book to help with SOAP/WSDL issues in Ruby, and Recipe 16.7 is great,
but Google is no longer issuing keys for this API, so I can't try it out to try
to figure out why it is working and my code isn't.

Is an update to this recipe going to be posted using some other web service?
(e.g. Amazon)

Anonymous   
Printed Page 637
2nd complete code block

On site 637 th following code is printed:
"DRb.start_service('druby://127.0.0.1:61676', Threadsafe.new)
But on site 636 the class is defined as ThreadsafeHash

Anonymous   
Printed Page 637
2nd complete code block

On site 637 th following code is printed:
"DRb.start_service('druby://127.0.0.1:61676', Threadsafe.new)
But on site 636 the class is defined as ThreadsafeHash

Anonymous   
Printed Page 662
Solution

During the third run of the divide.rb script, the error output says the script
that errored was 'divide_buggy.rb' in three spots of the exception.

In effect, the calling script and erroring script should be the same. Like such:

$ ruby -d divide.rb
Dividing 53 by 0
Exception `ZeroDivisionError' at divide.rb:6 - divided by 0
divide.rb:6:in `/': divided by 0 (ZeroDivisionError)
from divide.rb:6

Anonymous   
Printed Page 662
Solution

In order to get the results described by --debug switch, the user must pass the
switch to Ruby and not the script.

All instances that look like '$ ./divide.rb --debug' should be
'$ ruby --debug divide.rb' otherwise the interpreter never enters the debug state.

Anonymous   
Printed Page 662
Solution

During the third run of the divide.rb script, the error output says the script
that errored was 'divide_buggy.rb' in three spots of the exception.

In effect, the calling script and erroring script should be the same. Like such:

$ ruby -d divide.rb
Dividing 53 by 0
Exception `ZeroDivisionError' at divide.rb:6 - divided by 0
divide.rb:6:in `/': divided by 0 (ZeroDivisionError)
from divide.rb:6

Anonymous   
Printed Page 662
Solution

In order to get the results described by --debug switch, the user must pass the
switch to Ruby and not the script.

All instances that look like '$ ./divide.rb --debug' should be
'$ ruby --debug divide.rb' otherwise the interpreter never enters the debug state.

Anonymous   
Printed Page 760
Second code excerpt in the Solution

The code given completes straightaway, not after 5 seconds as described in the
text and simulated output.

Either change the described output to fit the code, or change the code to fit the
output -- perhaps like this:

start_time = Time.now

Anonymous   
Printed Page 760
Second code excerpt in the Solution

The code given completes straightaway, not after 5 seconds as described in the
text and simulated output.

Either change the described output to fit the code, or change the code to fit the
output -- perhaps like this:

start_time = Time.now

Anonymous   
Printed Page 763-4
all sample code for recipe 20.7

The code for recipe 20.7 does not run as intended when using Ruby 1.9+. It throws no exceptions, but also give no output. It runs well under 1.8.7. I have attempted to debug, but cannot determine what changes, if any, are needed to successfully run under 1.9.1.

Joshua Smith  Sep 02, 2011 
Printed Page 764
2nd code block (line 54 in source file)

The code runs as presented, but if you run the block over 5 times - like this:

1.upto(8) do |i| # <-- changed from 5
pool.dispatch(i) do |i|
print "Job #{i} started.
"
sleep(5-i) # <-- the bug
print "Job #{i} complete.
"
end
end

You get something like:

johnperkins:compare_files jperkins$ ruby thread_test.rb
Job 1 started.
Job 2 started.
Job 3 started.
Pool is full; waiting to run 4...
Pool is full; waiting to run 5...
Pool is full; waiting to run 6...
Pool is full; waiting to run 7...
Pool is full; waiting to run 8...
Job 3 complete.
Job 4 started.
Job 2 complete.
Job 5 started.
Job 5 complete.
Job 4 complete.
Job 7 started.
Exception `ArgumentError' at thread_test.rb:54 - time interval must be positive
Exception in thread #<ThreadPool:0x262b4>: time interval must be positive
Job 6 started.
Exception `ArgumentError' at thread_test.rb:54 - time interval must be positive
Exception in thread #<ThreadPool:0x262b4>: time interval must be positive
Job 8 started.
Exception `ArgumentError' at thread_test.rb:54 - time interval must be positive
Exception in thread #<ThreadPool:0x262b4>: time interval must be positive
Job 1 complete.
johnperkins:compare_files jperkins$

Apparently time travel is an ArgumentError.

Anonymous   
Printed Page 764
2nd code block (line 54 in source file)

The code runs as presented, but if you run the block over 5 times - like this:

1.upto(8) do |i| # <-- changed from 5
pool.dispatch(i) do |i|
print "Job #{i} started.\n"
sleep(5-i) # <-- the bug
print "Job #{i} complete.\n"
end
end

You get something like:

johnperkins:compare_files jperkins$ ruby thread_test.rb
Job 1 started.
Job 2 started.
Job 3 started.
Pool is full; waiting to run 4...
Pool is full; waiting to run 5...
Pool is full; waiting to run 6...
Pool is full; waiting to run 7...
Pool is full; waiting to run 8...
Job 3 complete.
Job 4 started.
Job 2 complete.
Job 5 started.
Job 5 complete.
Job 4 complete.
Job 7 started.
Exception `ArgumentError' at thread_test.rb:54 - time interval must be positive
Exception in thread #<ThreadPool:0x262b4>: time interval must be positive
Job 6 started.
Exception `ArgumentError' at thread_test.rb:54 - time interval must be positive
Exception in thread #<ThreadPool:0x262b4>: time interval must be positive
Job 8 started.
Exception `ArgumentError' at thread_test.rb:54 - time interval must be positive
Exception in thread #<ThreadPool:0x262b4>: time interval must be positive
Job 1 complete.
johnperkins:compare_files jperkins$

Apparently time travel is an ArgumentError.

Anonymous  May 15, 2008