Sunday, December 31, 2006

The job is done


Listen to this article.

Job done. Blog closed.

[Pages 163-5] But it's too slow ! - The profiler


Listen to this article.

Value: High

Level: Easy

Summary:

Ruby comes with a code profiler: it shows you the number of times each
method in the program is called and the average and cumulative time
that Ruby spends in those methods. You can add profiling to your code
using the command-line option "-r profile" or from within the code
using "require 'profile'".

The first thing to notice is that the timings shown are a lot slower
than when the program runs without the profiler. Profiling has a
serious overhead, but the assumption is that it applies across the
board, and therefore the relative numbers are still meaningful.

Memo: None

Example:
require 'profile'
count = 0
words = File.open("/usr/share/dict/words")
while word = words.gets
word = word.chomp!
if word.length == 12
count += 1
end
end
puts "#{count} twelve-character words"

# the following code runs more than five times faster
require 'profile'
words = File.read("/usr/share/dict/words")
count = words.scan(PATT= /^............\n/).size
puts "#{count} twelve-character words"

Reported errata (at 10/17/06 14:17:18 PDT): 1
*Erratum #4666 is a minor suggestion.

Errata I found: 0

My suggestions to the author: 0

Doubts: 1
*"Always try to eliminate unneeded code. Remember to check the code
without the profiler afterward, though—sometimes the slowdown the
profiler introduces can mask other problems.". What does it mean ?

Saturday, December 30, 2006

[Pages 162-3] But it's too slow ! - Benchmark


Listen to this article.

Value: High

Level: Easy

Summary:
Typically, slow-running programs have one or two performance graveyards, places where execution time goes to die. The trick is finding them. The Benchmark module and the Ruby profilers can help.
 
You can use the Benchmark module to time sections of code. For example, we may wonder which is faster: a large loop using variables local to the loop's block or using variables from the surrounding scope.

You have to be careful when benchmarking, because oftentimes Ruby programs can run slowly because of the overhead of garbage collection. Because this garbage collection can happen any time during your program's execution, you may find that benchmarking gives misleading results, showing a section of code running slowly when in fact the slowdown was caused because garbage collection happened to trigger while that code was executing. The Benchmark module has the bmbm method that runs the tests twice, once as a rehearsal and once to measure performance, in an attempt to minimize the distortion introduced by garbage collection. The benchmarking process itself is relatively well mannered—it doesn't slow down your program much.

Memo: Garbage collection could give you misleading results when benchmarking.

Example:
require 'benchmark'
include Benchmark
LOOP_COUNT = 1_000_000
bm(12) do |test|
test.report("normal:") do
LOOP_COUNT.times do |x|
y = x + 1
end
end
test.report("predefine:") do
x = y = 0
LOOP_COUNT.times do |x|
y = x + 1
end
end
end

Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 1
*On page 162 "bmbm" should be "bm".

My suggestions to the author: 0

Doubts: 2
*What is garbage collection ?
*Why is a large loop using variables local to the loop's block slower than a loop using variables from the surrounding scope ?

Thursday, December 28, 2006

[Pages 159-62] But it doesn't work !


Listen to this article.

Value: High

Level: Easy

Summary:

