Thursday, November 30, 2006

Fwd: Pages 130-1: Threads and Exceptions


Listen to this article.

Value: Medium

Level: Easy

Summary:
By default if a thread raises an unhandled exception, the exception kills the thread. If you set the abort_on_exception flag to true, or use -d to turn on the interpreter debug flag, an unhandled exception kills all running threads.

Memo: Inside threads, you better use print() instead of puts().

Example:

threads = []
4.times do |number|
    threads << Thread.new(number) do |i|
        raise "Boom!" if i == 2
        print "#{i}\n"
    end
end
threads.each {|t| t.join }

Thread.abort_on_exception = true
threads = []
4.times do |number|
    threads << Thread.new(number) do |i|
        raise "Boom!" if i == 2
        print "#{i}\n"
    end
end
threads.each {|t| t.join }

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

Errata I found: 0

My suggestions to the author: 1

  • In the last paragraph on page 121 the author explains why is not safe to use puts() inside threads. Unfortunately the explanation is not exhaustive, because the modal verbs in the statement "a thread could get scheduled, and the output would be interleaved" do not make clear the rule behind the phenomenon.

Doubts: 1

  • Is there a rule behind the fact that if I use puts() inside threads "a thread could get scheduled, and the output would be interleaved" ? 

Wednesday, November 29, 2006

Pages 129-30: Manipulating threads, thread variables


Listen to this article.

Value: High

Level: Hard

Summary:
When a Ruby program terminates, all threads are killed, regardless of their states. Thread#join blocks the program until the calling thread is finished. Other useful methods are: Thread#value(), Thread.current, Thread.list, Thread#status and Thread#alive?, Thread#priority=(). If you need per-thread variables that can be accessed by other threads, including the main thread, you can treat the thread object as an Hash with two special methods, Thread#[]=() and Thread#[].

Memo: You can give Thread#join a timeout parameter. If your threads share a variable, beware of race conditions.

Example:

count = 0
threads = []
10.times do |i|
    threads[i] = Thread.new do
        Thread.current["mycount"] = count
        count += 1
    end
end
threads.each do |t|
    t.join
    print "#{t}'s count= #{t["mycount"]}\n"
end
puts "Final count = #{count}"

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

Errata I found : 1

  • On page 129, second paragraph, you find this sentence: "The calling thread will block until the given thread is finished". As the verb "to block" is transitive, it seems to lack the object, to say "your program".

My suggestions to the author: 3 (4)

  • Make the first example code on page 130 more clear and comprehensible.
    •  Pull out the 5th line.
    •  Explode the block on the 10th line.
  • Give an example for Thread#value
  • Explain in detail how Thread#[]= and Thread#[] *exactly* work

Doubts: 1 (6)
  • How do Thread#[]= and Thread#[] *exactly* work ?
    • Do they automatically create an Hash object assigning it to a variable whose name is the same as the thread object id ?
    • What is the scope of this special Hash object ?
    • Does this special Hash object exist just to give you access to per-thread variables from other threads or does it have other uses, too ?
    • How many pairs of key and value can this special Hash object hold ?
    • Why can I retrieve the value associated with the/a key of this special Hash object treating the key either as a standard key or as a symbol, while this procedure is not allowed when dealing with a standard Hash object ?

Tuesday, November 28, 2006

Pages 129-30: Manipulating threads, thread variables


Listen to this article.

Value: High

Level: Hard

Summary:
When a Ruby program terminates, all threads are killed, regardless of their states. Thread#join blocks the program until the calling thread is finished. Other useful methods are: Thread#value(), Thread.current, Thread.list, Thread#status and Thread#alive?, Thread#priority=(). If you need per-thread variables that can be accessed by other threads, including the main thread, you can treat the thread object as an Hash with two special methods, Thread#[]=() and Thread#[].

Memo: You can give Thread#join a timeout parameter. If your threads share a variable, beware of race conditions.

Example:

count = 0
threads = []
10.times do |i|
threads[i] = Thread.new do
Thread.current["mycount"] = count
count += 1
end
end
threads.each do |t|
t.join
print "#{t}'s count= #{t["mycount"]}\n"
end
puts "Final count = #{count}"

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

Errata I found : 1

  • On page 129, second paragraph, you find this sentence: "The calling thread will block until the given thread is finished". As the verb "to block" is transitive, it seems to lack the object, to say "your program".

My suggestions to the author: 3 (4)

  • Make the first example code on page 130 more clear and comprehensible.
    • Pull out the 5th line.
    • Explode the block on the 10th line.
  • Give an example for Thread#value
  • Explain in detail how Thread#[]= and Thread#[] *exactly* work

Doubts: 1 (6)
  • How do Thread#[]= and Thread#[] *exactly* work ?
    • Do they automatically create an Hash object assigning it to a variable whose name is the same as the thread object id ?
    • What is the scope of this special Hash object ?
    • Does this special Hash object exist just to give you access to per-thread variables from other threads or does it have other uses, too ?
    • How many pairs of key and value can this special Hash object hold ?
    • Why can I retrieve the value associated with the/a key of this special Hash object treating the key either as a standard key or as a symbol, while this procedure is not allowed when dealing with a standard Hash object ?