• First and foremost, run your scripts with warnings enabled (the -w
command-line option).
• If you happen to forget a "," in an argument list—especially to
print—you can produce some very odd error messages.
• A parse error at the last line of the source often indicates a
missing end keyword, sometimes quite a bit earlier.
• An attribute setter is not being called. Within a class definition,
Ruby will parse setter= as an assignment to a local variable, not as a
method call. Use the form self.setter= to indicate the method call
(example code 1).
• Objects that don't appear to be properly set up may have been
victims of an incorrectly spelled initialize method (example code 2).
The same kind of thing can happen if you misspell the instance
variable name (example code 3).
• Block parameters are in the same scope as local variables. If an
existing local variable with the same name as a block parameter exists
when the block executes, that variable will be modified by the call to
the block. This may or may not be a Good Thing (example code 4).
• Watch out for precedence issues, especially when using {} instead of
do/end (example code 5).
• If numbers don't come out right, perhaps they're strings. Text read
from a file will be a String and will not be automatically converted
to a number by Ruby. A call to Integer will work wonders (and will
throw an exception if the input isn't a well-formed integer).
• Unintended aliasing—if you are using an object as the key of a hash,
make sure it doesn't change its hash value (or arrange to call
Hash#rehash if it does) (example code 6).
• Make sure the class of the object you are using is what you think it
is. If in doubt, use puts my_obj.class.
• Make sure your method names start with a lowercase letter and class
and constant names start with an uppercase letter.
• If method calls aren't doing what you'd expect, make sure you've put
parentheses around the arguments.
• Make sure the open parenthesis of a method's parameter list butts up
against the end of the method name with no intervening spaces.
• Use irb and the debugger.
• Use Object#freeze. If you suspect that some unknown portion of code
is setting a variable to a bogus value, try freezing the variable. The
culprit will then be caught during the attempt to modify the variable.

Memo: One major technique makes writing Ruby code both easier and more
fun. Develop your applications incrementally. Write a few lines of
code, and then run them. Perhaps use Test::Unit to write some tests.
Write a few more lines of code, and then exercise them. One of the
major benefits of a dynamically typed language is that things don't
have to be complete before you use them.

Example:

class Incorrect
attr_accessor :one, :two
def initialize
one = 1 # incorrect - sets local variable
self.two = 2
end
end
obj = Incorrect.new
obj.one → nil
obj.two → 2

class Incorrect
attr_reader :answer
def initialise # < < < spelling error
@answer = 42
end
end
ultimate = Incorrect.new
ultimate.answer → nil

class Incorrect
attr_reader :answer
def initialize
@anwser = 42 #<« spelling error
end
end
ultimate = Incorrect.new
ultimate.answer → nil

c = "carbon"
i = "iodine"
elements = [ c, i ]
elements.each_with_index do |element, i|
# do some chemistry
end
c → "carbon"
i → 1

def one(arg)
if block_given?
"block given to 'one' returns #{yield}"
else
arg
end
end
def two
if block_given?
"block given to 'two' returns #{yield}"
end
end
result1 = one two {
"three"
}
result2 = one two do
"three"
end
puts "With braces, result = #{result1}"
puts "With do/end, result = #{result2}"

arr = [1, 2]
hash = { arr => "value" }
hash[arr] → "value"
arr[0] = 99
hash[arr] → nil
hash.rehash → {[99, 2]=>"value"}
hash[arr] → "value"

Reported errata (at 10/17/06 14:17:18 PDT): 1
*See below

Errata I found: 1
*Reported erratum #2822 refers to page 133, not to page 161 as stated.

My suggestions to the author: 0

Doubts: 2
*I didn't understand example code 5.
*"Output written to a terminal may be buffered. This means you may not
see a message you write immediately. In addition, if you write
messages to both $stdout and $stderr, the output may not appear in the
order you were expecting. Always use nonbuffered I/O (set sync=true)
for debug messages." What does it mean ?

[Pages 156-9] Interactive Ruby, Editor support


Listen to this article.

Value: Low

Level: Easy

Summary:
IRb also allows you to create subsessions, each one of which may have
its own context. For example, you can create a subsession with the
same (top-level) context as the original session or create a
subsession in the context of a particular class or instance.
You can also run Ruby code from inside an editor: in Textmate try
selecting some code and press Ctrl+Shift+E.

Memo: None

Example:
irb [ irb-options ] [ ruby_script ] [ program-arguments ]

Reported errata (at 10/17/06 14:17:18 PDT): 1
*Erratum #4066 reports a problem using IRb with Windows XP SP 2 / Finnish locale

Errata I found: 0

My suggestions to the author: 0

Doubts: 0

Tuesday, December 26, 2006

[Pages 155-6]: When trouble strikes - Ruby debugger


Listen to this article.

Value: Low

Level: Easy

Summary:

The debugger supports the usual range of features you'd expect,
including the ability to set breakpoints, to step into and step over
method calls, and to display stack frames and variables, list the
instance methods defined for a particular object or class, list and
control separate threads within Ruby. If your Ruby installation has
readline support enabled, you can use cursor keys to move back and
forth in command history and use line-editing commands to amend
previous input.


Memo: None

Example:

# ruby -r debug [ debug-options ] [ programfile ] [ program-arguments ]

% ruby -r debug t.rb
Debug.rb
Emacs support available.
t.rb:1:def fact(n)
(rdb:1) list 1-9
[1, 10] in t.rb
=> 1 def fact(n)
2 if n <= 0
3 1
4 else
5 n * fact(n-1)
6 end
7 end
8
9 p fact(5)
(rdb:1) b 2
Set breakpoint 1 at t.rb:2
(rdb:1) c
breakpoint 1, fact at t.rb:2
t.rb:2: if n <= 0
(rdb:1) disp n
1: n = 5
(rdb:1) del 1
(rdb:1) watch n==1
Set watchpoint 2
(rdb:1) c
watchpoint 2, fact at t.rb:fact
t.rb:1:def fact(n)
1: n = 1
(rdb:1) where
--> #1 t.rb:1:in `fact'
#2 t.rb:5:in `fact'
#3 t.rb:5:in `fact'
#4 t.rb:5:in `fact'
#5 t.rb:5:in `fact'
#6 t.rb:9
(rdb:1) del 2
(rdb:1) c
120

Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 0

My suggestions to the author: 0

Doubts: 0

[Pages 155-6]: When trouble strikes - Ruby debugger


Listen to this article.

Value: Low

Level: Easy

Summary:

The debugger supports the usual range of features you'd expect,
including the ability to set breakpoints, to step into and step over
method calls, and to display stack frames and variables, list the
instance methods defined for a particular object or class, list and
control separate threads within Ruby. If your Ruby installation has
readline support enabled, you can use cursor keys to move back and
forth in command history and use line-editing commands to amend
previous input.


Memo: None

Example:

# ruby -r debug [ debug-options ] [ programfile ] [ program-arguments ]

% ruby -r debug t.rb
Debug.rb
Emacs support available.
t.rb:1:def fact(n)
(rdb:1) list 1-9
[1, 10] in t.rb
=> 1 def fact(n)
2 if n <= 0
3 1
4 else
5 n * fact(n-1)
6 end
7 end
8
9 p fact(5)
(rdb:1) b 2
Set breakpoint 1 at t.rb:2
(rdb:1) c
breakpoint 1, fact at t.rb:2
t.rb:2: if n <= 0
(rdb:1) disp n
1: n = 5
(rdb:1) del 1
(rdb:1) watch n==1
Set watchpoint 2
(rdb:1) c
watchpoint 2, fact at t.rb:fact
t.rb:1:def fact(n)
1: n = 1
(rdb:1) where
--> #1 t.rb:1:in `fact'
#2 t.rb:5:in `fact'
#3 t.rb:5:in `fact'
#4 t.rb:5:in `fact'
#5 t.rb:5:in `fact'
#6 t.rb:9
(rdb:1) del 2
(rdb:1) c
120

Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 0

My suggestions to the author: 0

Doubts: 0

Sunday, December 24, 2006

Pages 152-4: Organizing and running tests - Test suites


Listen to this article.

Value: Low

Level: Easy

Summary:
You can group test cases together into test suites, letting you run
them all as a group. This is easy to do in Test::Unit. All you have to
do is create a Ruby file that requires test/unit, and then requires
each of the files holding the test cases you want to group.
This way, you build yourself a hierarchy of test material individual
tests, all the tests in a file, group many file in a test suite and
run them as a unit, group test suites into other test suites.
Conventionally test cases are in files named tc_xxx and test suites
are in files named ts_xxx.

Memo: You should group cases that test different sets of functions in
different tests suites.

Example:
# file ts_dbaccess.rb
require 'test/unit'
require 'tc_connect'
require 'tc_query'
require 'tc_update'
require 'tc_delete'


Reported errata (at 10/17/06 14:17:18 PDT): 2
*Erratum #1946 is a minor typo.
*Erratum #1947, see below.

Errata I found: 1
*Erratum #1947 is a wrong erratum reporting: it's supposed to be
present in P1.0 Oct 4 2004, but it's not.

My suggestions to the author: 0

Doubts: 0

Friday, December 22, 2006

Pages 151-2: Organizing and Running Tests - Where to put tests


Listen to this article.

Value: Average

Level: Normal

Summary:
If you run a test case from the command line, Test::Unit is clever enough to notice that there's no main program, so it collects up all the test case classes and runs each in turn. From the command line you can also run just a particular method. You should create a test/ directory where to place all your test source files. This directory is then placed parallel to the lib/ directory containing the code you're developing. This works well as a way of organizing files but leaves you with a small problem: how do you tell Ruby where to find the library files to test ? At the front of your test code (for
example in test_roman.rb), add the following line:

$:.unshift File.join(File.dirname(__FILE__), "..", "lib")

This magic works because the test code is in a known location relative to the code being
tested. It starts by working out the name of the directory from which the test file is run and then constructing the path to the files under test. This directory is then prepended to the load path (the variable $:). From then on, code such as require 'roman' will search the library being tested first.

There are worse ways to achieve this goal: you could build the path into require statements in the test and run the tests from the test/ subdirectory, but if the source file to be tested requires other source files, they won't be found in Ruby's $LOAD_PATH. You could also run the tests from the directory containing the library being tested (% ruby ../test/test_roman.rb). Because the current directory is in the load path, the test code will be able to find it, but this approach will obviously break down if you run the test from somewhere else in your system.

Memo: Always tell your tests where to find the source code to be tested.

Reported errata (at 10/17/06 14:17:18 PDT): 1
* Erratum #1945 is a minor typo.

Errata I found: 0

My suggestions to the author: 0

Doubts: 1
*I didn't understand this sentence: "A second, less immediate problem is that we won't be able to use these same tests to test our classes once installed on a target system, as then they'll be referenced simply using require 'roman'."

Pages 151-2: Organizing and Running Tests - Where to put tests


Listen to this article.

Value: Average

Level: Normal

Summary:
If you run a test case from the command line, Test::Unit is clever enough to notice that there's no main program, so it collects up all the test case classes and runs each in turn. From the command line you can also run just a particular method. You should create a test/ directory where to place all your test source files. This directory is then placed parallel to the lib/ directory containing the code you're developing. This works well as a way of organizing files but leaves you with a small problem: how do you tell Ruby where to find the library files to test ? At the front of your test code (for
example in test_roman.rb), add the following line:

$:.unshift File.join(File.dirname(__FILE__), "..", "lib")

This magic works because the test code is in a known location relative to the code being
tested. It starts by working out the name of the directory from which the test file is run and then constructing the path to the files under test. This directory is then prepended to the load path (the variable $:). From then on, code such as require 'roman' will search the library being tested first.

There are worse ways to achieve this goal: you could build the path into require statements in the test and run the tests from the test/ subdirectory, but if the source file to be tested requires other source files, they won't be found in Ruby's $LOAD_PATH. You could also run the tests from the directory containing the library being tested (% ruby ../test/test_roman.rb). Because the current directory is in the load path, the test code will be able to find it, but this approach will obviously break down if you run the test from somewhere else in your system.

Memo: Always tell your tests where to find the source code to be tested.

Reported errata (at 10/17/06 14:17:18 PDT): 1
* Erratum #1945 is a minor typo.

Errata I found: 0

My suggestions to the author: 0

Doubts: 1
*I didn't understand this sentence: "A second, less immediate problem is that we won't be able to use these same tests to test our classes once installed on a target system, as then they'll be referenced simply using require 'roman'."

Pages 151-2: Organizing and Running Tests - Where to put tests


Listen to this article.

Value: Average

Level: Normal

Summary:
If you run a test case from the command line, Test::Unit is clever enough to notice that there's no main program, so it collects up all the test case classes and runs each in turn. From the command line you can also run just a particular method. You should create a test/ directory where to place all your test source files. This directory is then placed parallel to the lib/ directory containing the code you're developing. This works well as a way of organizing files but leaves you with a small problem: how do you tell Ruby where to find the library files to test ? At the front of your test code (for
example in test_roman.rb), add the following line:

$:.unshift File.join(File.dirname(__FILE__), "..", "lib")

This magic works because the test code is in a known location relative to the code being
tested. It starts by working out the name of the directory from which the test file is run and then constructing the path to the files under test. This directory is then prepended to the load path (the variable $:). From then on, code such as require 'roman' will search the library being tested first.

There are worse ways to achieve this goal: you could build the path into require statements in the test and run the tests from the test/ subdirectory, but if the source file to be tested requires other source files, they won't be found in Ruby's $LOAD_PATH. You could also run the tests from the directory containing the library being tested (% ruby ../test/test_roman.rb). Because the current directory is in the load path, the test code will be able to find it, but this approach will obviously break down if you run the test from somewhere else in your system.

Memo: Always tell your tests where to find the source code to be tested.

Reported errata (at 10/17/06 14:17:18 PDT): 1
* Erratum #1945 is a minor typo.

Errata I found: 0

My suggestions to the author: 0

Doubts: 1
*I didn't understand this sentence: "A second, less immediate problem is that we won't be able to use these same tests to test our classes once installed on a target system, as then they'll be referenced simply using require 'roman'."

Wednesday, December 20, 2006

[temp] Pages 148-50: Structuring tests


Listen to this article.

Value: Average

Level: Normal

Summary:
You include Test::Unit facilities in your unit test with the "require 'test/unit'" line.
Unit tests seem to fall quite naturally into high-level groupings, called test cases, and
lower level groupings, the test methods themselves. Quite often you'll find all of the test methods within a test case setting up a particular scenario. Each test method then probes some aspect of that scenario. Finally, each method may then tidy up after itself. For example, we could be testing a class that extracts jukebox playlists from a database.

Memo: The classes that represent test cases must be subclasses of Test::Unit::TestCase.
Within a TestCase class, the setup and teardown methods bracket each test, rather than being run once per test case.

Example:

require 'test/unit'
require 'playlist_builder'
require 'dbi'
class TestPlaylistBuilder < Test::Unit::TestCase
def test_empty_playlist
db = DBI.connect('DBI:mysql:playlists')
pb = PlaylistBuilder.new(db)
assert_equal([], pb.playlist())
db.disconnect
end
def test_artist_playlist
db = DBI.connect('DBI:mysql:playlists')
pb = PlaylistBuilder.new(db)
pb.include_artist ("krauss")
assert(pb.playlist.size > 0, "Playlist shouldn't be empty")
pb.playlist.each do |entry|
assert_match(/krauss/i, entry.artist)
end
db.disconnect
end
def test_title_playlist
db = DBI.connect('DBI:mysql:playlists')
pb = PlaylistBuilder.new(db)
pb.include_title("midnight")
assert(pb.playlist.size > 0, "Playlist shouldn't be empty")
pb.playlist.each do |entry|
assert_match(/midnight/i, entry.title)
end
db.disconnect
end
# ...
end

Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 0

My suggestions to the author: 0

Doubts: 0

Monday, December 18, 2006

[temp] pages 147-8


Listen to this article.

Summary:

You can test exceptions, too (see example code 2).

The final parameter to every assertion is a message, which is output before any
failure message. This normally isn't needed, as Test::Unit's messages are normally
pretty reasonable. The one exception is the test assert_not_nil, where the message
"<nil> expected to not be nil" doesn't help much. In that case, you may want to add
some annotation of your own (see example code 3).

 

Example:

require 'roman'
require 'test/unit'
class TestRoman < Test::Unit::TestCase
NUMBERS = [
[ 1, "i" ], [ 2, "ii" ], [ 3, "iii" ],
[ 4, "iv"], [ 5, "v" ], [ 9, "ix" ]
]
def test_simple
NUMBERS.each do |arabic, roman|
r = Roman.new(arabic)
assert_equal(roman, r.to_s)
end
end
end

require 'roman'
require 'test/unit'
class TestRoman < Test::Unit::TestCase
def test_range
assert_raise(RuntimeError) { Roman.new(0) }
assert_nothing_raised() { Roman.new(1) }
assert_nothing_raised() { Roman.new(499) }
assert_raise(RuntimeError) { Roman.new(5000) }
end
end

require 'test/unit'
class TestsWhichFail < Test::Unit::TestCase
def test_reading
assert_not_nil(ARGF.read, "Read next line of input")
end
end

produces:

Loaded suite -
Started
F
Finished in 0.033581 seconds.
1) Failure:
Read next line of input.
<nil> expected to not be nil.
1 tests, 1 assertions, 1 failures, 0 errors
test_reading(TestsWhichFail) [ prog.rb:4]:

Reported errata: 1
#1944 minor typo
 

Saturday, December 16, 2006

[temp] Pages 144-6: Test::Unit Framework - Assertions == Expected results (part I)


Listen to this article.

Value: Low

Level: Easy

Summary:
The Test::Unit framework gives you: a way of expressing individual tests, a framework for structuring the tests, flexible ways of invoking the tests. Rather than have you write series of individual if statements in your tests, Test::Unit provides a series of assertions that achieve the same thing. Although a number of different styles of assertion exist, they all follow basically the same pattern. Each assertion
gives you a way of specifying a desired result or outcome and a way of passing in the
actual outcome. If the actual doesn't equal the expected, the assertion outputs a nice
message and records the fact as a failure.

Memo:

Example:

require 'roman'
require 'test/unit'
class TestRoman < Test::Unit::TestCase
def test_simple
assert_equal("i", Roman.new(1).to_s)
assert_equal("ix", Roman.new(9).to_s)
end
end
Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 0

My suggestions to the author: 0

Doubts: 1
* Example code not understood

Prova


Listen to this article.



--
Everything is possible.

Thursday, December 14, 2006

[temp] Pages 143-4: Unit testing


Listen to this article.

Value: Low

Level: Easy

Summary:
Unit testing is running a program that calls part of your
application's code, get back some results, and then check the results
are what you expected. Various unit testing frameworks have emerged to
help structure the testing process. Ruby comes with one preinstalled,
Nathaniel Talbott's Test::Unit framework. Unit testing focuses on
small chunks (units) of code, typically individual methods or lines
within methods.