Sunday, November 26, 2006

Pages 127-8: Multithreading


Listen to this article.

Value: High

Level: Medium

Summary:
If you want parallelism in your code you can split up cooperating tasks within the program, using multiple threads. Thread#new() creates and runs a new thread to execute the instructions given in block. Any arguments passed to Thread.new are passed into the block. A thread shares all global, instance, and local variables that are in existence at the time the thread starts. However, local variables created within a thread's block are truly local to that thread.

Memo:
If you want to pass a variable to a thread block and work with it inside the block, you better create a brand new one. Don't share variables between threads (to say, don't use inside a thread a variable which already exists before Thread#new() is called): if you do it a following thread *could* manipulate it before the previous thread has finished working with it. You would end up with a bug difficult to track down.

Example:
def try_threads
x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" }
sleep 0.3
end
try_threads

Reported errata (at 10/17/06 14:17:18 PDT): 1
  • #1941 is a minor typo.
Errata I found: 1
  • Running the sample code on page 128 in my IRb I get "Got www.google.com: Moved Temporarily", running it on ruby.ch I get "Got www.google.com: Found", instead of "Got www.google.com: OK "

My suggestions to the author: 2
  • On page 127 paragraphs 2, 3 and 4 concern the difference between native threads and Ruby's in-process, implemented threads. Here the author is assuming the reader knows what he's talking about: it's not always true. I suggest to explain it all with much more detail.
  • On page 128 the author should explicit the reason behind the order of the results of the code produced, and why this order could be different when run by the reader.

Doubts: 0

Friday, November 24, 2006

[temp] Pages 125-6: Talking to networks


Listen to this article.

Value: Medium

Level: Average

Summary:
In the socket library Ruby provides you a set of classes to access TCP, UDP, SOCKS, Unix domain sockets, and more. The lib/net set of library modules provides handlers for a set of application-level protocols (currently FTP, HTTP, POP, SMTP, and telnet). By bringing the open-uri library into a program, the Kernel#open() method suddenly recognizes http:// and ftp:// URLs in the filename, and handles redirects automatically.

Memo: Use the open-uri library, and Kernel#open() will open URL (only http and ftp), automatically handling "404" errors and redirects, too.

Example:

You can list the images that are displayed on the Pragmatic Programmer home page in this way...

require 'net/http'
h = Net::HTTP.new(' www.pragmaticprogrammer.com', 80)
response = h.get('/index.html', nil)
if response.message == "OK"
puts response.body.scan(/<img src="(.*?)"/m).uniq
end

...or in this shorter, safer, smarter way

require 'open-uri'
open('http://www.pragmaticprogrammer.com') do |f|
puts f.read.scan(/<img src="(.*?)"/m).uniq
end

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

Errata I found: 0

My suggestions to the author : 0

Wednesday, November 22, 2006

[temp] Pages 123-4: Writing to files


Listen to this article.

Value: Medium

Level: Hard