Memo: 0

Example:
Let's say we're testing a Roman number class. So far the code is
pretty simple: it just lets us create an object representing a certain
number and display that object in Roman numerals.
We could test this code by writing another program, like this.
require 'roman'

r = Roman.new(1)
fail "'i' expected" unless r.to_s == "i"
r = Roman.new(9)
fail "'ix' expected" unless r.to_s == "ix"

Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 0

My suggestions to the author: 0

Doubts: 0

Tuesday, December 12, 2006

Pages 141-2: Running multiple processes - Independent children, Blocks and subprocesses


Listen to this article.

Value: Average

Level: Hard

Summary:
You can give a subprocess its job, come back to your business, and the wait for its end.
In the first example code the call to Kernel.fork returns a process ID in the parent, and nil in the child, so the child process will perform the Kernel.exec call and run sort. Process.wait call waits for the sort to complete (and returns its process ID).
If you don't want to wait you can be notified when a child exits. Just set up a signal handler using Kernel.trap.
In the second example code we set up a trap on SIGCLD, which is the signal sent on death of child process.
If you pass IO.popen a command, such as date, and then a block, the block will be passed an IO object as a parameter. The IO object will be closed automatically when the code block exits, just as it is with File.open.
If you associate a block with Kernel.fork, the code in the block will be run in a Ruby subprocess, and the parent will continue after the block.

Memo: $? is a global variable that contains information on the termination of a subprocess.

Example:
exec("sort testfile > output.txt") if fork.nil?
# The sort is now running in a child process
# carry on processing in the main program
# ... dum di dum ...
# then wait for the sort to finish
Process.wait

trap("CLD") do
pid = Process.wait
puts "Child pid #{pid}: terminated"
end
exec("sort testfile > output.txt") if fork.nil?
# do other stuff...

IO.popen("date") {|f| puts "Date is #{f.gets}" }

fork do
puts "In child, pid = #$$" # what is $$ ?
exit 99
end
pid = Process.wait
puts "Child terminated, pid = #{pid}, status = #{$?.exitstatus}"

Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 0

My suggestions to the author: 2
* (see doubts below)

Doubts: 2
* How does fork.nil? exactly work ?
* What is $$ ?

Sunday, December 10, 2006

[uncomplete] Pages 139-41: Running multiple processes - Spawning new processes


Listen to this article.

Value: Average

Level: Hard

Summary:
Sometimes you may want to split a task into several process-sized chunks, or perhaps you need to run a separate process that was not written in Ruby. You have several ways to spawn a separate process.
The method Kernel.system executes the given command in a subprocess, and you can capture the standard output of a subprocess using the  backquote (or backtick) method. IO.popen method runs a command as a subprocess and connects that subprocess's standard input and standard output to a Ruby IO object: every input you'll give this object will be given to the subprocess, and every subprocess output will be available in the object. [On some platforms, popen has one more twist. If the command you pass it is a single minus sign ( – ), popen will fork a new Ruby interpreter.] [add examples] [add references] In addition to the popen method, some platforms support the methods Kernel.fork, Kernel.exec, and IO.pipe. The file-naming convention of many IO methods and Kernel.open will also spawn subprocesses if you put a | as the first character of the filename.