Summary:
When you use puts and print to output, every object is converted to a string (note that nil is passed as "nil,", an array is passed as separate elements, a non-valid string is passed as object'class name and ID). To write binary data you can use IO#print() passing a string with the bytes. To get binary data into string you can use a literal, poke it in byte by byte, or use Array#pack(). You can use Array#<<() to append an object to an output IO stream. StringIO objects behave just like other I/O objects, but they read and write strings, not files.

Memo: None

Esample:

str1 = "\001\002\003" → "\001\002\003"
str2 = ""
str2 << 1 << 2 << 3 → "\001\002\003"
[ 1, 2, 3 ].pack("c*") → "\001\002\003"

require 'stringio'
ip = StringIO.new("now is\nthe time\nto learn\nRuby!")
op = StringIO.new("", "w")
ip.each_line do |line|
op.puts line.reverse
end
op.string → "\nsi won\n\nemit eht\n\nnrael ot\n!ybuR\n"

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

Errata I found: 0

My suggestions to the author: 0

Monday, November 20, 2006

[temp] Pages 122-3: Iterators for reading


Listen to this article.

Value: Medium

Level: Average

Summary:
You can use special iterators to read data from an IO stream, as IO#each_byte, IO#each_line, IO#foreach, IO#read, IO#readlines.

Memo:
Don’t forget that I/O is never certain in an uncertain world

Example:
IO.foreach("data.txt") {|line| puts line }
# read into string
str = IO.read("data.txt")
# read into an array
arr = IO.readlines("data.txt")

File.open("testfile") do |file|
file.each_byte {|ch| puts ch}
end

File.open("testfile") do |file|
file.each_line {|line| puts "#{line.dump}" }
end

Reported errata (): 0

Errata I found: 3

My suggestions to the author: 0

Saturday, November 18, 2006

[temp] Pages 120-1: Opening and closing files, Reading and writing files


Listen to this article.

Value: Medium

Level: Easy

Summary:
You can create a File object and read from it, write to it, or both. You can do it using File#new() or File#open(): the only difference between the two is that the latter can be associated with a block. If you use File#open() with an associated block Ruby will pass the file as a parameter to the block, and at the end will close the file. In case of exception Ruby will nevertheless close the file before raising it: if there's no block associated, or if you're using File#new(), you'll have to ensure to close the file as Ruby won't do it automatically.


Memo: Be sure your file will be closed even if an exception is raised.

Example:

Assuming we have data in data.txt we can read it in a standard way...

print File.new("data.txt", "r").readlines
# produces the same otuput of...
print File.open("data.txt", "r").readlines

... or in a "safe" way

File.open("data.txt", "r") do |a|
print a.readlines
end
# produces the same output of...
File.open("data.txt", "r") do |b|
while line = b.gets
print line
end

Finally we could have show.rb...

while line = gets
puts line
end

...and simply pass it one or more filenames in the command line

% ruby show.rb data.txt

Doubts: 0

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

Errata I found: 1
  • Typo on page 121: "w" instead of "we".

My suggestions to the author: 0

Thursday, November 16, 2006

Page 119: What is an IO object ?


Listen to this article.

Value: Medium

Level: Easy

Summary:
In Ruby you can manage Input/Output in two ways. The first is to use methods implemented in the module Kernel (as gets(), puts(), etc.) that typically do I/O to standard input and standard output. The second is to create an object of the IO class. Such an object is "a bidirectional channel between a Ruby program and some external resource": you'll read from it and/or write to it.

Memo: None

Example: None

Doubts: 0

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

Errata I found: 0

My suggestions to the author: 0

Tuesday, November 14, 2006

Pages 116-8: Including other files


Listen to this article.

Value: Medium

Level: Average

Summary:
As you split your code in multiple files, you can use two different methods to include it: load() or require(). The first reloads the file you need everytime you call it, the latter does it just once (usually).

Memo:
load() needs the file extension, while require() tries to add .rb to the filename.

Example:

This is the file included.rb

a = 1
def b
2
end

This is how require() works

a = "cat"
b = "dog"
require 'included'
puts a
puts b
b()
def b
puts 3
end
require 'included'
b()

This is how load() works

a = "cat"
b = "dog"
load 'included.rb'
puts a
puts b
b()
def b
puts 3
end
load 'included.rb'
b()

Doubts: 1
  • I was not able to understand the example for the load() method on page 118.
Reported errata (at 10/17/06 14:17:18 PDT): 0

Errata I found: 1
  • #2368: minor erratum.

My suggestions to the author: 6
  • Make clear that require() tries adding .rb to the filename, while load() doesn't.
  • Define what "shared binary libraries" are.
  • Make clear that also load() is an executable statements, as require() is.
  • Add "obviously" when stating that "local variables in a loaded or required file are not propagated to the scope that loads or requires them.". It's so obvious that could be misleading for a newbie.
  • Make clear that while local variables are not propagated, methods are.
  • Write a different example for the load() method. That one is way too contrived and impenetrable.

Sunday, November 12, 2006

Pages 115-6: Instance Variables in Mixins


Listen to this article.

Value: High

Level: Average

Summary:
If a "mixin’s instance variables [...] clash with those of the host class or with those of other mixins [your] program will go wrong in unexpected ways". The author suggests to "ensure that the [mixin's] instance variables have unique names to distinguish them from any other mixins in the system".

An alternative solution could be to "use a module-level hash, indexed by the current object ID, to store instance-specific data without using Ruby instance variables".

Finally the author talks about method lookup, explaining that Ruby looks first in the class of the object, then in the mixins last included, first searched), then in the superclasses (and, of course, their mixins).

Memo:
Give unique names to mixins' instance variables and methods.

Example:

Wrong Code

module SexuallyAppealing
attr_accessor :score
end
class PokerPlayer
include SexuallyAppealing
def initialize(name, surname, score)
@name = name
@surname = surname
@score = score
end
end
player1 = PokerPlayer.new("Adolfo", "Persichetti", 3200)
player1.score = 1
puts player1.inspect

Right code

module SexuallyAppealing
attr_accessor :sex_score
end
class PokerPlayer
include SexuallyAppealing
def initialize(name, surname, score)
@name = name
@surname = surname
@score = score
end
end
player1 = PokerPlayer.new("Adolfo", "Persichetti", 3200)
player1.sex_score = 1
puts player1.inspect

Doubts: 0

Reported Errata (at 10/17/06 14:17:18 PDT): 4
  • #2324 is an useful tip: in the example code there are method calls calling an instance variable. Those method calls could be mistakenly interpreted as local variables from a newbie: the reporter suggests to use an instance variable instead of that method calls.
  • #1938, #2814 regard the deprecated Object#id: just use Object#object_id instead.
  • #1937 is a typo.

Errata I found: 1
  • A typo on page 116: "Abmiguous" instead of "Ambiguous".

My suggestions to the author: 2
  • Give an example when stating that "mixin modules don't [usually] try to carry their own instance data around [but] use accessors to retrieve data from the client object".
  • Specify that "State", described as "a module-level hash" is a module constant.

Saturday, November 11, 2006

Hello World


Listen to this article.

In this blog I'll post from tomorrow, every two days, the progress of my climbing of Ruby with a Pickaxe, one page at a time.