Memo: If the subprocess response output doesn't get flushed, your program will hang forever. Note that you cannot create pipes using File.new; it's just for files.

Example:

system("date")
myname = `uname`
puts "My name is " + myname
date_in_array = IO.popen("date") {|a| a.readlines}
print date_in_array

Reported errata (at 10/17/06 14:17:18 PDT): 2
* #1943 #2815 both regard the obvious lack of a program called "pig" on some systems.

Errata I found: 0

My suggestions to the author: 1
* Give more details on IO#close_write.

Doubts: 2
* Is it possible to give options to a command stored in an IO object ?
* When I run "IO.popen("-")" in my IRb, input from my keyboard seems not to flow properly. Why ?

Friday, December 08, 2006

Pages 137-9: Mutual exclusion - Queues, Condition variables


Listen to this article.

Value: High

Level: Hard

Summary:
The Queue class, located in the thread library, implements a thread-safe queuing mechanism. Sometimes you need synchronization between threads based a condition: to do this you can use condition variables. A condition variable is a controlled way of communicating an event (or a condition) between two threads. One thread can wait on the condition, and the other can signal it.

Memo: If two threads are looping working on the same shared data, you're stuck: you'll need to use condition variables.

Example:
require 'monitor'
SONGS = [
'Blue Suede Shoes',
'Take Five',
'Bye Bye Love',
'Rock Around The Clock',
'Ruby Tuesday'
]
START_TIME = Time.now
def timestamp
(Time.now - START_TIME).to_i
end
# Wait for up to two minutes between customer requests
def get_customer_request
sleep(120 * rand)
song = SONGS.shift
puts "#{timestamp}: Requesting #{song}" if song
song
end
# Songs take between two and three minutes
def play(song)
puts "#{timestamp}: Playing #{song}"
sleep(120 + 60*rand)
end
ok_to_shutdown = false
# and here's our original code
playlist = []
playlist.extend(MonitorMixin)
plays_pending = playlist.new_cond
# Customer request thread  
customer = Thread.new do
loop do
req = get_customer_request
break unless req
playlist.synchronize do
playlist << req
plays_pending.signal
end
end
end
# Player thread
player = Thread.new do
loop do
song = nil
playlist.synchronize do
break if ok_to_shutdown && playlist.empty?
plays_pending.wait_while { playlist.empty? }
song = playlist.shift
end
break unless song
play(song)
end
end
customer.join
ok_to_shutdown = true
player.join


Reported errata (at 10/17/06 14:17:18 PDT): 1
  • #1942 is a minor typo on page 139.

Errata I found: 3
  • On page 137: duplicated word 'thread' in code comment.
  • On page 137: there's an incongruence: if the list gets emptied, there should be a thread for emptying it. Unfortunately the author talks about only two threads: one for adding to, one for reading the list.
  • On page 138: on line 26 of the code there's a comment states: "and here's our original code". It's not true, as the code is different from the one on page 137. Should be: "and here's a code similar to the one on page 137".

My suggestions to the author: 1 (3)
  • Totally rewrite this pages from scratch (see doubts below), or at least add these comments to the example code:
    • Line 9: add "# executed when the program starts".
    • Line 10: add "# chronologically increasing values".
    • Line 17: add "# if all the songs in SONGS have been played, song will be false".

Doubts: 9 (20)
My doubts all concern the example code on pages 138-9.
  • Line 2
    • What is SONGS ?
    • Is it an array of songs previously selected by the user ?
  • Line 13
    • What does this comment mean ? Why wait ?
  • Line 14
    • Why does get_customer_request method get the data from a premade array and not from the customer, as the method name suggests ?
  • Line 18
    • Why not use the return() method ?
    • Why does Ruby not raise an error in this case where the return() method is not used ?
  • Line 20
    • Does this comment mean that the shortest song has a duration of 120 seconds and the longest has a duration of 180 seconds ?
    • If yes, why should this information be useful ?
  • Line 23
    • Why let the program sleep instead of playing ?
    • Why does the play() method not play anything ?
  • Line 29
    • What is new_cond() ?
    • What does it do ?
    • Why is there no reference to it in the book's index ?
  • Line 37
    • What is signal() ?
    • What does it do ?
    • Why is there no refernce to it in the book's index ?
  • Line 45
    • What does this block do ?
  • Line 47
    • What's wait_while method ?
    • What does it do ?
    • Why is there no reference to it in the book's index ?

Wednesday, December 06, 2006

Test


Listen to this article.

Dec 7 2006 Indnav + BlogMailr test (w/ end tag). #end

Mail sent from Future Email of Indnav.com

Test


Listen to this article.

Dec 7 2006 Indnav + BlogMailr test (w/ end tag). #end

Mail sent from Future Email of Indnav.com

Pages 136-7: Mutual exclusion - Monitors (part II)


Listen to this article.

Value: Average

Level: Easy

Summary:
You can include MonitorMixin instead of using Monitor as a superclass. If all accesses to all objects of the class require synchronization, you'll put synchronization inside the resource being synchronized (see previous post). Otherwise you can use an external monitor.

Memo: While external monitors are safe, try to avoid making specific objects into monitors.

Example:
require 'monitor'
class Counter
    attr_reader :count
    def initialize
        @count = 0
    end
    def tick
        @count += 1
    end
end
c = Counter.new
lock = Monitor.new
t1 = Thread.new { 10000.times { lock.synchronize { c.tick } } }
t2 = Thread.new { 10000.times { lock.synchronize { c.tick } } }
t1.join; t2.join
c.count

Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 0

My suggestions to the author: 0

Doubts: 0

Monday, December 04, 2006

Pages 133-5: Mutual exclusion - Monitors


Listen to this article.

Value: High

Level: Hard

Summary:
When you work with shared data, race conditions can occur. For example, the simple +=() operator breaks down into 3 different operations within the Ruby interpreter (load, add, store). If different threads are using this operator on some shared data, race conditions could obviously occur. To avoid these bugs you can use mutual exclusion, in form of several secure synchronization schemes. You could use Thread.critical=(), the Sync library, the Mutex_m library, the Queue class or the Monitor library. Monitors wrap an object containing some kind of resource with synchronization functions. You can protect a block using Monitor#synchronize(): the code within this block will be accessible only to one thread at a time for any particular monitor object.

Memo: Use mutual exclusion when accessing and writing shared data in a multithreaded program.

Example:

# Bugged code
class Counter
attr_reader :count
def initialize
@count = 0
super
end
def tick
@count += 1
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.tick } }
t2 = Thread.new { 10000.times { c.tick } }
t1.join
t2.join
c.count

# Debugged code
require 'monitor'
class Counter < Monitor
attr_reader :count
def initialize
@count = 0
super
end
def tick
synchronize do
@count += 1
end
end
end
c = Counter.new
t1 = Thread.new { 10000.times { c.tick } }
t2 = Thread.new { 10000.times { c.tick } }
t1.join; t2.join
c.count

Reported errata (at 10/17/06 14:17:18 PDT): 1

* Here there's the full text of erratum #2092, and the author's answer: "The code print out shows c.count as being 20000, while the text says that it shouldn't be. The script itself seems fine as running it yields values other than 20000. (Dave says: yeah... they changed the thread scheduler, so both threads now run to completion. I've upped the count) —Justin"


Errata I found: 3

* On page 135, line 14 of table 11.1 states "t2: add 1 to val 4". It should be "t2: add 1 to val 3".
* On page 134 I read "Perhaps surprisingly, the count doesn't equal 20,000". I couldn't verify this statement. Nearly 50% of the times I ran the code I got 20.000. This is what reported erratum #2092 is about. Answering erratum #2092, the author states that now the count equals 20.000, and that he updated it in a new version of the book. Unfortunately running the code I got values other than 20.000 nearly 50% of the times.
* On page 792, in the index, method super is associated to page 554. Unfotunately, on page 554 there's nothing about method super.


My suggestions to the author: 1 (5)

* (See doubts 1-5)

Doubts: 5 (10)

* On page 134, line 5 of the code calls the method super.
o Why ?
o What does it exactly do in this particular case ?
o Why does the code run the same with or without it ?
o Which class/module does super method belong ?
* Why does the count not always equal the same number ?
o Why does the count sometime equal 20.000 even if the code is bugged ?
o Why does the count not always equal 20.000, even if "they changed the thread scheduler" (Dave Thomas, answer to reported erratum #2092) ?
* What is the rule behind race conditions ?
* Running the code on page 134 in my IRb (see below, blank lines abridged), I once got a count equal to -517607059. Why ?

irb(main):113:0> class Counter
irb(main):114:1> attr_reader :count
irb(main):115:1> def initialize
irb(main):116:2> @count = 0
irb(main):117:2> super
irb(main):118:2> end
irb(main):119:1> def tick
irb(main):120:2> @count += 1
irb(main):121:2> end
irb(main):122:1> end
=> nil
irb(main):123:0> c = Counter.new
=> #
irb(main):124:0> t1 = Thread.new { 10000.times { c.tick } }
=> #
irb(main):125:0> t2 = Thread.new { 10000.times { c.tick } }
=> #
irb(main):126:0> t1.join
=> #
irb(main):127:0> t2.join
=> #
irb(main):128:0> c.count
=> -517607059

* On page 135, class Counter is set as subclass of "Monitor".
o Is monitor also a Ruby built-in class, or just a library you can require in your programs ?
o Why is class Counter set as subclass of "Monitor" ?
o If I want to create a monitor object, do I need to both require monitor library and make my object's class a sublcass of class Monitor or not ?

Saturday, December 02, 2006

Pages 132-3


Listen to this article.

Value: Medium

Level: Average

Summary:
You can control thread scheduler. Invoking Thread.stop stops the current thread, and invoking Thread#run. Thread.pass deschedules the current thread, allowing others to run, and Thread#join and Thread#value suspend the calling thread until a given thread finishes.

Memo: If possible, leave threads independent.

Example:
class Chaserattr_reader :countdef initialize(name)@name = name@count = 0enddef chase(other)while @count <> 1Thread.pass #endprint "#@name: #{count}\n"@count += 1 print "#@name: #{count}\n"endendendc1 = Chaser.new("A")c2 = Chaser.new("B")threads = [Thread.new { Thread.stop; c1.chase(c2) },Thread.new { Thread.stop; c2.chase(c1) }]start_index = rand(2)threads[start_index].runthreads[1 - start_index].runthreads.each {t t.join }

Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 0

My suggestions to the author: 0Doubts: 0