Standard Library - Ruby Library Reference - Programming Ruby 1.9 & 2.0: The Pragmatic Programmers’ Guide (2013)

Programming Ruby 1.9 & 2.0: The Pragmatic Programmers’ Guide (2013)

Part 4. Ruby Crystallized

Chapter 28. Standard Library

The Ruby interpreter comes with a large number of classes, modules, and methods built in—they are available as part of the running program. When you need a facility that isn’t part of the built-in repertoire, you’ll often find it in a library that you can require into your program. Sometimes you’ll need to download one of these libraries (perhaps as a Ruby gem).

However, Ruby also ships as standard with a large number of libraries. Some of these are written in pure Ruby and will be available on all Ruby platforms. Others are Ruby extensions, and some of these will be present only if your system supports the resources that they need. All can be included into your Ruby program using require . And, unlike libraries you may find on the Internet, you can pretty much guarantee that all Ruby users will have these libraries already installed on their machines.

Ruby 1.9 has more than 100 standard libraries included in the distribution. For each of these libraries, this section shows a one- or a two-page summary. For each library, we give some introductory notes and typically an example or two of use. You won’t find detailed method descriptions here; for that, consult the library’s own documentation.

It’s all very well suggesting that you “consult the library’s own documentation,” but where can you find it? The answer is that it depends. Some libraries have already been documented using RDoc (see Chapter 19, Documenting Ruby). That means you can use the ri command to get the documentation.

If there’s no RDoc documentation available, the next place to look is the library. If you have a source distribution of Ruby, these library files are in the ext/ and lib/ subdirectories. If instead you have a binary-only installation, you can still find the source of pure-Ruby library modules (normally in the lib/ruby/1.9/ directory under your Ruby installation). Often, library source directories contain documentation that the author has not yet converted to RDoc format.

If you still can’t find documentation, turn to your search engine of choice. Many of the Ruby standard libraries are also hosted as external projects. The authors develop them stand-alone and then periodically integrate the code into the standard Ruby distribution. For example, if you want detailed information on the API for the YAML library, Try searching for yaml ruby—you’ll probably end up at http://www.yaml.org/YAML_for_ruby.html .

The next port of call is the ruby-talk mailing list. Ask a (polite) question there, and chances are that you’ll get a knowledgeable response within hours. See the tips in Section 28.4, Mailing Lists for pointers on how to subscribe.

And if you still can’t find documentation, you can always follow Obi Wan’s advice and do what we did when documenting Ruby—use the source. You’d be surprised at how easy it is to read the actual source of Ruby libraries and work out the details of usage.

There are some libraries that we don’t document, either because they are pretty low level or because we cover them extensively elsewhere in the book. These libraries include:

  • debug—the Ruby debugger, covered in Section 14.1,Ruby Debugger.
  • iconv—has been removed from Ruby 2.0. UseString#encode.«2.0»
  • mkmf—covered in the online guide to extending Ruby.
  • objspace—extensions to the ObjectSpace class designed to be used by the Ruby core team.
  • psych—an interface to libyaml. You’ll probably just use the YAML library.
  • racc—this is the runtime used by the Racc parser generator. If you need this library, you’ll also need the external Racc system.
  • rake—see Section 15.6,The Rake Build Tool.
  • rdoc—see Chapter 19,Documenting Ruby.
  • rubygems—covered in Section 15.5,RubyGems Integration and in Using RubyGems.

28.1 Library Changes in Ruby 1.9

These are the library changes in Ruby 1.9:

  • Much of the Complex and Rational libraries are now built in to the interpreter. However, requiring the external libraries adds some functionally. In the case of Rational, this functionality is minimal.
  • The CMath library has been added.
  • The Enumerator library is now built in.
  • The Fiber library has been added (it adds coroutine support to fibers).
  • The Fiddle library (an interface to libffi, which supports calling functions in shared librries) is documented as a replacement for DL.
  • ftools has been removed (and replaced by fileutils).
  • The Generator library has been removed (use fibers).
  • Notes on using irb from inside applications have been added.
  • jcode has been removed in favor of built-in encoding support.
  • The json library has been added.
  • The matrix library no longer requires that you include mathn.
  • The mutex library is now built in.
  • parsedate has been removed. The Date class handles most of its functionality.
  • readbytes has been removed. Class IO now supports the method directly.
  • A description of Ripper has been added.
  • A description of SecureRandom has been added.
  • The shell library has been omitted, because it seems more like a curiosity than something folks would use (and it’s broken under 1.9).
  • The soap library has been removed.
  • I’ve omitted the sync library. It is broken under 1.9, and the monitor library seems to be cleaner.
  • Win32API is now deprecated in favor of using the DL library.

Library Abbrev: Generate Sets of Unique Abbreviations

Given a set of strings, calculates the set of unambiguous abbreviations for those strings and returns a hash where the keys are all the possible abbreviations and the values are the full strings. Thus, given input of “car” and “cone,” the keys pointing to “car” would be “ca” and “car,” and those pointing to “cone” would be “co,” “con,” and “cone.”

An optional pattern or a string may be specified—only those input strings matching the pattern, or beginning with the string, are considered for inclusion in the output hash.

Including the Abbrev library also adds an abbrev method to class Array.

  • Shows the abbreviation set of some words:

require 'abbrev'

Abbrev::abbrev(%w{ruby rune}) # => {"ruby"=>"ruby", "rub"=>"ruby",

# .. "rune"=>"rune", "run"=>"rune"}

  • A trivial command loop using abbreviations:

require 'abbrev'

COMMANDS = %w{ sample send start status stop }.abbrev

while line = gets

line = line.chomp

case COMMANDS[line]

when "sample" then # ...

when "send" then # ...

# ...

else

STDERR.puts "Unknown command: #{line}"

end

end

Library Base64: Base64 Conversion Functions

Performs encoding and decoding of binary data using a Base64 representation. This allows you to represent any binary data in purely printable characters. The encoding is specified in RFC 2045 and RFC 4648.[125]

  • Encodes and decodes strings. Note the newlines inserted into the Base64 string.

require 'base64'

str = "Now is the time for all good coders\nto learn Ruby"

converted = Base64.encode64(str)

puts converted

puts Base64.decode64(converted)

  • Produces:

Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g

UnVieQ==

Now is the time for all good coders

to learn Ruby

  • Now uses RFC 4648 variants:

require 'base64'

str = "Now is the time for all good coders\nto learn Ruby"

converted = Base64.strict_encode64(str)

puts converted

puts Base64.strict_decode64(converted)

  • Produces:

Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4gUnVieQ==

Now is the time for all good coders

to learn Ruby

Library Benchmark: Time Code Execution

Allows code execution to be timed and the results tabulated. The Benchmark module is easier to use if you include it in your top-level environment.

  • Profile
  • Compares the costs of four kinds of method dispatch:

require 'benchmark'

include Benchmark

string = "Stormy Weather"

m = string.method(:length)

bm(6) do |x|

x.report("direct") { 100_000.times { string.length } }

x.report("call") { 100_000.times { m.call } }

x.report("send") { 100_000.times { string.send(:length) } }

x.report("eval") { 100_000.times { eval "string.length" } }

end

  • Produces:

user system total real

direct 0.010000 0.000000 0.010000 ( 0.012705)

call 0.020000 0.000000 0.020000 ( 0.022576)

send 0.020000 0.000000 0.020000 ( 0.020664)

eval 1.220000 0.000000 1.220000 ( 1.224656)

  • Which is better: reading all of a dictionary and splitting it or splitting it line by line? Use bmbm to run a rehearsal before doing the timing:

require 'benchmark'

include Benchmark

bmbm(6) do |x|

x.report("all") do

str = File.read("/usr/share/dict/words")

words = str.scan(/[-\w']+/)

end

x.report("lines") do

words = []

File.foreach("/usr/share/dict/words") do |line|

words << line.chomp

end

end

end

  • Produces:

Rehearsal ------------------------------------------

all 0.200000 0.010000 0.210000 ( 0.218034)

lines 0.150000 0.020000 0.170000 ( 0.165469)

--------------------------------- total: 0.380000sec

user system total real

all 0.180000 0.010000 0.190000 ( 0.185983)

lines 0.290000 0.010000 0.300000 ( 0.302548)

Library BigDecimal: Large-Precision Decimal Numbers

Ruby’s standard Bignum class supports integers with large numbers of digits. The BigDecimal class supports decimal numbers with large numbers of decimal places. The standard library supports all the normal arithmetic operations. BigDecimal also comes with some extension libraries.

bigdecimal/ludcmp

Performs an LU decomposition of a matrix.

bigdecimal/math

Provides the transcendental functions sqrt, sin, cos, atan, exp, and log, along with functions for computing PI and E. All functions take an arbitrary precision argument.

bigdecimal/jacobian

Constructs the Jacobian (a matrix enumerating the partial derivatives) of a given function. Not dependent on BigDecimal.

bigdecimal/newton

Solves the roots of nonlinear function using Newton’s method. Not dependent on BigDecimal.

bigdecimal/nlsolve

Wraps the bigdecimal/newton library for equations of big decimals.

You can find English-language documentation in the file ext/bigdecimal/bigdecimal_en.html in the Ruby source distribution.

# Calculate the area of a circle using BigDecimal numbers

require 'bigdecimal'

require 'bigdecimal/math'

include BigMath

pi = BigMath::PI(20) # 20 is the number of decimal digits

radius = BigDecimal("2.14156987652974674392")

area = pi * radius**2

area.to_s # => "0.144083540446856044176720033806679561688599846410

# .. 445032583215824758780405545861780909930190528E2"

# The same with regular floats

radius = 2.14156987652974674392

Math::PI * radius**2 # => 14.408354044685602

Library CGI: CGI Programming Support

The CGI class provides support for programs used as Common Gateway Interface (CGI) scripts in a web server. CGI objects are initialized with data from the environment and from the HTTP request, and they provide convenient accessors to form data and cookies. They can also manage sessions using a variety of storage mechanisms. Class CGI also provides basic facilities for HTML generation and class methods to escape and unescape requests and HTML.

  • CGI::Session
  • Escapes and unescapes special characters in URLs and HTML. Numeric entities less than 256 will be encoded based on the encoding of the input string.Other numeric entities will be left unchanged.

require 'cgi'

CGI.escape('c:\My Files') # => c%3A%5CMy+Files

CGI.unescape('c%3a%5cMy+Files') # => c:\My Files

CGI::escapeHTML('"a"<b & c') # => "a"<b & c

CGI.unescapeHTML('"a"<=>b') # => "a"<=>b

CGI.unescapeHTML('AA') # => AA

str = '2πr'

str.force_encoding("utf-8")

CGI.unescapeHTML(str) # => 2πr

  • Access information from the incoming request:

require 'cgi'

c = CGI.new

c.auth_type # => "basic"

c.user_agent # => "Mozscape Explorari V5.6"

  • Access form fields from an incoming request. Assume that the following script, installed as test.cgi, was linked to using http://mydomain.com/test.cgi?fred=10&barney=cat:

require 'cgi'

c = CGI.new

c['fred'] # => "10"

c.keys # => ["fred", "barney"]

c.params # => {"fred"=>["10"], "barney"=>["cat"]}

  • If a form contains multiple fields with the same name, the corresponding values will be returned to the script as an array. The [] accessor returns just the first of these—index the result of the params method to get them all.

In this example, assume the form has three fields called “name”:

require 'cgi'

c = CGI.new

c['name'] # => "fred"

c.params['name'] # => ["fred", "wilma", "barney"]

c.keys # => ["name"]

c.params # => {"name"=>["fred", "wilma", "barney"]}

  • Sends a response to the browser. (Not many folks use this form of HTML generation–use one of the templating libraries described in Section 20.3,Templating Systems.

require 'cgi'

cgi = CGI.new("html5")

cgi.http_header("type" => "text/html", "expires" => Time.now + 30)

cgi.out do

cgi.html do

cgi.head{ cgi.title{"Hello World!"} } +

cgi.body do

cgi.pre do

CGI::escapeHTML(

"params: " + cgi.params.inspect + "\n" +

"cookies: " + cgi.cookies.inspect + "\n")

end

end

end

end

  • Stores a cookie in the client browser:

require 'cgi'

cgi = CGI.new("html5")

cookie = CGI::Cookie.new('name' => 'mycookie',

'value' => 'chocolate chip',

'expires' => Time.now + 3600)

cgi.out('cookie' => cookie) do

cgi.head + cgi.body { "Cookie stored" }

end

  • Retrieves a previously stored cookie:

require 'cgi'

cgi = CGI.new("html5")

cookie = cgi.cookies['mycookie']

cgi.out('cookie' => cookie) do

cgi.head + cgi.body { "Flavor: " + cookie[0] }

end

Library CGI::Session: CGI Sessions

A CGI::Session maintains a persistent state for web users in a CGI environment. Sessions may be memory resident or may be stored on disk. See the discussion in Sessions for details.

  • CGI

sl_cgi_session/session.rb

# Store the timestamp of last access, along with the access count

# using a session object

require 'cgi'

require 'cgi/session'

cgi = CGI.new("html3")

sess = CGI::Session.new(cgi,

"session_key" => "rubyweb",

"prefix" => "web-session.")

if sess['lastaccess']

msg = "<p>You were last here #{sess['lastaccess']}.</p>"

else

msg = "<p>Looks like you haven't been here for a while</p>"

end

count = (sess["accesscount"] || 0).to_i

count += 1

msg << "<p>Number of visits: #{count}</p>"

sess["accesscount"] = count

sess["lastaccess"] = Time.now.to_s

sess.close

cgi.out {

cgi.html {

cgi.body {

msg

}

}

}

Library CMath: Complex Transcendental Functions

As of Ruby 1.9, the Complex class is built in to the interpreter. There is no need to require the complex library to create and manipulate complex numbers. However, if you want the transcendental functions defined by Math to work with complex numbers, you must also require the cmath library. The functions affected are as follows: acosh , acos , asinh , asin , atan2 , atanh , atan , cosh , cos , exp , log10 , log , sinh , sin , sqrt , tanh , and tan .

The complex library makes these complex functions the default (so, if you require ’complex’, you can use Math::sin and not CMath::sin).

require 'cmath'

point = Complex(2, 3)

CMath::sin(point) # => (9.15449914691143-4.168906959966565i)

CMath::cos(point) # => (-4.189625690968807-9.109227893755337i)

Library Complex: Complex Numbers

Loads the cmath library, which defines the transcendental functions for complex numbers. It then arranges things so that these complex-aware functions are the ones invoked when you use Math::. The net effect is that, after requiring complex, you can use functions such as Math::sin on any numeric value, including complex numbers.

  • Using transcendental numbers with complex arguments will, by default, cause an error:

point = Complex(2, 3)

Math::sin(point)

  • Produces:

from prog.rb:2:in `sin'

from prog.rb:2:in `<main>'

prog.rb:2:in `to_f': can't convert 2+3i into Float (RangeError)

  • However...

require 'complex'

point = Complex(2, 3)

Math::sin(point) # => (9.15449914691143-4.168906959966565i)

Library Continuation: Continuations

Continuation objects are generated by the Object#callcc method, which becomes available only when the continuation library is loaded. They hold a return address and execution context, allowing a nonlocal return to the end of the callcc block from anywhere within a program. Continuations are somewhat analogous to a structured version of C’s setjmp/longjmp (although they contain more state, so you may consider them closer to threads). This (somewhat contrived) example allows the inner loop to abandon processing early.

  • Does a nonlocal exit when a condition is met:

require 'continuation'

callcc do |cont|

for i in 0..4

print "\n#{i}: "

for j in i*5...(i+1)*5

cont.call() if j == 7

printf "%3d", j

end

end

end

print "\n"

  • Produces:

0: 0 1 2 3 4

1: 5 6

  • The call stack for methods is preserved in continuations:

require 'continuation'

def strange

callcc {|continuation| return continuation}

print "Back in method, "

end

print "Before method. "

continuation = strange()

print "After method. "

continuation.call if continuation

  • Produces:

Before method. After method. Back in method, After method.

Library coverage: Experimental Code Coverage Analysis

The coverage module counts the number of times each line of Ruby code is executed in one or more source files and provides a summary as a hash. The keys of the hash are the names of files that were analyzed, and the values are each an array containing counts (on a per-line basis).

Here’s a simple implementation of the Fizz Buzz program:

sl_coverage/fizzbuzz.rb

1.upto(100).with_object('') do |i, x|

if i % 3 == 0

x += 'Fizz'

end

if i % 5 == 0

x += 'Buzz'

end

if x.empty?

puts i

else

puts x

end

end

And here’s a program that loads and runs that program, using the coverage library to report on execution counts. (Note that it discards the output of the FizzBuzz program, simply to save space on this page.)

require 'coverage'

Coverage.start

STDOUT.reopen("/dev/null")

require_relative 'fizzbuzz.rb'

Coverage.result.each do |file_name, counts|

File.readlines(file_name).each.with_index do |code_line, line_number|

count = counts[line_number] || "--"

STDERR.printf "%3s: %s", count, code_line

end

end

Produces:

1: 1.upto(100).with_object('') do |i, x|

100: if i % 3 == 0

33: x += 'Fizz'

--: end

100: if i % 5 == 0

20: x += 'Buzz'

--: end

100: if x.empty?

53: puts i

--: else

47: puts x

--: end

--: end

Library CSV: Comma-Separated Values

Comma-separated data files are often used to transfer tabular information (and are a lingua franca for importing and exporting spreadsheet and database information). As of Ruby 1.9, the old library has been replaced by James Edward Gray II’s FasterCSV version. It has a few incompatibilities with the original. In particular, CSV.open now works like File.open , not File.foreach , and options are passed as a hash and not positional parameters.

Ruby’s CSV library deals with arrays (corresponding to the rows in the CSV file) and strings (corresponding to the elements in a row). If an element in a row is missing, it will be represented as a nil in Ruby.

The files used in these examples are as follows:

sl_csv/csvfile

12,eggs,2.89,

2,"shirt, blue",21.45,special

1,"""Hello Kitty"" bag",13.99

sl_csv/csvfile_hdr

Count,Description,Price

12,eggs,2.89,

2,"shirt, blue",21.45,special

1,"""Hello Kitty"" bag",13.99

  • Reads a file containing CSV data and processes line by line:

require 'csv'

CSV.foreach("csvfile") do |row|

qty = row[0].to_i

price = row[2].to_f

printf "%20s: $%5.2f %s\n", row[1], qty*price, row[3] || " ---"

end

  • Produces:

eggs: $34.68 ---

shirt, blue: $42.90 special

"Hello Kitty" bag: $13.99 ---

  • Processes a CSV file that contains a header line. Automatically converts fields that look like numbers.

require 'csv'

total_cost = 0

CSV.foreach("csvfile_hdr", headers: true, converters: :numeric) do |data|

total_cost += data["Count"] * data["Price"]

end

puts "Total cost is #{total_cost}"

  • Produces:

Total cost is 91.57

  • Writes CSV data to an existing open stream (STDOUT in this case). Uses | as the column separator.

require 'csv'

CSV(STDOUT, col_sep: "|") do |csv|

csv << [ 1, "line 1", 27 ]

csv << [ 2, nil, 123 ]

csv << [ 3, "|bar|", 32.5]

end

  • Produces:

1|line 1|27

2||123

3|"|bar|"|32.5

  • Accesses a CSV file as a two-dimensional table:

require 'csv'

table = CSV.read("csvfile_hdr",

headers: true,

header_converters: :symbol)

puts "Row count = #{table.count}"

puts "First row = #{table[0].fields}"

puts "Count of eggs = #{table[0][:count]}"

table << [99, "red balloons", 1.23]

table[:in_stock] = [10, 5, 10, 10]

puts "\nAfter adding a row and a column, the new table is:"

puts table

  • Produces:

Row count = 3

First row = ["12", "eggs", "2.89", nil]

Count of eggs = 12

After adding a row and a column, the new table is:

count,description,price,,in_stock

12,eggs,2.89,,10

2,"shirt, blue",21.45,special,5

1,"""Hello Kitty"" bag",13.99,10

99,red balloons,1.23,,10

Library Curses: CRT Screen Handling

curses or ncurses installed in target environment

The Curses library is a thin wrapper around the C curses or ncurses libraries, giving applications a device-independent way to draw on consoles and other terminal-like devices. As a nod toward object-orientation, curses windows and mouse events are represented as Ruby objects. Otherwise, the standard curses calls and constants are simply defined in the Curses module.

sl_curses/pong_paddle.rb

# Draw the paddle for game of 'pong' that moves in response to up and down keys

require 'curses'

include Curses

class Paddle

HEIGHT = 4

PADDLE = " \n" + "|\n"*HEIGHT + " "

def initialize

@top = (Curses::lines - HEIGHT)/2

draw

end

def up

@top -= 1 if @top > 1

end

def down

@top += 1 if (@top + HEIGHT + 1) < lines

end

def draw

setpos(@top-1, 0)

addstr(PADDLE)

refresh

end

end

init_screen

begin

cbreak

noecho

stdscr.keypad(true)

paddle = Paddle.new

loop do

case ch = getch

when "Q", "q" thenbreak

when Key::UP, 'U', 'u' then paddle.up

when Key::DOWN, 'D', 'd' then paddle.down

else

beep

end

paddle.draw

end

ensure

close_screen

end

Library Date/DateTime: Date and Time Manipulation

The date library implements classes Date and DateTime, which provide a comprehensive set of facilities for storing, manipulating, and converting dates with or without time components. The classes can represent and manipulate civil, ordinal, commercial, Julian, and standard dates, starting January 1, 4713 BCE. The DateTime class extends Date with hours, minutes, seconds, and fractional seconds, and it provides some support for time zones. The classes also provide support for parsing and formatting date and datetime strings. The classes have a rich interface—consult the ri documentation for details. The introductory notes in the file lib/date.rb in the Ruby source tree are also well worth reading.

  • Experiment with various representations:

require 'date'

d = Date.new(2000, 3, 31)

[d.year, d.yday, d.wday] # => [2000, 91, 5]

[d.month, d.mday] # => [3, 31]

[d.cwyear, d.cweek, d.cwday] # => [2000, 13, 5]

[d.jd, d.mjd] # => [2451635, 51634]

d1 = Date.commercial(2000, 13, 7)

d1.to_s # => "2000-04-02"

[d1.cwday, d1.wday] # => [7, 0]

  • Essential information about Christmas:

require 'date'

now = DateTime.now

year = now.year

year += 1 if now.month == 12 && now.day > 25

xmas = DateTime.new(year, 12, 25)

diff = xmas - now

puts "It's #{diff.to_i} days to Christmas"

puts "Christmas #{year} falls on a #{xmas.strftime('%A')}"

  • Produces:

It's 211 days to Christmas

Christmas 2013 falls on a Wednesday

Library DBM: Interface to DBM Databases

a DBM library is installed in target environment

DBM files implement simple, hashlike persistent stores. Many DBM implementations exist: the Ruby library can be configured to use one of the DBM libraries db, dbm (ndbm), gdbm, and qdbm. The interface to DBM files is similar to class Hash, except that DBM keys and values will be strings. This can cause confusion, as the conversion to a string is performed silently when the data is written. The DBM library is a wrapper around the lower-level access method. For true low-level access, see also the GDBM and SDBM libraries.

  • gdbm
  • sdbm

The following creates a simple DBM file and then reopens it read-only and reads some data. Note the conversion of a date object to its string form.

sl_dbm/dbm1.rb

require 'dbm'

require 'date'

DBM.open("data.dbm") do |dbm|

dbm['name'] = "Walter Wombat"

dbm['dob'] = Date.new(1997, 12,25)

end

DBM.open("data.dbm", nil, DBM::READER) do |dbm|

p dbm.keys

p dbm['dob']

p dbm['dob'].class

end

Produces:

["name", "dob"]

"1997-12-25"

String

Library Delegator: Delegate Calls to Other Object

Object delegation is a way of composing objects—extending an object with the capabilities of another—at runtime. The Ruby Delegator class implements a simple but powerful delegation scheme, where requests are automatically forwarded from a master class to delegates or their ancestors and where the delegate can be changed at runtime with a single method call.

  • Forwardable
  • For simple cases where the class of the delegate is fixed, make the master class a subclass of DelegateClass, passing the name of the class to be delegated as a parameter. In the master class’s initialize method, pass the object to be delegated to the superclass.

require 'delegate'

class Words < DelegateClass(Array)

def initialize(list = "/usr/share/dict/words")

words = File.read(list).split

super(words)

end

end

words = Words.new

words[9999] # => "anticontagionist"

words.size # => 235886

words.grep(/matz/) # => ["matzo", "matzoon", "matzos", "matzoth"]

  • Use SimpleDelegator to delegate to a particular object (which can be changed):

require 'delegate'

words = File.read("/usr/share/dict/words").split

names = File.read("/usr/share/dict/propernames").split

stats = SimpleDelegator.new(words)

stats.size # => 235886

stats[226] # => "abidingly"

stats.__setobj__(names)

stats.size # => 1308

stats[226] # => "Deirdre"

Library Digest: MD5, RIPEMD-160 SHA1, and SHA2 Digests

The Digest module is the home for a number of classes that implement message digest algorithms: MD5, RIPEMD-160, SHA1, and SHA2 (256, 384, and 512 bit). The interface to all these classes is identical.

  • You can create a binary or hex digest for a given string by calling the class method digest or hexdigest .
  • You can also create an object (optionally passing in an initial string) and determine the object’s hash by calling the digest or hexdigest instance methods. You can then append to the string using the update method and then recover an updated hash value.
  • Calculates some MD5 and SHA1 hashes:

require 'digest/md5'

require 'digest/sha1'

for hash_class in [ Digest::MD5, Digest::SHA1 ]

puts "Using #{hash_class.name}"

# Calculate directly

puts hash_class.hexdigest("hello world")

# Or by accumulating

digest = hash_class.new

digest << "hello"

digest << " "

digest << "world"

puts digest.hexdigest

puts digest.base64digest # new in 1.9.2

puts

end

  • Produces:

Using Digest::MD5

5eb63bbbe01eeed093cb22bb8f5acdc3

5eb63bbbe01eeed093cb22bb8f5acdc3

XrY7u+Ae7tCTyyK7j1rNww==

Using Digest::SHA1

2aae6c35c94fcfb415dbe95f408b9ce91ee846ed

2aae6c35c94fcfb415dbe95f408b9ce91ee846ed

Kq5sNclPz7QV2+lfQIuc6R7oRu0=

Library dRuby: Distributed Ruby Objects (drb)

dRuby allows Ruby objects to be distributed across a network connection. Although expressed in terms of clients and servers, once the initial connection is established, the protocol is effectively symmetrical: either side can invoke methods in objects on the other side. Normally, objects passed and returned by remote calls are passed by value; including the DRbUndumped module in an object forces it to be passed by reference (useful when implementing callbacks).

  • Rinda
  • XMLRPC
  • This server program is observable —it notifies all registered listeners of changes to a count value:

sl_drb/drb_server1.rb

require 'drb'

require 'drb/observer'

class Counter

include DRb::DRbObservable

def run

5.times do |count|

changed

notify_observers(count)

end

end

end

counter = Counter.new

DRb.start_service('druby://localhost:9001', counter)

DRb.thread.join

  • This client program interacts with the server, registering a listener object to receive callbacks before invoking the server’s run method:

sl_drb/drb_client1.rb

require 'drb'

class Listener

include DRbUndumped

def update(value)

puts value

end

end

DRb.start_service

counter = DRbObject.new(nil, "druby://localhost:9001")

listener = Listener.new

counter.add_observer(listener)

counter.run

Library English: English Names for Global Symbols

Includes the English library file in a Ruby script, and you can reference the global variables such as $_ using less-cryptic names, listed in the following table. Prior to Ruby 1.9, the name $PROGRAM_NAME was declared using English. It is now predefined in the Ruby interpreter.

\toprule $*

$ARGV

$_

$LAST_READ_LINE

$?

$CHILD_STATUS

$"

$LOADED_FEATURES

$<

$DEFAULT_INPUT

$&

$MATCH

$>

$DEFAULT_OUTPUT

$.

$NR

$!

$ERROR_INFO

$,

$OFS

$@

$ERROR_POSITION

$\

$ORS

$;

$FIELD_SEPARATOR

$,

$OUTPUT_FIELD_SEPARATOR

$;

$FS

$\

$OUTPUT_RECORD_SEPARATOR

$=

$IGNORECASE

$$

$PID

$.

$INPUT_LINE_NUMBER

$’

$POSTMATCH

$/

$INPUT_RECORD_SEPARATOR

$‘

$PREMATCH

$~

$LAST_MATCH_INFO

$$

$PROCESS_ID

$+

$LAST_PAREN_MATCH

$/

$RS

The following code shows some regular variable names along with their English counterparts.

require 'English'

$OUTPUT_FIELD_SEPARATOR = ' -- '

"waterbuffalo" =~ /buff/

print $., $INPUT_LINE_NUMBER, "\n"

print $', $POSTMATCH, "\n"

print $$, $PID

Produces:

0 -- 0 --

alo -- alo --

24658 -- 24658

Library erb: Lightweight Templating for HTML

ERb is a lightweight templating system, allowing you to intermix Ruby code and plain text. This is sometimes a convenient way to create HTML documents but also is usable in other plain-text situations. See Section 20.3, Templating Systems for other templating solutions.

ERB breaks its input text into chunks of regular text and program fragments. It then builds a Ruby program that, when run, outputs the result text and executes the program fragments. Program fragments are enclosed between <% and %> markers. The exact interpretation of these fragments depends on the character following the opening <%:

Table 26. Directives for ERB

Sequence

Action

<% ruby code %>

Inserts the given Ruby code at this point in the generated program. If it outputs anything, include this output in the result.

<%= ruby expression %>

Evaluate expression and insert its value in the output of the generated program.

<%# ... %>

Comment (ignored).

<%% and %%>

Replaced in the output by <% and%> respectively.

The following code uses <%…%> blocks to execute a Ruby loop, and <%=…%> to substitute a value into the output.

require 'erb'

input = %{<% high.downto(low) do |n| # set high, low externally %>

<%= n %> green bottles, hanging on the wall

<%= n %> green bottles, hanging on the wall

And if one green bottle should accidentally fall

There'd be <%= n-1 %> green bottles, hanging on the wall

<% end %>}

high,low = 10, 8

erb = ERB.new(input)

erb.run(binding)

Produces:

10 green bottles, hanging on the wall

10 green bottles, hanging on the wall

And if one green bottle should accidentally fall

There'd be 9 green bottles, hanging on the wall

. . .

An optional second parameter to ERB.new sets the safe level for evaluating expressions. If nil, expressions are evaluated in the current thread; otherwise, a new thread is created, and its $SAFE level is set to the parameter value.

The optional third parameter to ERB.new allows some control of the interpretation of the input and of the way whitespace is added to the output. If the third parameter is a string and that string contains a percent sign, then ERb treats lines starting with a percent sign specially. Lines starting with a single percent sign are treated as if they were enclosed in <%...%>. Lines starting with a double percent sign are copied to the output with a single leading percent sign.

require 'erb'

str = %{\

% 2.times do |i|

This is line <%= i %>

%end

%%%done}

ERB.new(str, 0, '%').run

Produces:

This is line 0

This is line 1

%%done

If the third parameter contains the string <>, then a newline will not be written if an input line starts with an ERB directive and ends with %>. If the trim parameter contains >>, then a newline will not be written if an input line ends %>.

require 'erb'

str1 = %{\

* <%= "cat" %>

<%= "dog" %>

}

ERB.new(str1, 0, ">").run

ERB.new(str1, 0, "<>").run

Produces:

* catdog* cat

dog

The erb library also defines the helper module ERB::Util that contains two methods: html_escape (aliased as h ) and url_encode (aliased as u ). These are equivalent to the CGI methods escapeHTML and escape , respectively (except escape encodes spaces as plus signs, and url_encode uses %20).

require 'erb'

include ERB::Util

str1 = %{\

h(a) = <%= h(a) %>

u(a) = <%= u(a) %>

}

a = "< a & b >"

ERB.new(str1).run(binding)

Produces:

h(a) = < a & b >

u(a) = %3C%20a%20%26%20b%20%3E

You may find the command-line utility erb is supplied with your Ruby distribution. This allows you to run erb substitutions on an input file; see erb --help for details.

Library Etc: Access User and Group Information in /etc/passwd

Unix or Cygwin

The Etc module provides a number of methods for querying the passwd and group facilities on Unix systems.

  • Finds out information about the currently logged-in user:

require 'etc'

name = Etc.getlogin

info = Etc.getpwnam(name)

info.name # => "dave"

info.uid # => 501

info.dir # => "/Users/dave"

info.shell # => "/bin/zsh"

group = Etc.getgrgid(info.gid)

group.name # => "staff"

  • Returns the names of users on the system used to create this book:

require 'etc'

users = []

Etc.passwd {|passwd| users << passwd.name }

users[1,5].join(", ") # => "_appleevents, _appowner, _appserver, _ard,

# .. _assetcache"

  • Returns the IDs of groups on the system used to create this book:

require 'etc'

ids = []

Etc.group {|entry| ids << entry.gid }

ids[1,5].join(", ") # => "55, 87, 81, 79, 33"

Library expect: Expect Method for IO Objects

The expect library adds the method expect to all IO objects. This allows you to write code that waits for a particular string or pattern to be available from the I/O stream. The expect method is particularly useful with pty objects (see the Pty library) and with network connections to remote servers, where it can be used to coordinate the use of external interactive processes.

If the global variable $expect_verbose is true, the expect method writes all characters read from the I/O stream to STDOUT.

  • pty
  • Connects to the local FTP server, logs in, and prints out the name of the user’s directory. (Note that it would be a lot easier to do this using the net/ftp library.)

# This code might be specific to the particular ftp daemon.

require 'expect'

require 'socket'

$expect_verbose = true

socket = TCPSocket.new('localhost', 'ftp')

socket.expect("ready")

socket.puts("user testuser")

socket.expect("331 User testuser accepted, provide password.")

socket.puts("pass wibble")

socket.expect("logged in.\r\n")

socket.puts("pwd")

puts(socket.gets)

socket.puts "quit"

  • Produces:

220 ::1 FTP server (tnftpd 20100324+GSSAPI) ready.

331 User testuser accepted, provide password.

230 User testuser logged in.

257 "/Users/testuser" is the current directory.

Library Fcntl: Symbolic Names for IO#fcntl Commands

The Fcntl module provides symbolic names for each of the host system’s available fcntl(2) constants (defined in fcntl.h). That is, if the host system has a constant named F_GETLK defined in fcntl.h, then the Fcntl module will have a corresponding constant Fcntl::F_GETLK with the same value as the header file’s #define.

  • Different operating system will have different Fcntl constants available. The value associated with a constant of a given name may also differ across platforms. Here are the values on our Mac OS X system:

require 'fcntl'

Fcntl.constants.sort.each do |name|

printf "%10s: 0x%06x\n", name, Fcntl.const_get(name)

end

  • Produces:

FD_CLOEXEC: 0x000001

F_DUPFD: 0x000000

F_GETFD: 0x000001

F_GETFL: 0x000003

F_GETLK: 0x000007

F_RDLCK: 0x000001

F_SETFD: 0x000002

F_SETFL: 0x000004

F_SETLK: 0x000008

F_SETLKW: 0x000009

F_UNLCK: 0x000002

F_WRLCK: 0x000003

O_ACCMODE: 0x000003

O_CREAT: 0x000200

O_EXCL: 0x000800

O_NDELAY: 0x000004

O_NOCTTY: 0x020000

O_NONBLOCK: 0x000004

O_RDONLY: 0x000000

O_RDWR: 0x000002

O_TRUNC: 0x000400

O_WRONLY: 0x000001

Library Fiber: Coroutines Using Fibers

The Fiber class that is built into Ruby provides a generator-like capability—fibers may be created and resumed from some controlling program. If you want to extend the Fiber class to provide full, symmetrical coroutines, you need first to require the fiber library. This adds two instance methods, transfer and alive? , to Fiber objects and adds the singleton method current to the Fiber class.

  • It is difficult to come up with a meaningful, concise example of symmetric coroutines that can’t more easily be coded with asymetric (plain old) fibers. So, here’s an artificial example:

require 'fiber'

# take items two at a time off a queue, calling the producer

# if not enough are available

consumer = Fiber.new do |producer, queue|

5.times do

while queue.size < 2

queue = producer.transfer(consumer, queue)

end

puts "Consume #{queue.shift} and #{queue.shift}"

end

end

# add items three at a time to the queue

producer = Fiber.new do |consumer, queue|

value = 1

loop do

puts "Producing more stuff"

3.times { queue << value; value += 1}

puts "Queue size is #{queue.size}"

consumer.transfer queue

end

end

consumer.transfer(producer, [])

  • Produces:

Producing more stuff

Queue size is 3

Consume 1 and 2

Producing more stuff

Queue size is 4

Consume 3 and 4

Consume 5 and 6

Producing more stuff

Queue size is 3

Consume 7 and 8

Producing more stuff

Queue size is 4

Consume 9 and 10

Library Fiddle: Access Dynamically Loaded Libraries (.dll and .so)

The Fiddle module is a wrapper around libffi, a library that provides access to shared libraries. On Windows boxes, it can be used to interface with functions in DLLs. Under Unix it can load shared libraries. Because Ruby does not have typed method parameters or return values, you must define the types expected by the methods you call by specifying their signatures.

  • Here’s a trivial C program that we’ll build as a shared library:

sl_fiddle/lib.c

#include <stdio.h>

int print_msg(char *text, int number) {

int count = printf("Text: %s (%d)\n", text, number);

fflush(stdout);

return count;

}

  • Generates a proxy to access the print_msg method in the shared library. The way this book is built, the shared library is in the same directory as the Ruby code; this directory must be added to the directories searched when looking for dynamic objects. You can do this by setting the DYLD_LIBRARY_PATH environment variable.

require 'fiddle'

include Fiddle

lib = Fiddle.dlopen("lib.so")

print_msg = Fiddle::Function.new(lib['print_msg'], # entry point

[TYPE_VOIDP, TYPE_INT], # parameter types

TYPE_INT) # return type

msg_size = print_msg.call("Answer", 42)

puts "Just wrote #{msg_size} bytes"

  • Produces:

Text: Answer (42)

Just wrote 18 bytes

Library FileUtils: File and Directory Manipulation

FileUtils is a collection of methods for manipulating files and directories. Although generally applicable, the model is particularly useful when writing installation scripts and Rake tasks.

Many methods take a src parameter and a dest parameter. If dest is a directory, src may be a single filename or an array of filenames. For example, the following copies the files a, b, and c to /tmp:

cp( %w{ a b c }, "/tmp")

Most functions take a set of options. These may be zero or more of the following:

Option

Meaning

:verbose

Traces execution of each function (by default to STDERR, although this can be overridden by setting the class variable @fileutils_output.

:noop

Does not perform the action of the function (useful for testing scripts).

:force

Overrides some default conservative behavior of the method (for example, overwriting an existing file).

:preserve

Attempts to preserve atime, mtime, and mode information from src in dest. (Setuid and setgid flags are always cleared.)

For maximum portability, use forward slashes to separate the directory components of filenames, even on Windows.

FileUtils contains three submodules that duplicate the top-level methods but that have different default options: module FileUtils::Verbose sets the verbose option, module FileUtils::NoWrite sets noop, and FileUtils::DryRun sets verbose and noop.

  • un

require 'fileutils'

include FileUtils::Verbose

cd("/tmp") do

cp("/etc/passwd", "tmp_passwd")

chmod(0666, "tmp_passwd")

cp_r("/usr/include/net/", "headers")

rm("tmp_passwd") # Tidy up

rm_rf("headers")

end

Produces:

cd /tmp

cp /etc/passwd tmp_passwd

chmod 666 tmp_passwd

cp -r /usr/include/net/ headers

rm tmp_passwd

rm -rf headers

cd -

Library Find: Traverse Directory Trees

The Find module supports the top-down traversal of a set of file paths, given as arguments to the find method. If an argument is a file, its name is passed to the block associated with the call. If it’s a directory, then its name and the name of all its files and subdirectories will be passed in. If no block is associated with the call, an Enumerator is returned.

Within the block, the method prune may be called, which skips the current file or directory, restarting the loop with the next directory. If the current file is a directory, that directory will not be recursively entered. In the following example, we don’t list the contents of the local Subversion cache directories:

require 'find'

Find.find("/etc/passwd", "code/ducktyping") do |f|

type = case

when File.file?(f) then "File: "

when File.directory?(f) then "Dir: "

else "?"

end

puts "#{type} #{f}"

Find.prune if f =~ /.svn/

end

Produces:

File: /etc/passwd

Dir: code/ducktyping

Dir: code/ducktyping/.svn

File: code/ducktyping/addcust.rb

File: code/ducktyping/roman3.rb

File: code/ducktyping/testaddcust1.rb

File: code/ducktyping/testaddcust2.rb

File: code/ducktyping/testaddcust3.rb

Library Forwardable: Object Delegation

Forwardable provides a mechanism to allow classes to delegate named method calls to other objects.

  • Delegator
  • This simple symbol table uses a hash, exposing a subset of the hash’s methods:

require 'forwardable'

class SymbolTable

extend Forwardable

def_delegator(:@hash, :[], :lookup)

def_delegator(:@hash, :[]=, :add)

def_delegators(:@hash, :size, :has_key?)

def initialize

@hash = Hash.new

end

end

st = SymbolTable.new

st.add('cat', 'feline animal') # => "feline animal"

st.add('dog', 'canine animal') # => "canine animal"

st.add('cow', 'bovine animal') # => "bovine animal"

st.has_key?('cow') # => true

st.lookup('dog') # => "canine animal"

  • Forwards can also be defined for individual objects by extending them with the SingleForwardable module. It’s hard to think of a good reason to use this feature, so here’s a silly one:

require 'forwardable'

TRICKS = [ "roll over", "play dead" ]

dog = "rover"

dog.extend SingleForwardable

dog.def_delegator(:TRICKS, :each, :can)

dog.can do |trick|

puts trick

end

  • Produces:

roll over

play dead

Library GDBM: Interface to GDBM Database

gdbm library available

Interfaces to the gdbm database library.[126] Although the DBM library provides generic access to gdbm databases, it doesn’t expose some features of the full gdbm interface, such as the cache size, synchronization mode, reorganization, and locking. Only one process may have a GDBM database open for writing (unless locking is disabled).

  • DBM
  • SDBM
  • Stores some values into a database and then reads them back. The second parameter to the open method specifies the file mode, and the next parameter uses two flags that (1) create the database if it doesn’t exist and (2) force all writes to be synced to disk. Create on open is the default Ruby gdbm behavior.

require 'gdbm'

GDBM.open("data.dbm", 0644, GDBM::WRCREAT | GDBM::SYNC) do |dbm|

dbm['name'] = "Walter Wombat"

dbm['dob'] = "1969-12-25"

dbm['uses'] = "Ruby"

end

GDBM.open("data.dbm") do |dbm|

p dbm.keys

p dbm['dob']

dbm.delete('dob')

p dbm.keys

end

  • Opens a database read-only. The attempt to delete a key would fail.

require 'gdbm'

GDBM.open("data.dbm", 0, GDBM::READER) do |dbm|

p dbm.keys

dbm.delete('name') # !! fails !!

end

Library GetoptLong: Parse Command-Line Options

Class GetoptLong supports GNU-style command-line option parsing. Options may be a minus sign (-) followed by a single character or may be two minus signs (--) followed by a name (a long option). Long options may be abbreviated to their shortest unambiguous lengths.

A single internal option may have multiple external representations. For example, the option to control verbose output could be any of -v, --verbose, or --details. Some options may also take an associated value.

Each internal option is passed to GetoptLong as an array, containing strings representing the option’s external forms and a flag. The flag specifies how GetoptLong is to associate an argument with the option (NO_ARGUMENT, REQUIRED_ARGUMENT, or OPTIONAL_ARGUMENT).

If the environment variable POSIXLY_CORRECT is set, all options must precede nonoptions on the command line. Otherwise, the default behavior of GetoptLong is to reorganize the command line to put the options at the front. This behavior may be changed by setting the attributeGetoptLong#ordering= to one of PERMUTE, REQUIRE_ORDER, or RETURN_IN_ORDER. The environment variable POSIXLY_CORRECT may not be overridden.

  • OptionParser

# Call using "ruby example.rb --size 10k -v -q a.txt b.doc"

require 'getoptlong'

# Fake out an initial command line

ARGV.clear.push *%w(--size 10k -v -q a.txt b.doc)

# specify the options we accept and initialize

# the option parser

opts = GetoptLong.new(

[ "--size", "-s", GetoptLong::REQUIRED_ARGUMENT ],

[ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],

[ "--query", "-q", GetoptLong::NO_ARGUMENT ],

[ "--check", "--valid", "-c", GetoptLong::NO_ARGUMENT ]

)

# process the parsed options

opts.each do |opt, arg|

puts "Option: #{opt}, arg #{arg.inspect}"

end

puts "Remaining args: #{ARGV.join(', ')}"

Produces:

Option: --size, arg "10k"

Option: --verbose, arg ""

Option: --query, arg ""

Remaining args: a.txt, b.doc

Library GServer: Generic TCP Server

This is a simple framework for writing TCP servers. To use it, subclass the GServer class, set the port (and potentially other parameters) in the constructor, and then implement a serve method to handle incoming requests.

GServer manages a thread pool for incoming connections, so your serve method may be running in multiple threads in parallel.

You can run multiple GServer copies on different ports in the same application.

  • When a connection is made on port 2000, responds with the current time as a string. Terminates after handling three requests.

require 'gserver'

class TimeServer < GServer

def initialize

super(2000)

@count = 3

end

def serve(client)

client.puts Time.now

@count -= 1

stop if @count.zero?

end

end

server = TimeServer.new

server.start.join

  • You can test this server by reading from localhost on port 2000. We use curl to do this—you could also use telnet:

$ curl -s localhost:2000

2013-05-27 12:33:22 -0500

Library IO/console: Add console support to IO objects

Require io/console, and I/O objects associated with terminals gain the methods IO#raw, IO#raw!, IO#getch, IO#echo=, IO#echo?, IO#noecho, IO#winsize, IO#winsize=, IO#iflush, IO#oflush, and IO#ioflush. The IO class also gains a singleton method, IO.console, which returns an I/O object connected to the controlling terminal of the process.

  • Prompt for a password with no echo.

require 'io/console'

password = STDIN.noecho do

print "Your password: "

gets

end

  • What’s the size of the controlling terminal?

require "io/console"

IO.console.winsize # => [22, 137]

Library IO/nonblock: Turn blocking I/O on and off

If a program requires io/nonblock, I/O objects gain the methods IO#nonblock, IO#nonblock?, and IO#nonblock=. The first takes a block, and runs that block with the given file description in nonblocking mode. The second lets you query the blocking status of a file descriptor, and the last lets you turn blocking on and off. You’ll probably want to investigate IO.select, as you’ll need it to tell when the file cn be read or written.

Library IO/Wait: Check for Pending Data to Be Read

FIONREAD feature in ioctl(2)

Including the library io/wait adds the methods IO#nread, IO#ready?, and IO#wait to the standard IO class. These allow an IO object opened on a stream (not a file) to be queried to see whether data is available to be read without reading it and to wait for a given number of bytes to become available.

  • Sets up a pipe between two processes and writes 10 bytes at a time into it. Periodically sees how much data is available.

require 'io/wait'

reader, writer = IO.pipe

if (pid = fork)

writer.close

8.times do

sleep 0.03

if reader.ready?

len = reader.nread

puts "#{len} bytes available: #{reader.sysread(len)}"

else

puts "No data available"

end

end

Process.waitpid(pid)

else

reader.close

5.times do |n|

sleep 0.04

writer.write n.to_s * 10

end

writer.close

end

  • Produces:

No data available

10 bytes available: 0000000000

10 bytes available: 1111111111

10 bytes available: 2222222222

No data available

10 bytes available: 3333333333

10 bytes available: 4444444444

No data available

Library IPAddr: Represent and Manipulate IP Addresses

Class IPAddr holds and manipulates Internet Protocol (IP) addresses. Each address contains three parts: an address, a mask, and an address family. The family will typically be AF_INET for IPv4 and IPv6 addresses. The class contains methods for extracting parts of an address, checking for IPv4-compatible addresses (and IPv4-mapped IPv6 addresses), testing whether an address falls within a subnet, and performing many other functions. It is also interesting in that it contains as data its own unit tests.

require 'ipaddr'

v4 = IPAddr.new('192.168.23.0/24')

v4 # => #<IPAddr: IPv4:192.168.23.0/ 255.255.255.0>

v4.mask(16) # => #<IPAddr: IPv4:192.168.0.0/ 255.255.0.0>

v4.reverse # => "0.23.168.192.in-addr.arpa"

v6 = IPAddr.new('3ffe:505:2::1')

v6 # => #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0001/

# .. ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>

v6.mask(48) # => #<IPAddr: IPv6:3ffe:0505:0002:0000:0000:0000:0000:0000/

# .. ffff:ffff:ffff:0000:0000:0000:0000:0000>

# the value for 'family' is OS dependent. This

# value is for OS X

v6.family # => 30

other = IPAddr.new("192.168.23.56")

v4.include?(other) # => true

Library irb: Interactive Ruby

The irb library is most commonly associated with the console command irb. However, you can also start an irb session from within your running application. A common technique is to trap a signal and start irb in the handler.

The following program sets up a signal handler that runs irb when the user hits ^C. The user can change the value of the instance variable @value. When they exit from irb, the original program continues to run with that new value.

sl_irb/run_irb.rb

require 'irb'

trap "INT" do

IRB.start

end

count = 0

loop do

count += 1

puts count

puts "Value = #{@value}" ifdefined? @value

sleep 1

end

Here’s a simple session using it:

$ ruby code/sl_irb/run_irb.rb

1

2

3

^Cruby-1.9.2-p0 > @value = "wibble"

=> "wibble"

ruby-1.9.2-p0 > exit

4

Value = wibble

5

Value = wibble

. . .

Library json: Generate and Parse JSON Format

JSON is a language-independent data interchange format based on key/value pairs (hashes in Ruby) and sequences of values (arrays in Ruby).[127] JSON is frequently used to exchange data between JavaScript running in browsers and server-based applications. JSON is not a general-purpose object marshaling format. Although you can add to_json methods to your own classes, you will lose interoperability.

  • yaml
  • Serializes a data structure into a string and writes that to a file:

require 'json'

data = { name: 'dave', address: [ 'tx', 'usa' ], age: 17 }

serialized = data.to_json

serialized # => {"name":"dave","address":["tx","usa"],"age":17}

File.open("data", "w") {|f| f.puts serialized}

  • Reads the serialized data from the file and reconstitutes it:

require 'json'

serialized = File.read("data")

data = JSON.parse(serialized)

data # => {"name"=>"dave", "address"=>["tx", "usa"], "age"=>17}

  • The methods j and jj convert their argument to JSON and write the result to STDOUT (jj prettyprints). This can be useful in irb.

require 'json'

data = { name: 'dave', address: [ 'tx', 'usa' ], age: 17 }

puts "Regular"

j data

puts "Pretty"

jj data

  • Produces:

Regular

{"name":"dave","address":["tx","usa"],"age":17}

Pretty

{

"name": "dave",

"address": [

"tx",

"usa"

],

"age": 17

}

Library Logger: Application Logging

Writes log messages to a file or stream. Supports automatic time- or size-based rolling of log files. Messages can be assigned severities, and only those messages at or above the logger’s current reporting level will be logged.

  • During development, you may want to see all messages:

require 'logger'

log = Logger.new(STDOUT)

log.level = Logger::DEBUG

log.datetime_format = "%H:%M:%S"

log.info("Application starting")

3.times do |i|

log.debug("Executing loop, i = #{i}")

temperature = some_calculation(i) # defined externally

if temperature > 50

log.warn("Possible overheat. i = #{i}")

end

end

log.info("Application terminating")

  • Produces:

I, [12:33:23#24712] INFO -- : Application starting

D, [12:33:23#24712] DEBUG -- : Executing loop, i = 0

D, [12:33:23#24712] DEBUG -- : Executing loop, i = 1

D, [12:33:23#24712] DEBUG -- : Executing loop, i = 2

W, [12:33:23#24712] WARN -- : Possible overheat. i = 2

I, [12:33:23#24712] INFO -- : Application terminating

  • In deployment, you can turn off anything below INFO:

require 'logger'

log = Logger.new(STDOUT)

log.level = Logger::INFO

log.datetime_format = "%H:%M:%S"

# as above...

  • Produces:

I, [12:33:23#24714] INFO -- : Application starting

W, [12:33:23#24714] WARN -- : Possible overheat. i = 2

I, [12:33:23#24714] INFO -- : Application terminating

  • Logs to a file, which is rotated when it gets to about 10KB. Keeps up to five old files.

require 'logger'

log = Logger.new("application.log", 5, 10*1024)

log.info("Application starting")

# ...

Library mathn: Unified Numbers

The mathn library attempts to bring some unity to numbers under Ruby, making classes Bignum, Complex, Fixnum, Integer, and Rational work and play better together. It automatically includes the libraries complex, rational, matrix, and prime.

  • Types will tend to convert between themselves in a more natural way (so, for example, Complex::I squared will evaluate to -1, rather than Complex[-1,0]).
  • Division will tend to produce more accurate results. The conventional division operator (/) is redefined to use quo , which doesn’t round.
  • Related to the previous point, rational numbers will be used in preference to floats when possible. Dividing one by two results in the rational number 1/2, rather than 0.5 (or 0, the result of normal integer division).
  • Matrix
  • Rational
  • Complex
  • Prime
  • Without mathn:

require 'matrix'

36/16 # => 2

Math.sqrt(36/16) # => 1.4142135623730951

Complex::I * Complex::I # => (-1+0i)

(36/16)**-2 # => 1/4

(-36/16)**-2 # => 1/9

(36/16)**(1/2) # => 1

(-36/16)**(1/2) # => 1

(36/16)**(-1/2) # => 1/2

(-36/16)**(-1/2) # => -1/3

Matrix.diagonal(6,7,8)/3 # => Matrix[[2, 0, 0], [0, 2, 0], [0, 0, 2]]

  • With mathn:

36/16 # => 9/4

Math.sqrt(36/16) # => 3/2

Complex::I * Complex::I # => -1

(36/16)**-2 # => 16/81

(-36/16)**-2 # => 16/81

(36/16)**(1/2) # => 3/2

(-36/16)**(1/2) # => (9.184850993605148e-17+1.5i)

(36/16)**(-1/2) # => 2/3

(-36/16)**(-1/2) # => (4.082155997157844e-17-0.6666666666666666i)

Matrix.diagonal(6,7,8)/3 # => Matrix[[2, 0, 0], [0, 7/3, 0], [0, 0, 8/3]]

Library Matrix: Matrix and Vector Manipulation

The matrix library defines classes Matrix and Vector, representing rectangular matrices and vectors. As well as the normal arithmetic operations, they provide methods for matrix-specific functions (such as rank, inverse, and determinants) and a number of constructor methods (for creating special-case matrices—zero, identity, diagonal, singular, and vector).

As of Ruby 1.9, matrices use quo internally for division, so rational numbers may be returned as a result of integer division. In prior versions of Ruby, you’d need to include the mathn library to achieve this.

require 'matrix'

m1 = Matrix[ [2, 1], [-1, 1] ]

m1[0,1] # => 1

m1.inv # => Matrix[[1/3, -1/3], [1/3, 2/3]]

m1 * m1.inv # => Matrix[[1/1, 0/1], [0/1, 1/1]]

m1.determinant # => 3

m1.singular? # => false

v1 = Vector[3, 4] # => Vector[3, 4]

v1.covector # => Matrix[[3, 4]]

m1 * v1 # => Vector[10, 1]

m2 = Matrix[ [1,2,3], [4,5,6], [7,8,9] ]

m2.minor(1, 2, 1, 2) # => Matrix[[5, 6], [8, 9]]

Library MiniTest: Unit Testing Framework

New in Ruby 1.9, MiniTest is now the standard unit testing framework supplied with Ruby. The minitest library contains classes for unit tests, mock objects, and a (trivial) subset of RSpec-style testing syntax.

The unit testing framework is similar to the original Test::Unit framework. However, if you want functionality that is the same as Test::Unit, use the Test::Unit wrappers for MiniTest—simply require "test/unit" as normal.

Chapter 13, Unit Testing contains a tutorial on unit testing with Ruby.

Library Monitor: Monitor-Based Synchronization

Monitors are a mutual-exclusion mechanism. They allow separate threads to define shared resources that will be accessed exclusively, and they provide a mechanism for a thread to wait for resources to become available in a controlled way.

The monitor library actually defines three separate ways of using monitors: by subclassing, as a mixin, and as a extension to a particular object. In this section, we show the mixin form of Monitor. The subclassing form is effectively identical. In both it and when including MonitorMixin in an existing class, it is essential to invoke super in the class’s initialize method.

  • Thread

# This example would be better written using fibers.

require 'monitor'

require 'mathn'

numbers = []

numbers.extend(MonitorMixin)

number_added = numbers.new_cond

consumer = Thread.new do # Reporter thread

5.times do

numbers.synchronize do

number_added.wait_while { numbers.empty? }

puts numbers.shift

end

end

end

generator = Thread.new do # Prime number generator thread

primes = Prime.each

5.times do

numbers.synchronize do

numbers << primes.next

number_added.signal

end

end

end

generator.join

consumer.join

Produces:

2

3

5

7

11

Library Mutex_m: Mutex Mix-In

mutex_m is a variant of class Mutex that allows mutex facilities to be mixed into any object.

The Mutex_m module defines methods that correspond to those in Mutex but with the prefix mu_ (so that lock is defined as mu_lock and so on). These are then aliased to the original Mutex names.

  • Mutex
  • Thread

require 'mutex_m'

class Counter

include Mutex_m

attr_reader :count

def initialize

@count = 0

super

end

def tick

lock

@count += 1

unlock

end

end

c = Counter.new

t1 = Thread.new { 100_000.times { c.tick } }

t2 = Thread.new { 100_000.times { c.tick } }

t1.join

t2.join

c.count # => 200000

Library Net::FTP: FTP Client

The net/ftp library implements a File Transfer Protocol (FTP) client. As well as data transfer commands (getbinaryfile, gettextfile, list, putbinaryfile, and puttextfile), the library supports the full complement of server commands (acct, chdir, delete, mdtm, mkdir, nlst, rename, rmdir, pwd, size, status, and system). Anonymous and password-authenticated sessions are supported. Connections may be active or passive.

  • open-uri

require 'net/ftp'

ftp = Net::FTP.new('ftp.ruby-lang.org')

ftp.login

ftp.chdir('pub/ruby/doc')

puts ftp.list('*txt')

ftp.getbinaryfile('MD5SUM.txt', 'md5sum.txt', 1024)

ftp.close

puts File.read('md5sum.txt')

produces:

-rw-rw-r-- 1 1027 100 12149 Sep 10 06:02 MD5SUM.txt

-rw-rw-r-- 1 1027 100 13565 Sep 10 06:03 SHA1SUM.txt

d529768c828c930c49b3766d13dc1f2c ruby-man-1.4.6-jp.tar.gz

8eed63fec14a719df26247fb8384db5e ruby-man-1.4.6.tar.gz

623b5d889c1f15b8a50fe0b3b8ba4b0f ruby-man-ja-1.6.6-20011225-rd.tar.gz

5f37ef2d67ab1932881cd713989af6bf ruby-man-ja-html-20050214.tar.bz2

e9949b2023a63b6259b02bed4fb13064 ruby-man-ja-html-20050214.tar.gz

. . .

Library Net::HTTP: HTTP Client

The net/http library provides a simple client to fetch headers and web page contents using the HTTP and HTTPS protocols.

The get post and head methods return a response object, with the content of the response accessible through the response’s body method.

  • OpenSSL
  • open-uri
  • URI
  • Opens a connection and fetches a page, displaying the response code and message, header information, and some of the body:

require 'net/http'

Net::HTTP.start('www.pragprog.com') do |http|

response = http.get('/categories/new')

puts "Code = #{response.code}"

puts "Message = #{response.message}"

response.each {|key, val| printf "%-14s = %-40.40s\n", key, val }

p response.body[0, 55]

end

  • Produces:

Code = 302

Message = Found

content-type = text/html; charset=utf-8

date = Mon, 27 May 2013 17:36:21 GMT

location = http://pragprog.com/categories/new

server = nginx/1.2.6

status = 302 Found

x-request-id = 1c76c5446f0a1dd001ceb768f2611364

x-runtime = 0.004833

x-ua-compatible = IE=Edge,chrome=1

content-length = 100

connection = keep-alive

"<html><body>You are being <a href=\"http://pragprog.com/"

  • Fetches a single page, displaying the response code and message, header information, and some of the body:

require 'net/http'

response = Net::HTTP.get_response('www.pragprog.com',

'/categories/new')

puts "Code = #{response.code}"

puts "Message = #{response.message}"

response.each {|key, val| printf "%-14s = %-40.40s\n", key, val }

p response.body[0, 55]

  • Produces:

Code = 302

Message = Found

content-type = text/html; charset=utf-8

date = Mon, 27 May 2013 17:36:21 GMT

location = http://pragprog.com/categories/new

server = nginx/1.2.6

status = 302 Found

x-request-id = ab9de753032bb022cbd33fefbe030f56

x-runtime = 0.005468

x-ua-compatible = IE=Edge,chrome=1

content-length = 100

connection = keep-alive

"<html><body>You are being <a href=\"http://pragprog.com/"

  • Follows redirections (the open-uri library does this automatically). This code comes from the RDoc documentation.

require 'net/http'

require 'uri'

def fetch(uri_str, limit=10)

fail 'http redirect too deep' if limit.zero?

puts "Trying: #{uri_str}"

response = Net::HTTP.get_response(URI.parse(uri_str))

case response

when Net::HTTPSuccess then response

when Net::HTTPRedirection then fetch(response['location'], limit-1)

else response.error!

end

end

response = fetch('http://www.ruby-lang.org')

p response.body[0, 50]

  • Produces:

Trying: http://www.ruby-lang.org

"<html>\n <head>\n <script type=\"text/javascript\""

  • Searches our site for things about Ruby and lists the authors. (This would be tidier using Hpricot.)

require 'net/http'

uri = URI.parse('http://pragprog.com/search')

response = Net::HTTP.post_form(uri, "q" => "ruby")

puts response.body.scan(%r{<p class="by-line">by (.*?)</p>})[0,3]

  • produces:

Caleb Tennis

Maik Schmidt

Bruce Tate

Library Net::IMAP: Access an IMAP Mail Server

The Internet Mail Access Protocol (IMAP) is used to allow mail clients to access mail servers. It supports plain-text login and the IMAP login and CRAM-MD5 authentication mechanisms. Once connected, the library supports threading, so multiple interactions with the server may take place at the same time.

The examples that follow are taken with minor modifications from the RDoc documentation in the library source file.

The TMail gem provides an interface for creating and parsing email messages.

  • Net::POP
  • Lists senders and subjects of messages to “dave” in the inbox:

require 'net/imap'

imap = Net::IMAP.new('my.mailserver.com')

imap.authenticate('LOGIN', 'dave', 'secret')

imap.examine('INBOX')

puts "Message count: #{ imap.responses["EXISTS"]}"

imap.search(["TO", "dave"]).each do |message_id|

envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]

puts "#{envelope.from[0].name}: \t#{envelope.subject}"

end

  • Moves all email messages with a date in April 2008 from the folder Mail/sent-mail to Mail/sent-apr08:

require 'net/imap'

imap = Net::IMAP.new('my.mailserver.com')

imap.authenticate('LOGIN', 'dave', 'secret')

imap.select('Mail/sent-mail')

ifnot imap.list('Mail/', 'sent-apr08')

imap.create('Mail/sent-apr08')

end

imap.search(["BEFORE", "01-May-2008",

"SINCE", "1-Apr-2008"]).each do |message_id|

imap.copy(message_id, "Mail/sent-apr08")

imap.store(message_id, "+FLAGS", [:Deleted])

end

imap.expunge

Library Net::POP: Access a POP Mail Server

The net/pop library provides a simple client to fetch and delete mail on a Post Office Protocol (POP) server.

The class Net::POP3 is used to access a POP server, returning a list of Net::POPMail objects, one per message stored on the server. These POPMail objects are then used to fetch and/or delete individual messages.

The library also provides class APOP, an alternative to the POP3 class that performs encrypted authentication.

require 'net/pop'

pop = Net::POP3.new('server.ruby-stuff.com')

pop.start('joe', 'secret') do |server|

msg = server.mails[0]

# Print the 'From:' header line

from = msg.header.split("\r\n").grep(/^From: /)[0]

puts from

puts

puts "Full message:"

text = msg.pop

puts text

end

Produces:

From: dave@facet.ruby-stuff.com (Dave Thomas)

Full message:

Return-Path: <dave@facet.ruby-stuff.com>

Received: from facet.ruby-stuff.com (facet.ruby-stuff.com [10.96.0.122])

by pragprog.com (8.11.6/8.11.6) with ESMTP id i2PJMW701809

for <joe@carat.ruby-stuff.com>; Thu, 25 Mar 2008 13:22:32 -0600

Received: by facet.ruby-stuff.com (Postfix, from userid 502)

id 4AF228B1BD; Thu, 25 Mar 2008 13:22:36 -0600 (CST)

To: joe@carat.ruby-stuff.com

Subject: Try out the new features!

Message-Id: <20080325192236.4AF228B1BD@facet.ruby-stuff.com>

Date: Thu, 25 Mar 2008 13:22:36 -0600 (CST)

From: dave@facet.ruby-stuff.com (Dave Thomas)

Status: RO

Ruby 1.9 has even more new features, both in

the core language and in the supplied libraries.

Try it out!

Library Net::SMTP: Simple SMTP Client

The net/smtp library provides a simple client to send electronic mail using the Simple Mail Transfer Protocol (SMTP). It does not assist in the creation of the message payload—it simply delivers messages once an RFC 822 message has been constructed. The TMail gem provides an interface for creating and parsing email messages.

  • Sends an e-mail from a string:

require 'net/smtp'

msg = "Subject: Test\n\nNow is the time\n"

Net::SMTP.start('pragprog.com') do |smtp|

smtp.send_message(msg, 'dave@pragprog.com', ['dave'])

end

  • Sends an e-mail using an SMTP object and an adapter:

require 'net/smtp'

Net::SMTP::start('pragprog.com', 25, "pragprog.com") do |smtp|

smtp.open_message_stream('dave@pragprog.com', # from

[ 'dave' ] # to

) do |stream|

stream.puts "Subject: Test1"

stream.puts

stream.puts "And so is this"

end

end

  • Sends an e-mail to a server requiring CRAM-MD5 authentication:

require 'net/smtp'

msg = "Subject: Test\n\nNow is the time\n"

Net::SMTP.start('pragprog.com', 25, 'pragprog.com',

'user', 'password', :cram_md5) do |smtp|

smtp.send_message(msg, 'dave@pragprog.com', ['dave'])

end

Library Net::Telnet: Telnet Client

The net/telnet library provides a complete implementation of a telnet client and includes features that make it a convenient mechanism for interacting with nontelnet services.

  • Connects to localhost, runs the date command, and disconnects:

require 'net/telnet'

tn = Net::Telnet.new({})

tn.login "testuser", "wibble"

tn.cmd "date" # => "date\nMon May 27 12:33:29 CDT 2013\nlight-boy:~ testuser$ "

  • The methods new , cmd , login , and waitfor take an optional block. If present, the block is passed output from the server as it is received by the routine. This can be used to provide real-time output, rather than waiting (for example) for a login to complete before displaying the server’s response.

require 'net/telnet'

tn = Net::Telnet.new({}) {|str| print str }

tn.login("testuser", "wibble") {|str| print str }

tn.cmd("date") {|str| print str }

  • Produces:

Trying localhost...

Connected to localhost.

Darwin/BSD (light-boy.local) (ttys007)

login: testuser

Password:

Last login: Mon May 27 12:33:29 on ttys007

light-boy:~ testuser$ date

Mon May 27 12:33:29 CDT 2013

light-boy:~ testuser$

  • Query a WHOIS server on port 43.

require 'net/telnet'

tn = Net::Telnet.new('Host' => 'whois.domain.com',

'Port' => '43',

'Timeout' => 5,

'Telnetmode' => false)

tn.write("pragprog.com\r\n")

puts tn.sock.grep(/ on /)

  • Produces:

Record last updated on 15-Oct-2012.

Record expires on 19-Jan-2016.

Record created on 19-Jan-1999.

Library NKF: Interface to Network Kanji Filter

The NKF module is a wrapper around Itaru Ichikawa’s Network Kanji Filter (NKF) library (version 1.7). It provides functions to guess at the encoding of JIS, EUC, and SJIS streams and to convert from one encoding to another. Even though Ruby 1.9 now supports these encodings natively, this library is still useful for guessing encodings.

  • As of Ruby 1.9, NFK uses the built-in encoding objects:

require 'nkf'

NKF::AUTO # => nil

NKF::JIS # => #<Encoding:ISO-2022-JP (dummy)>

NKF::EUC # => #<Encoding:EUC-JP>

NKF::SJIS # => #<Encoding:Shift_JIS>

  • Guesses at the encoding of a string. (Thanks to Nobu Nakada for the examples on this page.)

require 'nkf'

p NKF.guess("Yukihiro Matsumoto")

p NKF.guess("\e$B$^$D$b$H$f$-$R$m\e(B")

p NKF.guess("\244\336\244\304\244\342\244\310\244\346\244\255\244\322\244\355")

p NKF.guess("\202\334\202\302\202\340\202\306\202\344\202\253\202\320\202\353")

  • Produces:

#<Encoding:US-ASCII>

#<Encoding:ISO-2022-JP (dummy)>

#<Encoding:EUC-JP>

#<Encoding:Shift_JIS>

  • TheNFK.nkf method takes two parameters. The first is a set of options, passed on to the NKF library. The second is the string to translate. The following examples assume that your console is set up to accommodate Japanese characters. The text at the end of the three ruby commands is Yukihiro Matsumoto in Hiragana.

Library Observable: The Observer Pattern

The Observer pattern, also known as Publish/Subscribe, provides a simple mechanism for one object (the source) to inform a set of interested third-party objects when its state changes (see Design Patterns [GHJV95]). In the Ruby implementation, the notifying class mixes in the module Observable, which provides the methods for managing the associated observer objects. The observers must implement the update method to receive notifications.

require 'observer'

class CheckWaterTemperature # Periodically check the water

include Observable

def run

last_temp = nil

loop do

temp = Temperature.fetch # external class...

puts "Current temperature: #{temp}"

if temp != last_temp

changed # notify observers

notify_observers(Time.now, temp)

last_temp = temp

end

end

end

end

class Warner

def initialize(&limit)

@limit = limit

end

def update(time, temp) # callback for observer

if @limit.call(temp)

puts "--- #{time.to_s}: Temperature outside range: #{temp}"

end

end

end

checker = CheckWaterTemperature.new

checker.add_observer(Warner.new {|t| t < 80})

checker.add_observer(Warner.new {|t| t > 120})

checker.run

Produces:

Current temperature: 83

Current temperature: 75

--- 2013-05-27 12:33:30 -0500: Temperature outside range: 75

Current temperature: 90

Current temperature: 134

--- 2013-05-27 12:33:30 -0500: Temperature outside range: 134

Current temperature: 134

Current temperature: 112

Current temperature: 79

--- 2013-05-27 12:33:30 -0500: Temperature outside range: 79

Library open-uri: Treat FTP and HTTP Resources as Files

The open-uri library extends Object#open, allowing it to accept URIs for FTP and HTTP as well as local filenames. Once opened, these resources can be treated as if they were local files, accessed using conventional IO methods. The URI passed to open is either a string containing an HTTP or FTP URL or a URI object (see the URI library). When opening an HTTP resource, the method automatically handles redirection and proxies. When using an FTP resource, the method logs in as an anonymous user.

The IO object returned by open in these cases is extended to support methods that return metainformation from the request: content_type , charset , content_encoding , last_modified , status , base_uri , meta .

  • URI

require 'open-uri'

require 'pp'

open('http://ruby-lang.org') do |f|

puts "URI: #{f.base_uri}"

puts "Content-type: #{f.content_type}, charset: #{f.charset}"

puts "Encoding: #{f.content_encoding}"

puts "Last modified: #{f.last_modified}"

puts "Status: #{f.status.inspect}"

pp f.meta

puts "----"

3.times {|i| puts "#{i}: #{f.gets}" }

end

Produces:

URI: http://www.ruby-lang.org/

Content-type: text/html, charset: iso-8859-1

Encoding: []

Last modified: 2013-05-22 16:31:36 -0500

Status: ["200", "OK"]

{"date"=>"Mon, 27 May 2013 17:33:23 GMT",

"server"=>"nginx/0.7.67",

"content-type"=>"text/html",

"content-length"=>"748",

"last-modified"=>"Wed, 22 May 2013 21:31:36 GMT",

"accept-ranges"=>"bytes",

"via"=>"1.1 www.ruby-lang.org"}

----

0: <html>

1: <head>

2: <script type="text/javascript">

Library Open3: Run Subprocess and Connect to All Streams

Runs a command in a subprocess. Data written to stdin can be read by the subprocess, and data written to standard output and standard error in the subprocess will be available on the stdout and stderr streams. The subprocess is actually run as a grandchild, and as a result, Process#waitallcannot be used to wait for its termination (hence the sleep in the following example). Note also that you probably cannot assume that the application’s output and error streams will not be buffered, so output may not arrive when you expect it to arrive.

require 'open3'

def read_from(label, stream)

while line = stream.gets

puts "#{label}: #{line}"

end

end

Open3.popen3('bc') do | stdin, stdout, stderr |

t1 = Thread.new { read_from('STDOUT', stdout) }

t2 = Thread.new { read_from('STDERR', stderr) }

stdin.puts "3 * 4"

stdin.puts "1 / 0"

stdin.puts "2 ^ 5"

stdin.close

t1.join

t2.join

end

Produces:

STDOUT: 12

STDERR: Runtime error (func=(main), adr=3): Divide by zero

STDOUT: 32

Library OpenSSL: SSL Library

OpenSSL library available

The Ruby OpenSSL extension wraps the freely available OpenSSL library.[128] It provides the Secure Sockets Layer and Transport Layer Security (SSL and TLS) protocols, allowing for secure communications over networks. The library provides functions for certificate creation and management, message signing, and encryption/decryption. It also provides wrappers to simplify access to HTTPS servers, along with secure FTP. The interface to the library is large (roughly 330 methods), but the average Ruby user will probably use only a small subset of the library’s capabilities.

  • Net::FTP
  • Net::HTTP
  • Socket
  • Accesses a secure website using HTTPS. Note that SSL is used to tunnel to the site, but the requested page also requires standard HTTP basic authorization.

require 'net/https'

USER = "xxx"

PW = "yyy"

site = Net::HTTP.new("www.securestuff.com", 443)

site.use_ssl = true

response = site.get2("/cgi-bin/cokerecipe.cgi",

'Authorization' => 'Basic ' +

["#{USER}:#{PW}"].pack('m').strip)

  • Creates a socket that uses SSL. This isn’t a good example of accessing a website. However, it illustrates how a socket can be encrypted.

require 'socket'

require 'openssl'

socket = TCPSocket.new("www.secure-stuff.com", 443)

ssl_context = OpenSSL::SSL::SSLContext.new()

unless ssl_context.verify_mode

warn "warning: peer certificate won't be verified this session."

ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE

end

sslsocket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)

sslsocket.sync_close = true

sslsocket.connect

sslsocket.puts("GET /secret-info.shtml")

while line = sslsocket.gets

p line

end

Library OptionParser: Option Parsing

OptionParser is a flexible and extensible way to parse command-line arguments. It has a particularly rich abstraction of the concept of an option.

  • An option can have multiple short names (options preceded by a single hyphen) and multiple long names (options preceded by two hyphens). Thus, an option that displays help may be available as -h, -?, --help, and --about. Users may abbreviate long option names to the shortest nonambiguous prefix.
  • An option may be specified as having no argument, an optional argument, or a required argument. Arguments can be validated against patterns or lists of valid values.
  • Arguments may be returned as objects of any type (not just strings). The argument type system is extensible (we add Date handling in the example).
  • Arguments can have one or more lines of descriptive text, used when generating usage information.

Options are specified using the on and def methods. These methods take a variable number of arguments that cumulatively build a definition of each option. The arguments accepted by these methods are:

"-x" "-xARG" "-x=ARG" "-x[OPT]" "-x[=OPT]" "-x PLACE"

Option has short name x. First form has no argument, next two have mandatory argument, next two have optional argument, last specifies argument follows option. The short names may also be specified as a range (such as "-[a-c]").

"--switch" "--switch=ARG" "--switch=[OPT]" "--switch PLACE"

Option has long name switch. First form has no argument, next has a mandatory argument, the next has an optional argument, and the last specifies the argument follows the switch.

"--no-switch"

Defines a option whose default value is false.

"=ARG" "=[OPT]"

Argument for this option is mandatory or optional. For example, the following code says there’s an option known by the aliases -x, -y, and -z that takes a mandatory argument, shown in the usage as N:

opt.on("-x", "-y", "-z", "=N")

"description"

Any string that doesn’t start - or = is used as a description for this option in the summary. Multiple descriptions may be given; they’ll be shown on additional lines.

/pattern/

Any argument must match the given pattern.

array

Argument must be one of the values from array.

proc or method

Argument type conversion is performed by the given proc or method (rather than using the block associated with the on or def method call).

ClassName

Argument must match that defined for ClassName, which may be predefined or added using OptionParser.accept. Built-in argument classes are

Object:

Any string. No conversion. This is the default.

String:

Any nonempty string. No conversion.

Integer:

Ruby/C-like integer with optional sign (0ddd is octal, 0bddd binary, 0xddd hexadecimal). Converts to Integer.

Float:

Float number format. Converts to Float.

Numeric:

Generic numeric format. Converts to Integer for integers, Float for floats.

Array:

Argument must be of list of strings separated by a comma.

OptionParser::DecimalInteger:

Decimal integer. Converted to Integer.

OptionParser::OctalInteger:

Ruby/C-like octal/hexadecimal/binary integer.

OptionParser::DecimalNumeric:

Decimal integer/float number. Integers converted to Integer, floats to Float.

TrueClass, FalseClass:

Boolean switch.

  • GetoptLong

require 'optparse'

require 'date'

# Add Dates as a new option type

OptionParser.accept(Date, /(\d+)-(\d+)-(\d+)/) do |d, mon, day, year|

Date.new(year.to_i, mon.to_i, day.to_i)

end

opts = OptionParser.new

opts.on("-x") {|val| puts "-x seen" }

opts.on("-s", "--size VAL", Integer) {|val| puts "-s #{val}" }

opts.on("-a", "--at DATE", Date) {|val| puts "-a #{val}" }

my_argv = [ "--size", "1234", "-x", "-a", "12-25-2008", "fred", "wilma" ]

rest = opts.parse(*my_argv)

puts "Remainder = #{rest.join(', ')}"

puts opts.to_s

Produces:

-s 1234

-x seen

-a 2008-12-25

Remainder = fred, wilma

Usage: prog [options]

-x

-s, --size VAL

-a, --at DATE

Library OpenStruct: Open (dynamic) Structure

An open structure is an object whose attributes are created dynamically when first assigned. In other words, if obj is an instance of an OpenStruct, then the statement obj.abc=1 will create the attribute abc in obj and then assign the value 1 to it.

require 'ostruct'

os = OpenStruct.new( "f1" => "one", :f2 => "two" )

os.f3 = "cat"

os.f4 = 99

os.f1 # => "one"

os.f2 # => "two"

os.f3 # => "cat"

os.f4 # => 99

OpenStruct uses method_missing to intercept calls. This might cause a problem, because calls to a method defined in class Object will not invoke method_missing —they’ll simply call the method in Object. In practice, this isn’t a problem, because you typically call a setter before calling a getting, and when you do call the setter method, ostruct will defined getter and setter methods, overriding those in Object. Here’s a typical example; because we call ice.freeze= first, the freeze= and freeze methods will be dynamically created in the ostruct, and the getter will work as expected.

require 'ostruct'

ice = OpenStruct.new

ice.freeze = "yes"

ice.freeze # => #<OpenStruct freeze="yes">

However, if you don’t first call the setter, the freeze getter will not invoke method_missing —it’ll simply call the underlying freeze method in Object.

require 'ostruct'

ice = OpenStruct.new

p ice.freeze

ice.freeze = "yes"

Produces:

#<OpenStruct>

prog.rb:5:in `<main>': can't modify frozen OpenStruct (TypeError)

Library Pathname: Representation of File Paths

A Pathname represents the absolute or relative name of a file. It has two distinct uses. First, it allows manipulation of the parts of a file path (extracting components, building new paths, and so on). Second (and somewhat confusingly), it acts as a façade for some methods in classes Dir, File, and module FileTest, forwarding on calls for the file named by the Pathname object.

  • File
  • Path name manipulation:

require 'pathname'

p1 = Pathname.new("/usr/bin")

p2 = Pathname.new("ruby")

p3 = p1 + p2

p4 = p2 + p1

p3.parent # => #<Pathname:/usr/bin>

p3.parent.parent # => #<Pathname:/usr>

p1.absolute? # => true

p2.absolute? # => false

p3.split # => [#<Pathname:/usr/bin>, #<Pathname:ruby>]

p5 = Pathname.new("testdir")

puts p5.realpath

puts p5.children

  • Produces:

/Users/dave/BS2/published/ruby4/Book/testdir

testdir/.svn

testdir/config.h

testdir/main.rb

  • Path name as proxy for file and directory status requests:

require 'pathname'

p1 = Pathname.new("/usr/bin/ruby")

p1.file? # => true

p1.directory? # => false

p1.executable? # => true

p1.size # => 34752

p2 = Pathname.new("testfile") # => #<Pathname:testfile>

p2.read # => "This is line one\nThis is line two\nThis is

# .. line three\nAnd so on...\n"

p2.readlines # => ["This is line one\n", "This is line two\n",

# .. "This is line three\n", "And so on...\n"]

Library PP: Pretty-print Objects

PP uses the PrettyPrint library to format the results of inspecting Ruby objects. As well as the methods in the class, it defines a global function, pp , which works like the existing p method but formats its output.

PP has a default layout for all Ruby objects. However, you can override the way it handles a class by defining the method pretty_print , which takes a PP object as a parameter. It should use that PP object’s methods text , breakable , nest , group , and pp to format its output (see PrettyPrint for details).

  • JSON
  • PrettyPrint
  • YAML
  • Compares “p” and “pp”:

require 'pp'

Customer = Struct.new(:name, :sex, :dob, :country)

cust = Customer.new("Walter Wall", "Male", "12/25/1960", "Niue")

puts "Regular print"

p cust

puts "\nPretty print"

pp cust

  • Produces:

Regular print

#<struct Customer name="Walter Wall", sex="Male", dob="12/25/1960",

country="Niue">

Pretty print

#<struct Customer

name="Walter Wall",

sex="Male",

dob="12/25/1960",

country="Niue">

  • You can tell PP not to display an object if it has already displayed it:

require 'pp'

a = "string"

b = [ a ]

c = [ b, b ]

PP.sharing_detection = false

pp c

PP.sharing_detection = true

pp c

  • Produces:

[["string"], ["string"]]

[["string"], [...]]

Library PrettyPrint: General Pretty Printer

PrettyPrint implements a pretty printer for structured text. It handles details of wrapping, grouping, and indentation. The PP library uses PrettyPrint to generate more legible dumps of Ruby objects.

  • PP

The following program prints a chart of Ruby’s classes, showing subclasses as a bracketed list following the parent. To save some space, we show just the classes in the Numeric branch of the tree.

require 'prettyprint'

@children = Hash.new { |h,k| h[k] = Array.new }

ObjectSpace.each_object(Class) do |cls|

@children[cls.superclass] << cls if cls <= Numeric

end

def print_children_of(printer, cls)

printer.text(cls.name)

kids = @children[cls].sort_by(&:name)

unless kids.empty?

printer.group(0, " [", "]") do

printer.nest(3) do

printer.breakable

kids.each_with_index do |k, i|

printer.breakable unless i.zero?

print_children_of(printer, k)

end

end

printer.breakable

end

end

end

printer = PrettyPrint.new(STDOUT, 30)

print_children_of(printer, Object)

printer.flush

Produces:

Object [

Numeric [

Complex

Float

Integer [

Bignum

Fixnum

]

Rational

]

]

Library prime: Prime Numbers

Provides facilities for generating prime numbers, as well as factoring numbers. Note that the Prime class is a singleton.

  • mathn
  • The prime library extends the number classes to include new functionality and adds a new class Prime:

require 'prime'

# 60 = 2**2 * 3 * 5

60.prime? # => false

60.prime_division # => [[2, 2], [3, 1], [5, 1]]

  • You can also use it to generate sequences of primes:

require 'prime'

Prime.each {|p| breakif p > 20; print p, " " }

  • Produces:

2 3 5 7 11 13 17 19

  • Because Prime.each returns an enumerator if no block is present, we can write the previous example more concisely.

require 'prime'

puts Prime.each.take_while {|p| p <= 20 }.join(" ")

  • Produces:

2 3 5 7 11 13 17 19

Library Profile: Profile Execution of a Ruby Program

The profile library is a trivial wrapper around the Profiler module, making it easy to profile the execution of an entire program. Profiling can be enabled from the command line using the -r profile option or from within a source program by requiring the profile module.

Unlike Ruby 1.8, Ruby 1.9 does not profile primitive methods such as Fixnum#== and Fixnum#+. This helps boost Ruby’s performance.

  • Benchmark
  • Profiler__

require 'profile'

def ackerman(m, n)

if m == 0 then n+1

elsif n == 0 and m > 0 then ackerman(m-1, 1)

else ackerman(m-1, ackerman(m, n-1))

end

end

ackerman(3, 3)

Produces:

% cumulative self self total

time seconds seconds calls ms/call ms/call name

100.00 0.04 0.04 2432 0.02 0.64 Object#ackerman

0.00 0.04 0.00 1 0.00 0.00 TracePoint#enable

0.00 0.04 0.00 1 0.00 0.00 Module#method_added

0.00 0.04 0.00 1 0.00 0.00 TracePoint#disable

0.00 0.04 0.00 1 0.00 40.00 #toplevel

Library Profiler__: Control Execution Profiling

The Profiler__ module can be used to collect a summary of the number of calls to, and the time spent in, methods in a Ruby program. The output is sorted by the total time spent in each method. The profile library is a convenience wrapper that profiles an entire program.

  • Benchmark
  • profile

require 'profiler'

# ...Omit definition of connection and fetching methods...

def calc_discount(qty, price)

case qty

when 0..10 then 0.0

when 11..99 then price * 0.05

else price * 0.1

end

end

def calc_sales_totals(rows)

total_qty = total_price = total_disc = 0

rows.each do |row|

total_qty += row.qty

total_price += row.price

total_disc += calc_discount(row.qty, row.price)

end

end

connect_to_database

rows = read_sales_data

Profiler__::start_profile

calc_sales_totals(rows)

Profiler__::stop_profile

Profiler__::print_profile(STDOUT)

Produces:

% cumulative self self total

time seconds seconds calls ms/call ms/call name

28.57 0.02 0.02 648 0.03 0.03 Range#include?

28.57 0.04 0.02 1 20.00 70.00 Array#each

14.29 0.05 0.01 325 0.03 0.37 Object#calc_sales_totals

14.29 0.06 0.01 324 0.03 0.12 Object#calc_discount

14.29 0.07 0.01 648 0.02 0.05 Range#===

0.00 0.07 0.00 1 0.00 0.00 TracePoint#enable

0.00 0.07 0.00 648 0.00 0.00 Float#<=>

0.00 0.07 0.00 648 0.00 0.00 Fixnum#<=>

0.00 0.07 0.00 648 0.00 0.00 SalesData#price

0.00 0.07 0.00 3 0.00 0.00 Fixnum#+

0.00 0.07 0.00 648 0.00 0.00 SalesData#qty

0.00 0.07 0.00 1 0.00 0.00 TracePoint#disable

0.00 0.07 0.00 1 0.00 70.00 #toplevel

Library PStore: Persistent Object Storage

The PStore class provides transactional, file-based, persistent storage of Ruby objects. Each PStore can store several object hierarchies. Each hierarchy has a root, identified by a key (often a string). At the start of a PStore transaction, these hierarchies are read from a disk file and made available to the Ruby program. At the end of the transaction, the hierarchies are written back to the file. Any changes made to objects in these hierarchies are therefore saved on disk, to be read at the start of the next transaction that uses that file.

In normal use, a PStore object is created and then is used one or more times to control a transaction. Within the body of the transaction, any object hierarchies that had previously been saved are made available, and any changes to object hierarchies, and any new hierarchies, are written back to the file at the end.

The following example stores two hierarchies in a PStore. The first, identified by the key "names", is an array of strings. The second, identified by "tree", is a simple binary tree.

require 'pstore'

require 'pp'

class T

def initialize(val, left=nil, right=nil)

@val, @left, @right = val, left, right

end

def to_a

[ @val, @left.to_a, @right.to_a ]

end

end

def T(*args)

T.new(*args)

end

store = PStore.new("/tmp/store")

store.transaction do

store['names'] = [ 'Douglas', 'Barenberg', 'Meyer' ]

store['tree'] = T('top',

T('A', T('B')),

T('C', T('D', nil, T('E'))))

end

# now read it back in

store.transaction do

puts "Roots: #{store.roots.join(', ')}"

puts store['names'].join(', ')

pp store['tree'].to_a

end

Produces:

Roots: names, tree

Douglas, Barenberg, Meyer

["top",

["A", ["B", [], []], []],

["C", ["D", [], ["E", [], []]], []]]

Library PTY: Pseudo-Terminal Interface: Interact with External Processes

Unix with pty support

Many Unix platforms support a pseudo-terminal —a device pair where one end emulates a process running on a conventional terminal, and the other end can read and write that terminal as if it were a user looking at a screen and typing on a keyboard.

The PTY library provides the method spawn , which starts the given command (by default a shell), connecting it to one end of a pseudo-terminal. It then returns the reader and writer streams connected to that terminal, allowing your process to interact with the running process.

Working with pseudo-terminals can be tricky. See IO#expect for a convenience method that makes life easier. You might also want to track down Ara T. Howard’s Session module for an even simpler approach to driving subprocesses.[129]

  • expect

This example runs irb in a subshell and asks it to convert the string “cat” to uppercase:

require 'pty'

require 'expect'

$expect_verbose = true

PTY.spawn("irb") do |reader, writer, pid|

reader.expect(/> /)

writer.puts "'cat'.upcase"

reader.expect("=> ")

answer = reader.gets

puts "Answer = #{answer}"

end

Produces:

2.0.0p0 :001 > 'cat'.upcase

=> Answer = "CAT"

Library Rational: Rational Numbers

The Rational class is now built in to Ruby. The vestigial Rational library simply defines a few aliases for backward compatibility. For the classes Fixnum and Bignum, the following aliases are defined:

Floating-point division

quof is an alias for fdiv .

Rational division

rdiv is an alias for quo .

Exponentiation

power! and rpower are aliases for ** .

Library Readline: Interface to GNU Readline Library

GNU readline present

The Readline module allows programs to prompt for and receive lines of user input. The module allows lines to be edited during entry, and command history allows previous commands to be recalled and edited. The history can be searched, allowing the user to (for example) recall a previous command containing the text ruby. Command completion allows context-sensitive shortcuts: tokens can be expanded in the command line under control of the invoking application. In typical GNU fashion, the underlying readline library supports more options than any user could need and emulates both vi and emacs key bindings.

This meaningless program implements a trivial interpreter that can increment and decrement a value. It uses the Abbrev module to expand abbreviated commands when the Tab key is pressed.

sl_readline/readline.rb

require 'abbrev'

require 'readline'

include Readline

ABBREV = %w{ exit inc dec }.abbrev

Readline.completion_proc = -> string { ABBREV[string] }

value = 0

loop do

cmd = readline("wibble [#{value}]: ", true) || "exit"

case cmd.strip

when "exit" thenbreak

when "inc" then value += 1

when "dec" then value -= 1

else puts "Invalid command #{cmd}"

end

end

wibble [0]: inc

wibble [1]: <up-arrow> => inc

wibble [2]: d<tab> => dec

wibble [1]: in<esc><p> => inc

wibble [2]: exit

Library Resolv: DNS Client Library

The resolv library is a pure-Ruby implementation of a DNS client—it can be used to convert domain names into corresponding IP addresses. It also supports reverse lookups and the resolution of names in the local hosts file.

Loading the additional library resolv-replace insinuates the resolv library into Ruby’s socket library.

Basic name lookups are already built-in to the standard socket libraries. The resolv library exists because, prior to Ruby 1.9, calling the operating system to do a name lookup would suspend all interpreter threads. That is no longer the case.

Library REXML: XML Processing Library

REXML is a pure-Ruby XML processing library, including DTD-compliant document parsing, XPath querying, and document generation. It supports both tree-based and stream-based document processing. Because it is written in Ruby, it is available on all platforms supporting Ruby. REXML has a full and complex interface—this section contains a few small examples.

  • Assume the file demo.xml contains this:

<classes language="ruby">

<class name="Numeric">

Numeric represents all numbers.

<class name="Float">

Floating point numbers have a fraction and a mantissa.

</class>

<class name="Integer">

Integers contain exact integral values.

<class name="Fixnum">

Fixnums are stored as machine ints.

</class>

<class name="Bignum">

Bignums store arbitraty-sized integers.

</class>

</class>

</class>

</classes>

  • Reads and processes the XML:

require 'rexml/document'

xml = REXML::Document.new(File.open("code/sl_rexml/demo.xml"))

puts "Root element: #{xml.root.name}"

print "The names of all classes: "

xml.elements.each("//class") {|c| print c.attributes["name"], " " }

print "\nDescription of Fixnum: "

p xml.elements["//class[@name='Fixnum']"].text

  • Produces:

Root element: classes

The names of all classes: Numeric Float Integer Fixnum Bignum

Description of Fixnum: "\n Fixnums are stored as machine ints.\n "

  • Reads in a document, adds and deletes elements, and manipulates attributes before writing it back out:

require 'rexml/document'

include REXML

xml = Document.new(File.open("code/sl_rexml/demo.xml"))

cls = Element.new("class")

cls.attributes["name"] = "Rational"

cls.text = "Represents complex numbers"

# Remove Integer's children, and add our new node as

# the one after Integer

int = xml.elements["//class[@name='Integer']"]

int.delete_at(1)

int.delete_at(2)

int.next_sibling = cls

# Change all the 'name' attributes to class_name

xml.elements.each("//class") do |c|

c.attributes['class_name'] = c.attributes['name']

c.attributes.delete('name')

end

# and write it out with a XML declaration at the front

xml << XMLDecl.new

xml.write(STDOUT, 2)

  • Produces:

<?xml version='1.0'?>

<classes language='ruby'>

<class class_name='Numeric'>

Numeric represents all numbers.

<class class_name='Float'>

Floating point numbers have a fraction and a mantissa.

</class>

<class class_name='Integer'>

Integers contain exact integral values.

</class>

<class class_name='Rational'>

Represents complex numbers

</class>

</class>

</classes>

Library Rinda: Tuplespace Implementation

Tuplespaces are a distributed blackboard system. Processes may add tuples to the blackboard, and other processes may remove tuples from the blackboard that match a certain pattern. Originally presented by David Gelernter, tuplespaces offer an interesting scheme for distributed cooperation among heterogeneous processes.

Rinda, the Ruby implementation of tuplespaces, offers some interesting additions to the concept. In particular, the Rinda implementation uses the === operator to match tuples. This means that tuples may be matched using regular expressions, the classes of their elements, and the element values.

  • DRb
  • The blackboard is a DRb server that offers a shared tuplespace:

require 'rinda/tuplespace'

MY_URI = "druby://127.0.0.1:12131"

DRb.start_service(MY_URI, Rinda::TupleSpace.new)

DRb.thread.join

  • The arithmetic agent accepts messages containing an arithmetic operator and two numbers. It stores the result back on the blackboard.

require 'rinda/rinda'

MY_URI = "druby://127.0.0.1:12131"

DRb.start_service

ts = Rinda::TupleSpaceProxy.new(DRbObject.new(nil, MY_URI))

loop do

op, v1, v2 = ts.take([ %r{^[-+/*]$}, Numeric, Numeric])

ts.write(["result", v1.send(op, v2)])

end

  • The client places tuples on the blackboard and reads back the result of each:

require 'rinda/rinda'

MY_URI = "druby://127.0.0.1:12131"

DRb.start_service

ts = Rinda::TupleSpaceProxy.new(DRbObject.new(nil, MY_URI))

queries = [[ "+", 1, 2 ], [ "*", 3, 4 ], [ "/", 8, 2 ]]

queries.each do |q|

ts.write(q)

ans = ts.take(["result", nil])

puts "#{q[1]} #{q[0]} #{q[2]} = #{ans[1]}"

end

  • Produces:

1 + 2 = 3

3 * 4 = 12

8 / 2 = 4

Library Ripper: Parse Ruby Source

The ripper library gives you access to Ruby’s parser. It can tokenize input, return lexical tokens, and return a nested S-expression. It also supports event-based parsing.

  • Tokenize a line of Ruby code:

require "ripper"

content = "a=1;b=2;puts a+b"

p Ripper.tokenize(content)

  • Produces:

["a", "=", "1", ";", "b", "=", "2", ";", "puts", " ", "a", "+", "b"]

  • Does a lexical analysis, returning token types, values, and line and column numbers:

require "ripper"

require "pp"

content = "a=1;b=2;puts a+b"

pp Ripper.lex(content)[0,5]

  • Produces:

[[[1, 0], :on_ident, "a"],

[[1, 1], :on_op, "="],

[[1, 2], :on_int, "1"],

[[1, 3], :on_semicolon, ";"],

[[1, 4], :on_ident, "b"]]

  • Returns the sexp representing a chunk of code:

require "ripper"

require "pp"

content = "a=1;b=2;puts a+b"

pp Ripper.sexp(content)

  • Produces:

[:program,

[[:assign, [:var_field, [:@ident, "a", [1, 0]]], [:@int, "1", [1, 2]]],

[:assign, [:var_field, [:@ident, "b", [1, 4]]], [:@int, "2", [1, 6]]],

[:command,

[:@ident, "puts", [1, 8]],

[:args_add_block,

[[:binary,

[:var_ref, [:@ident, "a", [1, 13]]],

:+,

[:var_ref, [:@ident, "b", [1, 15]]]]],

false]]]]

  • As a (silly) example of event-based lexical analysis, here’s a program that finds class definitions and their associated comment blocks. For each, it outputs the class name and the comment. It might be considered the zeroth iteration of an RDoc-like program.

The parameter to parse is an accumulator—it is passed between event handlers and can be used to construct the result.

require 'ripper'

# This class handles parser events, extracting

# comments and attaching them to class definitions

class BabyRDoc < Ripper::Filter

def initialize(*)

super

reset_state

end

def on_default(event, token, output)

reset_state

output

end

def on_sp(token, output)

output

end

alias on_nil on_sp

def on_comment(comment, output)

@comment << comment.sub(/^\s*#\s*/, " ")

output

end

def on_kw(name, output)

@expecting_class_name = (name == 'class')

output

end

def on_const(name, output)

if @expecting_class_name

output << "#{name}:\n"

output << @comment

end

reset_state

output

end

private

def reset_state

@comment = ""

@expecting_class_name = false

end

end

BabyRDoc.new(File.read(__FILE__)).parse(STDOUT)

Produces:

BabyRDoc:

This class handles parser events, extracting

comments and attaching them to class definitions

Library RSS: RSS Feed Generation and Parsing

Rich Site Summary or RDF Site Summary or Really Simple Syndication—take your pick. RSS is the protocol of choice for disseminating news on the Internet. The Ruby RSS library supports creating and parsing streams compliant with RSS 0.9, RSS 1.0, and RSS 2.0.

  • Reads and summarizes the latest stories from http://ruby-lang.org :

require 'rss/2.0'

require 'open-uri'

open('http://ruby-lang.org/en/feeds/news.rss') do |http|

response = http.read

result = RSS::Parser.parse(response, false)

puts "Channel: " + result.channel.title

result.items.each_with_index do |item, i|

puts "#{i+1}. #{item.title}" if i < 3

end

end

  • Produces:

Channel: Ruby News

1. Ruby 1.9.3-p429 is released

2. Ruby 2.0.0-p195 is released

3. Object taint bypassing in DL and Fiddle in Ruby (CVE-2013-2065)

  • Generates some RSS information:

require 'rss/0.9'

rss = RSS::Rss.new("0.9")

chan = RSS::Rss::Channel.new

chan.title = "The Daily Dave"

chan.description = "Dave's Feed"

chan.language = "en-US"

chan.link = "http://pragdave.pragprog.com"

rss.channel = chan

image = RSS::Rss::Channel::Image.new

image.url = "http://pragprog.com/pragdave.gif"

image.title = "PragDave"

image.link = chan.link

chan.image = image

3.times do |i|

item = RSS::Rss::Channel::Item.new

item.title = "My News Number #{i}"

item.link = "http://pragprog.com/pragdave/story_#{i}"

item.description = "This is a story about number #{i}"

chan.items << item

end

puts rss.to_s

Library Scanf: Input Format Conversion

Implements a version of the C library scanf function, which extracts values from a string under the control of a format specifier.

The Ruby version of the library adds a scanf method to both class IO and class String. The version in IO applies the format string to the next line read from the receiver. The version in String applies the format string to the receiver. The library also adds the global method Object#scanf, which uses as its source the next line of standard input.

Scanf has one main advantage over using regular expressions to break apart a string: a regular expression extracts strings, whereas scanf will return objects converted to the correct type.

  • Splits a date string into its constituents:

require 'scanf'

date = "2010-12-15"

year, month, day = date.scanf("%4d-%2d-%2d")

year # => 2010

month # => 12

day # => 15

year.class # => Fixnum

  • The block form of scanf applies the format multiple times to the input string, returning each set of results to the block. The numbers are returned as integers, not strings:

require 'scanf'

data = "cat:7 dog:9 cow:17 walrus:31"

data.scanf("%[^:]:%d ") do |animal, value|

puts "A #{animal.strip} has #{value}"

end

  • Produces:

A cat has 7

A dog has 9

A cow has 17

A walrus has 31

  • Extracts hex numbers:

require 'scanf'

data = "decaf bad"

data.scanf("%3x%2x%x") # => [3564, 175, 2989]

Library SDBM: Interface to SDBM Database

The SDBM database implements a simple key/value persistence mechanism. Because the underlying SDBM library itself is provided with Ruby, there are no external dependencies, and SDBM should be available on all platforms supported by Ruby. SDBM database keys and values must be strings. SDBM databases are effectively hashlike.

  • DBM
  • GDBM

The example that follows stores a record in a new database and then fetches it back. Unlike the DBM library, all values to SDBM must be strings (or implement to_str ).

require 'sdbm'

require 'date'

SDBM.open("data.dbm") do |dbm|

dbm['name'] = "Walter Wombat"

dbm['dob'] = Date.new(1997, 12,25).to_s

dbm['uses'] = "Ruby"

end

SDBM.open("data.dbm", nil) do |dbm|

p dbm.keys

p dbm['dob']

end

Produces:

["name", "dob", "uses"]

"1997-12-25"

Library SecureRandom: Access to Secure Random Number Generators

Provides access to one of your operating system’s secure random number generators. If the OpenSSL library is installed, the module uses its random_bytes method. Otherwise, the module looks for and uses /dev/urandom or the CryptGenRandom method in the Windows API.

  • Generates some random numbers:

require 'securerandom'

# Random floats such that 0.0 <= rand < 1.0

SecureRandom.random_number(0) # => 0.26256698786247024

SecureRandom.random_number(0) # => 0.6885743213737645

# Random integers such that 0 <= rand < 1000

SecureRandom.random_number(1000) # => 112

SecureRandom.random_number(1000) # => 273

  • Generates ten random bytes, returning the result as a hex string, a Base64 string, and a string of binary data. A different random string is returned for each call.

require 'securerandom'

SecureRandom.hex(10) # => "bf4262e94d093ffbb4a7"

SecureRandom.base64(10) # => "X/8YpCbCEyO2zA=="

SecureRandom.random_bytes(10) # => "\x7FO\0r\r\xC1?\xB7b#"

Library Set: Implement Various Forms of Set

A Set is a collection of unique values (where uniqueness is determined using eql? and hash ). Convenience methods let you build sets from enumerable objects.

  • Basic set operations:

require 'set'

set1 = Set.new([:bear, :cat, :deer])

set1.include?(:bat) # => false

set1.add(:fox) # => #<Set: {:bear, :cat, :deer, :fox}>

partition = set1.classify {|element| element.to_s.length }

partition # => {4=>#<Set: {:bear, :deer}>, 3=>#<Set: {:cat, :fox}>}

set2 = [ :cat, :dog, :cow ].to_set

set1 | set2 # => #<Set: {:bear, :cat, :deer, :fox, :dog, :cow}>

set1 & set2 # => #<Set: {:cat}>

set1 - set2 # => #<Set: {:bear, :deer, :fox}>

set1 ^ set2 # => #<Set: {:dog, :cow, :bear, :deer, :fox}>

  • Partitions the users in our /etc/passwd file into subsets where members of each subset have adjacent user IDs:

require 'etc'

require 'set'

users = []

Etc.passwd {|u| users << u }

related_users = users.to_set.divide do |u1, u2|

(u1.uid - u2.uid).abs <= 1

end

related_users.each do |relatives|

relatives.each {|u| print "#{u.uid}/#{u.name} " }

puts "\n======="

end

  • Produces:

235/_assetcache 234/_krb_anonymous 233/_krb_kerberos 232/_krb_changepw

231/_krb_kadmin 230/_krb_krbtgt 229/_avbdeviced 228/_netstatistics 227/_dovenull

=======

93/_calendar 92/_securityagent 91/_tokend

=======

202/_coreaudiod 203/_screensaver 201/Guest 200/_softwareupdate

=======

...

Library Shellwords: Manipulate Shell Lines Using POSIX Semantics

Given a string representative of a shell command line, splits it into word tokens according to POSIX semantics. Also allows you to create properly escaped shell lines from individual words.

  • Spaces between double or single quotes are treated as part of a word.
  • Double quotes may be escaped using a backslash.
  • Spaces escaped by a backslash are not used to separate words.
  • Otherwise, tokens separated by whitespace are treated as words.

require 'shellwords'

include Shellwords

line = %{Code Ruby Be Happy!}

shellwords(line) # => ["Code", "Ruby", "Be", "Happy!"]

line = %{"Code Ruby" 'Be Happy'!}

shellwords(line) # => ["Code Ruby", "Be Happy!"]

line = %q{Code\ Ruby "Be Happy"!}

shellwords(line) # => ["Code Ruby", "Be Happy!"]

shelljoin(["Code Ruby", "Be Happy"]) # => Code\ Ruby Be\ Happy

In addition, the library adds shellsplit and shelljoin methods to classes String and Array, respectively:

require 'shellwords'

include Shellwords

%{Code\\ Ruby Be Happy!}.shellsplit # => ["Code Ruby", "Be", "Happy!"]

["Code Ruby", "Be Happy"].shelljoin # => "Code\\ Ruby Be\\ Happy"

Library Singleton: The Singleton Pattern

The Singleton design pattern ensures that only one instance of a particular class may be created for the lifetime of a program (see Design Patterns [GHJV95]).

The singleton library makes this simple to implement. Mix the Singleton module into each class that is to be a singleton, and that class’s new method will be made private. In its place, users of the class call the method instance , which returns a singleton instance of that class.

In this example, the two instances of MyClass are the same object:

require 'singleton'

class MyClass

attr_accessor :data

include Singleton

end

a = MyClass.instance # => #<MyClass:0x007feb190604d0>

b = MyClass.instance # => #<MyClass:0x007feb190604d0>

a.data = 123 # => 123

b.data # => 123

a.object_id # => 70323856933480

b.object_id # => 70323856933480

Library Socket: IP, TCP, Unix, and SOCKS Socket Access

The socket extension defines nine classes for accessing the socket-level communications of the underlying system. All of these classes are (indirect) subclasses of class IO, meaning that IO’s methods can be used with socket connections.

The hierarchy of socket classes reflects the reality of network programming and hence is somewhat confusing. The BasicSocket class largely contains methods common to data transfer for all socket-based connections. It is subclassed to provide protocol-specific implementations: IPSocket and UNIXSocket (for domain sockets). These in turn are subclassed by TCPSocket, UDPSocket, and SOCKSSocket.

BasicSocket is also subclassed by class Socket, which is a more generic interface to socket-oriented networking. Although classes such as TCPSocket are specific to a protocol, Socket objects can, with some work, be used regardless of protocol.

TCPSocket, SOCKSSocket, and UNIXSocket are each connection oriented. Each has a corresponding xxxxServer class, which implements the server end of a connection.

The socket libraries are something that you may never use directly. However, if you do use them, you’ll need to know the details. For that reason, we’ve put a reference section online at http://pragprog.com/book/ruby3/programming-ruby-1-9?tab=tab-contents .

The following code shows a trivial UDP server and client:

# Simple logger prints messages received on UDP port 12121

require 'socket'

socket = UDPSocket.new

socket.bind("127.0.0.1", 12121)

loop do

msg, sender = socket.recvfrom(100)

host = sender[3]

puts "#{Time.now}: #{host} '#{msg}'"

STDOUT.flush

end

# Exercise the logger

require 'socket'

log = UDPSocket.new

log.connect("127.0.0.1", 12121)

log.print "Up and Running!"

# process ... process ..

log.print "Done!"

Produces:

2013-05-27 12:33:39 -0500: 127.0.0.1 'Up and Running!'

2013-05-27 12:33:39 -0500: 127.0.0.1 'Done!'

Library StringIO: Treat Strings as IO Objects

In some ways, the distinction between strings and file contents is artificial: the contents of a file are basically a string that happens to live on disk, not in memory. The StringIO library aims to unify the two concepts, making strings act as if they were opened IO objects. Once a string is wrapped in a StringIO object, it can be read from and written to as if it were an open file. This can make unit testing a lot easier. It also lets you pass strings into classes and methods that were originally written to work with files. StringIO objects take their encoding from the string you pass in or the default external encoding is that no string is passed.

  • Reads and writes from a string:

require 'stringio'

sio = StringIO.new("time flies like an arrow")

sio.read(5) # => "time "

sio.read(5) # => "flies"

sio.pos = 19

sio.read(5) # => "arrow"

sio.rewind # => 0

sio.write("fruit") # => 5

sio.pos = 16

sio.write("a banana") # => 8

sio.rewind # => 0

sio.read # => "fruitflies like a banana"

  • Uses StringIO as a testing aid:

require 'stringio'

require 'csv'

require 'test/unit'

class TestCSV < Test::Unit::TestCase

def test_simple

StringIO.open do |op|

CSV(op) do |csv|

csv << [ 1, "line 1", 27 ]

csv << [ 2, nil, 123 ]

end

assert_equal("1,line 1,27\n2,,123\n", op.string)

end

end

end

  • Produces:

Run options:

# Running tests:

.

Finished tests in 0.004047s, 247.0966 tests/s, 247.0966 assertions/s.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

ruby -v: ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-darwin12.2.0]

Library StringScanner: Basic String Tokenizer

StringScanner objects progress through a string, matching (and optionally returning) tokens that match a given pattern. Unlike the built-in scan methods, StringScanner objects maintain a current position pointer in the string being examined, so each call resumes from the position in the string where the previous call left off. Pattern matches are anchored to this previous point.

  • Implements a simple language:

require 'strscan'

# Handle the language:

# set <var> = <value>

# get <var>

values = {}

while line = gets

scanner = StringScanner.new(line.chomp)

scanner.scan(/(get|set)\s+/) or fail "Missing command"

cmd = scanner[1]

var_name = scanner.scan(/\w+/) or fail "Missing variable"

case cmd

when "get"

puts "#{var_name} => #{values[var_name].inspect}"

when "set"

scanner.skip(/\s+=\s+/) or fail "Missing '='"

value = scanner.rest

values[var_name] = value

else

fail cmd

end

end

  • Run this from the command line, typing in phrases from the language:

$ ruby strscan.rb

set a = dave

set b = hello

get b

b => "hello"

get a

a => "dave"

Library Syslog: Interface to Unix System Logging

Unix system with syslog

The Syslog class is a simple wrapper around the Unix syslog(3) library. It allows messages to be written at various severity levels to the logging daemon, where they are disseminated according to the configuration in syslog.conf. Ruby 2.0 adds support for Syslog::Logger, which is compatible with the Logger API.«2.0»

The following examples assume the log file is /var/log/system.log.

  • Adds to our local system log. We’ll log all the levels configured for the user facility for our system (which is every level except debug and info messages).

require 'syslog'

log = Syslog.open("test") # "test" is the app name

log.debug("Warm and fuzzy greetings from your program")

log.info("Program starting")

log.notice("I said 'Hello!'")

log.warning("If you don't respond soon, I'm quitting")

log.err("You haven't responded after %d milliseconds", 7)

log.alert("I'm telling your mother...")

log.emerg("I'm feeling totally crushed")

log.crit("Aarrgh....")

system("tail -6 /var/log/system.log")

  • Produces:

Sep 16 12:48:44 dave-4 test[35121]: Warm and fuzzy greetings from your program

Sep 16 12:48:44 dave-4 test[35121]: Program starting

Sep 16 12:48:44 dave-4 test[35121]: I said 'Hello!'

Sep 16 12:48:44 dave-4 test[35121]: If you don't respond soon, I'm quitting

Sep 16 12:48:44 dave-4 test[35121]: You haven't responded after 7 milliseconds

Sep 16 12:48:44 dave-4 test[35121]: I'm telling your mother...

Sep 16 12:48:44 dave-4 test[35121]: I'm feeling totally crushed

Sep 16 12:48:44 dave-4 test[35121]: Aarrgh....

  • Logs only errors and above:

require 'syslog'

log = Syslog.open("test")

log.mask = Syslog::LOG_UPTO(Syslog::LOG_ERR)

log.debug("Warm and fuzzy greetings from your program")

log.info("Program starting")

log.notice("I said 'Hello!'")

log.warning("If you don't respond soon, I'm quitting")

log.err("You haven't responded after %d milliseconds", 7)

log.alert("I'm telling your mother...")

log.emerg("I'm feeling totally crushed")

log.crit("Aarrgh....")

system("tail -4 /var/log/system.log")

  • Produces:

Sep 16 12:48:44 dave-4 test[35124]: You haven't responded after 7 milliseconds

Sep 16 12:48:44 dave-4 test[35124]: I'm telling your mother...

Sep 16 12:48:44 dave-4 test[35124]: I'm feeling totally crushed

Sep 16 12:48:44 dave-4 test[35124]: Aarrgh....

Library Tempfile: Temporary File Support

Class Tempfile creates managed temporary files. Although they behave the same as any other IO objects, temporary files are automatically deleted when the Ruby program terminates. Once a Tempfile object has been created, the underlying file may be opened and closed a number of times in succession.

Tempfile does not directly inherit from IO. Instead, it delegates calls to a File object. From the programmer’s perspective, apart from the unusual new , open, and close semantics, a Tempfile object behaves as if it were an IO object.

If you don’t specify a directory to hold temporary files when you create them, the tmpdir library will be used to find a system-dependent location.

  • tmpdir

require 'tempfile'

tf = Tempfile.new("afile")

tf.path # => "/var/folders/44/j19_ml3n3dx7bwrb_qmbcjyc0000gn/T/afile20130527-24

# .. 867-1greefy"

tf.puts("Cosi Fan Tutte")

tf.close

tf.open

tf.gets # => "Cosi Fan Tutte\n"

tf.close(true)

Library Test::Unit: Unit Testing Framework

Test::Unit is a unit testing framework based on the original SUnit Smalltalk framework. It provides a structure in which unit tests may be organized, selected, and run. Tests can be run from the command line or using one of several GUI-based interfaces.

Chapter 13, Unit Testing contains a tutorial on Test::Unit.

Maybe we have a simple playlist class, designed to store and retrieve songs:

require_relative 'song.rb'

require 'forwardable'

class Playlist

extend Forwardable

def_delegator(:@list, :<<, :add_song)

def_delegators(:@list, :size, :empty?)

def initialize

@list = []

end

def find(title)

@list.find {|song| song.title == title}

end

end

We can write unit tests to exercise this class. The Test::Unit framework is smart enough to run the tests in a test class if no main program is supplied.

require 'test/unit'

require_relative 'playlist.rb'

class TestPlaylist < Test::Unit::TestCase

def test_adding

pl = Playlist.new

assert_empty(pl)

assert_nil(pl.find("My Way"))

pl.add_song(Song.new("My Way", "Sinatra"))

assert_equal(1, pl.size)

s = pl.find("My Way")

refute_nil(s)

assert_equal("Sinatra", s.artist)

assert_nil(pl.find("Chicago"))

# .. and so on

end

end

Produces:

Run options:

# Running tests:

.

Finished tests in 0.004140s, 241.5459 tests/s, 1690.8213 assertions/s.

1 tests, 7 assertions, 0 failures, 0 errors, 0 skips

ruby -v: ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-darwin12.2.0]

Library thread: Utility Functionality for Threading

The thread library adds some utility functions and classes for supporting threads. Much of this has been superseded by the Monitor class, but the thread library contains two classes, Queue and SizedQueue, that are still useful. Both classes implement a thread-safe queue that can be used to pass objects between producers and consumers in multiple threads. The Queue object implements a unbounded queue. A SizedQueue is told its capacity; any producer that tries to add an object when the queue is at that capacity will block until a consumer has removed an object.

The following example was provided by Robert Kellner. It has three consumers taking objects from an unsized queue. Those objects are provided by two producers, which each add three items.

require 'thread'

queue = Queue.new

consumers = (1..3).map do |i|

Thread.new("consumer #{i}") do |name|

begin

obj = queue.deq

print "#{name}: consumed #{obj.inspect}\n"

enduntil obj == :END_OF_WORK

end

end

producers = (1..2).map do |i|

Thread.new("producer #{i}") do |name|

3.times do |j|

queue.enq("Item #{j} from #{name}")

end

end

end

producers.each(&:join)

consumers.size.times { queue.enq(:END_OF_WORK) }

consumers.each(&:join)

Produces:

consumer 1: consumed "Item 0 from producer 1"

consumer 1: consumed "Item 1 from producer 1"

consumer 1: consumed "Item 2 from producer 1"

consumer 1: consumed "Item 0 from producer 2"

consumer 2: consumed "Item 1 from producer 2"

consumer 3: consumed "Item 2 from producer 2"

consumer 1: consumed :END_OF_WORK

consumer 3: consumed :END_OF_WORK

consumer 2: consumed :END_OF_WORK

Library ThreadsWait: Wait for Multiple Threads to Terminate

Class ThreadsWait handles the termination of a group of thread objects. It provides methods to allow you to check for termination of any managed thread and to wait for all managed threads to terminate.

The following example kicks off a number of threads that each wait for a slightly shorter length of time before terminating and returning their thread number. Using ThreadsWait, we can capture these threads as they terminate, either individually or as a group.

require 'thwait'

group = ThreadsWait.new

# construct threads that wait for 1 second, .9 second, etc.

# add each to the group

9.times do |i|

thread = Thread.new(i) {|index| sleep 1.0 - index/10.0; index }

group.join_nowait(thread)

end

# any threads finished?

group.finished? # => false

# wait for one to finish

group.next_wait.value # => 8

# wait for 5 more to finish

5.times { group.next_wait } # => 5

# wait for next one to finish

group.next_wait.value # => 2

# and then wait for all the rest

group.all_waits # => nil

Library Time: Extended Functionality for Class Time

The time library adds functionality to the built-in class Time, supporting date and/or time formats used by RFC 2822 (e-mail), RFC 2616 (HTTP), and ISO 8601 (the subset used by XML schema).

require 'time'

# Convert external formats into Time objects

Time.rfc2822("Thu, 1 Apr 2010 16:32:45 CST") # => 2010-04-01 17:32:45 -0500

Time.rfc2822("Thu, 1 Apr 2010 16:32:45 -0600") # => 2010-04-01 17:32:45 -0500

Time.httpdate("Thu, 01 Apr 2010 16:32:45 GMT") # => 2010-04-01 11:32:45 -0500

Time.httpdate("Thursday, 01-Apr-04 16:32:45 GMT") # => 2004-04-01 16:32:45 UTC

Time.httpdate("Thu Apr 1 16:32:45 2010") # => 2010-04-01 16:32:45 UTC

Time.xmlschema("2010-04-01T16:32:45") # => 2010-04-01 16:32:45 -0500

Time.xmlschema("2010-04-01T16:32:45.12-06:00") # => 2010-04-01 22:32:45 UTC

# Convert time objects into external formats

Time.now.rfc2822 # => "Mon, 27 May 2013 12:33:41 -0500"

Time.now.httpdate # => "Mon, 27 May 2013 17:33:41 GMT"

Time.now.xmlschema # => "2013-05-27T12:33:41-05:00"

Library Timeout: Run a Block with Timeout

The Timeout.timeout method takes a parameter representing a timeout period in seconds, an optional exception parameter, and a block. The block is executed, and a timer is run concurrently. If the block terminates before the timeout, timeout returns the value of the block. Otherwise, the exception (default Timeout::Error) is raised.

require 'timeout'

for snooze in 1..2

puts "About to sleep for #{snooze}"

begin

Timeout::timeout(1.5) do |timeout_length|

puts "Timeout period is #{timeout_length}"

sleep(snooze)

puts "That was refreshing"

end

rescue Timeout::Error

puts "Woken up early!!"

end

end

Produces:

About to sleep for 1

Timeout period is 1.5

That was refreshing

About to sleep for 2

Timeout period is 1.5

Woken up early!!

Be careful when using timeouts—you may find them interrupting system calls that you cannot reliably restart, resulting in possible data loss.

Library Tk: Wrapper for Tcl/Tk

Tk library installed

Of all the Ruby options for creating GUIs, the Tk library is probably the most widely supported, running on Windows, Linux, Mac OS X, and other Unix-like platforms.[130] Although it doesn’t produce the prettiest interfaces, Tk is functional and relatively simple to program.

sl_tk/curves.rb

# encoding: utf-8

require 'tk'

include Math

def plot(val)

Integer(val * 180 + 200)

end

TkRoot.new do |root|

title "Curves"

geometry "400x400"

TkCanvas.new(root) do |canvas|

width 400

height 400

pack('side'=>'top', 'fill'=>'both', 'expand'=>'yes')

points = [ ]

a = 2

b = 3

0.0.step(8, 0.1) do |t|

x = Math.sin(a*t)

y = Math.cos(b*t)

points << plot(x) << plot(y)

end

TkcLine.new(canvas, *(points), smooth: 'on', width: 10, fill: 'blue')

end

end

Tk.mainloop

produces:

Library tmpdir: System-Independent Temporary Directory Location

The tmpdir library adds the tmpdir method to class Dir. This method returns the path to a temporary directory that should be writable by the current process. (This will not be true if none of the well-known temporary directories is writable and if the current working directory is also not writable.) Candidate directories include those referenced by the environment variables TMPDIR, TMP, TEMP, and USERPROFILE; the directory /tmp; and (on Windows boxes) the temp subdirectory of the Windows or System directory.

require 'tmpdir'

Dir.tmpdir # => "/var/folders/44/j19_ml3n3dx7bwrb_qmbcjyc0000gn/T"

ENV['TMPDIR'] = "/wibble" # doesn't exist

ENV['TMP'] = "/sbin" # not writable

ENV['TEMP'] = "/Users/dave/tmp" # just right

Dir.tmpdir # => "/Users/dave/tmp"

The mktmpdir method can be used to create a new temporary directory:

require 'tmpdir'

name = Dir.mktmpdir

# .. process, process, process ..

Dir.rmdir(name)

Library Tracer: Trace Program Execution

The tracer library uses Object#set_trace_func to trace all or part of a Ruby program’s execution. The traced lines show the thread number, file, line number, class, event, and source line. The events shown are - for a change of line, < for a call, > for a return, C for a class definition, and E for the end of a definition.

  • You can trace an entire program by including the tracer library from the command line:

class Account

def initialize(balance)

@balance = balance

end

def debit(amt)

if @balance < amt

fail "Insufficient funds"

else

@balance -= amt

end

end

end

acct = Account.new(100)

acct.debit(40)

$ ruby -r tracer account.rb

#0:prog.rb:15::-: acct = Account.new(100)

#0:prog.rb:3:Account:>: def initialize(balance)

#0:prog.rb:4:Account:-: @balance = balance

#0:prog.rb:5:Account:<: end

#0:prog.rb:16::-: acct.debit(40)

#0:prog.rb:6:Account:>: def debit(amt)

#0:prog.rb:7:Account:-: if @balance < amt

#0:prog.rb:10:Account:-: @balance -= amt

#0:prog.rb:12:Account:<: end

  • You can also use tracer objects to trace just a portion of your code and use filters to select what to trace:

require 'tracer'

tracer = Tracer.new

tracer.add_filter lambda {|event, *rest| event == "line" }

acct = Account.new(100)

tracer.on { acct.debit(40) }

  • Produces:

#0:prog.rb:18::-: tracer.on { acct.debit(40) }

#0:prog.rb:6:Account:-: if @balance < amt

#0:prog.rb:9:Account:-: @balance -= amt

Library TSort: Topological Sort

Given a set of dependencies between nodes (where each node depends on zero or more other nodes and there are no cycles in the graph of dependencies), a topological sort will return a list of the nodes ordered such that no node follows a node that depends on it. One use for this is scheduling tasks, where the order means that you complete the dependencies before you start any task that depends on them. The make program uses a topological sort to order its execution.

In the Ruby implementation, you mix in the TSort module and define two methods: tsort_each_node , which yields each node in turn, and tsort_each_child , which, given a node, yields each of that node’s dependencies.

  • Given the set of dependencies among the steps for making a piña colada, what is the optimum order for undertaking the steps?

require 'tsort'

class Tasks

include TSort

def initialize

@dependencies = {}

end

def add_dependency(task, *relies_on)

@dependencies[task] = relies_on

end

def tsort_each_node(&block)

@dependencies.each_key(&block)

end

def tsort_each_child(node, &block)

deps = @dependencies[node]

deps.each(&block) if deps

end

end

tasks = Tasks.new

tasks.add_dependency(:add_rum, :open_blender)

tasks.add_dependency(:add_pc_mix, :open_blender)

tasks.add_dependency(:add_ice, :open_blender)

tasks.add_dependency(:close_blender, :add_rum, :add_pc_mix, :add_ice)

tasks.add_dependency(:blend_mix, :close_blender)

tasks.add_dependency(:pour_drink, :blend_mix)

tasks.add_dependency(:pour_drink, :open_blender)

puts tasks.tsort

  • Produces:

open_blender

add_rum

add_pc_mix

add_ice

close_blender

blend_mix

pour_drink

Library un: Command-Line Interface to FileUtils

Why un? When you invoke it from the command line with the -r option to Ruby, it spells -run. This pun gives a hint as to the intent of the library: it lets you run commands (in this case, a subset of the methods in FileUtils) from the command line. In theory this gives you an operating system--independent set of file manipulation commands, possibly useful when writing portable Makefiles.

  • FileUtils
  • The available commands are as follows:

$ ruby -run -e cp -- <options>* source dest $ ruby -run -e ln -- <options>* target linkname $ ruby -run -e mv -- <options>* source dest $ ruby -run -e rm -- <options>* file $ ruby -run -e mkdir -- <options>* dirs $ ruby -run -e rmdir --<options>* dirs $ ruby -run -e install -- <options>* source dest $ ruby -run -e chmod -- <options>* octal_mode file $ ruby -run -e touch -- <options>* file

Note the use of -- to tell the Ruby interpreter that options to the program follow.

You can get a list of all available commands with this:

$ ruby -run -e help

For help on a particular command, append the command’s name:

$ ruby -run -e help mkdir

Library URI: RFC 2396 Uniform Resource Identifier (URI) Support

URI encapsulates the concept of a Uniform Resource Identifier (URI), a way of specifying some kind of (potentially networked) resource. URIs are a superset of URLs: URLs (such as the addresses of web pages) allow specification of addresses by location, and URIs also allow specification by name.

URIs consist of a scheme (such as http, mailto, ftp, and so on), followed by structured data identifying the resource within the scheme.

URI has factory methods that take a URI string and return a subclass of URI specific to the scheme. The library explicitly supports the ftp, http, https, ldap, and mailto schemes; others will be treated as generic URIs. The module also has convenience methods to escape and unescape URIs. The class Net::HTTP accepts URI objects where a URL parameter is expected.

  • open-uri
  • Net::HTTP

require 'uri'

uri = URI.parse("http://pragprog.com:1234/mypage.cgi?q=ruby")

uri.class # => URI::HTTP

uri.scheme # => "http"

uri.host # => "pragprog.com"

uri.port # => 1234

uri.path # => "/mypage.cgi"

uri.query # => "q=ruby"

uri = URI.parse("mailto:ruby@pragprog.com?Subject=help&body=info")

uri.class # => URI::MailTo

uri.scheme # => "mailto"

uri.to # => "ruby@pragprog.com"

uri.headers # => [["Subject", "help"], ["body", "info"]]

uri = URI.parse("ftp://dave@anon.com:/pub/ruby;type=i")

uri.class # => URI::FTP

uri.scheme # => "ftp"

uri.host # => "anon.com"

uri.port # => 21

uri.path # => "pub/ruby"

uri.typecode # => "i"

Library WeakRef: Support for Weak References

In Ruby, objects are not eligible for garbage collection if references still exist to them. Normally, this is a Good Thing—it would be disconcerting to have an object simply evaporate while you were using it. However, sometimes you may need more flexibility. For example, you might want to implement an in-memory cache of commonly used file contents. As you read more files, the cache grows. At some point, you may run low on memory. The garbage collector will be invoked, but the objects in the cache are all referenced by the cache data structures and so will not be deleted.

A weak reference behaves like any normal object reference with one important exception—the referenced object may be garbage collected, even while references to it exist. In the cache example, if the cached files were accessed using weak references, once memory runs low, they will be garbage collected, freeing memory for the rest of the application.

  • Weak references introduce a slight complexity. Because the object referenced can be deleted by garbage collection at any time, code that accesses these objects must take care to ensure that the references are valid. Two techniques can be used. First, the code can reference the objects normally. Any attempt to reference an object that has been garbage collected will raise a WeakRef::RefError exception.

require 'weakref'

# Generate lots of small strings. Hopefully the early ones will have

# been garbage collected...

refs = (1..10000).map {|i| WeakRef.new("#{i}") }

puts "Last element is #{refs.last}"

puts "First element is #{refs.first}"

  • Produces:

Last element is 10000

prog.rb:6:in `<main>': Invalid Reference - probably recycled (WeakRef::RefError)

  • Alternatively, use theWeakRef#weakref_alive? method to check that a reference is valid before using it. Garbage collection must be disabled during the test and subsequent reference to the object. In a single-threaded program, you could use something like this:

ref = WeakRef.new(some_object)

# .. some time later

gc_was_disabled = GC.disable

if ref.weakref_alive?

# do stuff with 'ref'

end

GC.enable unless gc_was_disabled

Library WEBrick: Web Server Toolkit

WEBrick is a pure-Ruby framework for implementing HTTP-based servers. The Ruby standard library includes WEBrick services that implement a standard web server (serving files and directory listings) and servlets supporting CGI, erb, file download, and the mounting of Ruby lambdas.

The Web programming chapter has more examples of WEBrick.

  • The following code mounts two Ruby procs on a web server.

Requests to http://localhost:2000/hello run one proc, and the other proc is invoked by requests to http://localhost:2000/bye.

#!/usr/bin/ruby

require 'webrick'

include WEBrick

hello_proc = lambda do |req, resp|

resp['Content-Type'] = "text/html"

resp.body = %{

<html><body>

Hello. You're calling from a #{req['User-Agent']}

<p>

I see parameters: #{req.query.keys.join(', ')}

</body></html>

}

end

bye_proc = lambda do |req, resp|

resp['Content-Type'] = "text/html"

resp.body = %{

<html><body>

<h3>Goodbye!</h3>

</body></html>

}

end

hello = HTTPServlet::ProcHandler.new(hello_proc)

bye = HTTPServlet::ProcHandler.new(bye_proc)

s = HTTPServer.new(:Port => 2000)

s.mount("/hello", hello)

s.mount("/bye", bye)

trap("INT"){ s.shutdown }

s.start

Library WIN32OLE: Windows Automation

Windows

This is an interface to Windows automation, allowing Ruby code to interact with Windows applications. The Ruby interface to Windows is discussed in more detail in Chapter 21, Ruby and Microsoft Windows.

  • Opens Internet Explorer and asks it to display our home page:

require 'win32ole'

ie = WIN32OLE.new('InternetExplorer.Application')

ie.visible = true

ie.navigate("http://www.pragprog.com")

  • Creates a new chart in Microsoft Excel and then rotates it. This code is one of the samples that comes with the library.

require 'win32ole'

# -4100 is the value for the Excel constant xl3DColumn.

ChartTypeVal = -4100;

# Creates OLE object to Excel

#excel = WIN32OLE.new("excel.application.5")

excel = WIN32OLE.new("excel.application")

# Create and rotate the chart

excel.visible = TRUE;

excel.Workbooks.Add();

excel.Range("a1").value = 3;

excel.Range("a2").value = 2;

excel.Range("a3").value = 1;

excel.Range("a1:a3").Select();

excelchart = excel.Charts.Add();

excelchart.type = ChartTypeVal;

i = 30

i.step(180, 10) do |rot|

# excelchart['Rotation'] = rot;

excelchart.rotation=rot;

end

# Done, bye

excel.ActiveWorkbook.Close(0);

excel.Quit();

Library XMLRPC: Remote Procedure Calls using XML-RPC

XMLRPC allows clients to invoke methods on networked servers using the XML-RPC protocol. Communications take place over HTTP. The server may run in the context of a web server, in which case ports 80 or 443 (for SSL) will typically be used. The server may also be run stand-alone. The Ruby XML-RPC server implementation supports operation as a CGI script, as a mod_ruby script, as a WEBrick handler, and as a stand-alone server. Basic authentication is supported, and clients can communicate with servers via proxies. Servers may throw FaultException errors—these generate the corresponding exception on the client (or optionally may be flagged as a status return to the call).

  • dRuby
  • WEBrick
  • The following simple server accepts a temperature in Celsius and converts it to Fahren­heit. It runs within the context of the WEBrick web server.

sl_xmlrpc/xmlserver.rb

require 'webrick'

require 'xmlrpc/server'

xml_servlet = XMLRPC::WEBrickServlet.new

xml_servlet.add_handler("convert_celcius") do |celcius|

celcius*1.8 + 32

end

xml_servlet.add_multicall # Add support for multicall

server = WEBrick::HTTPServer.new(:Port => 2000)

server.mount("/RPC2", xml_servlet)

trap("INT"){ server.shutdown }

server.start

  • This client makes calls to the temperature conversion server. Note that in the output we show both the server’s logging and the client program’s output.

require 'xmlrpc/client'

server = XMLRPC::Client.new("localhost", "/RPC2", 2000)

puts server.call("convert_celcius", 0)

puts server.call("convert_celcius", 100)

puts server.multicall(['convert_celcius', -10], ['convert_celcius', 200])

  • Produces:

[2013-05-27 12:33:44] INFO WEBrick 1.3.1

[2013-05-27 12:33:44] INFO ruby 2.0.0 (2013-02-24) [x86_64-darwin12.2.0]

[2013-05-27 12:33:44] INFO WEBrick::HTTPServer#start: pid=24895 port=2000

localhost - - [27/May/2013:12:33:45 CDT] "POST /RPC2 HTTP/1.1" 200 124

- -> /RPC2

localhost - - [27/May/2013:12:33:45 CDT] "POST /RPC2 HTTP/1.1" 200 125

- -> /RPC2

localhost - - [27/May/2013:12:33:45 CDT] "POST /RPC2 HTTP/1.1" 200 290

- -> /RPC2

32.0

212.0

14.0

392.0

Library YAML: Object Serialization/Deserialization

The YAML library (also described in the tutorial) serializes and deserializes Ruby object trees to and from an external, readable, plain-text format. YAML can be used as a portable object marshaling scheme, allowing objects to be passed in plain text between separate Ruby processes. In some cases, objects may also be exchanged between Ruby programs and programs in other languages that also have YAML support. While Ruby 1.9.2 can use libyaml if it is available, Ruby 2.0«2.0 makes it a requirement, and bundles it with the interpreter.

  • json
  • YAML can be used to store an object tree in a flat file:

require 'yaml'

tree = { name: 'ruby',

uses: [ 'scripting', 'web', 'testing', 'etc' ]

}

File.open("tree.yml", "w") {|f| YAML.dump(tree, f)}

  • Once stored, it can be read by another program:

require 'yaml'

tree = YAML.load_file("tree.yml")

tree[:uses][1] # => "web"

  • The YAML format is also a convenient way to store configuration information for programs. Because it is readable, it can be maintained by hand using a normal editor and then read as objects by programs. For example, a configuration file may contain the following:

sl_yaml/config.yml

---

username: dave

prefs:

background: dark

foreground: cyan

timeout: 30

  • We can use this in a program:

require 'yaml'

config = YAML.load_file("code/sl_yaml/config.yml")

config["username"] # => "dave"

config["prefs"]["timeout"] * 10 # => 300

Library Zlib: Read and Write Compressed Files

zlib library available

The Zlib module is home to a number of classes for compressing and decompressing streams and for working with gzip-format compressed files. They also calculate zip checksums.

  • Compresses /etc/passwd as a gzip file and then reads the result back:

require 'zlib'

# These methods can take a filename

Zlib::GzipWriter.open("passwd.gz") do |gz|

gz.write(File.read("/etc/passwd"))

end

system("ls -l /etc/passwd passwd.gz")

puts

# or a stream

File.open("passwd.gz") do |f|

gzip = Zlib::GzipReader.new(f)

data = gzip.read.split(/\n/)

puts data[15,3]

end

  • Produces:

-rw-r--r-- 1 root wheel 5086 Jul 20 2011 /etc/passwd

-rw-rw-r-- 1 dave staff 1621 May 27 12:33 passwd.gz

_installassistant:*:25:25:Install Assistant:/var/empty:/usr/bin/false

_lp:*:26:26:Printing Services:/var/spool/cups:/usr/bin/false

_postfix:*:27:27:Postfix Mail Server:/var/spool/postfix:/usr/bin/false

  • Compresses data sent between two processes:

require 'zlib'

rd, wr = IO.pipe

if fork

rd.close

zipper = Zlib::Deflate.new

zipper << "This is a string "

data = zipper.deflate("to compress", Zlib::FINISH)

wr.write(data)

wr.close

Process.wait

else

wr.close

text = Zlib.inflate(rd.read)

puts "We got: #{text}"

end

  • Produces:

We got: This is a string to compress

Footnotes

[125]

http://www.faqs.org/rfcs/rfc2045.html and http://www.faqs.org/rfcs/rfc4648.html

[126]

http://www.gnu.org/software/gdbm/gdbm.html

[127]

http://www.ietf.org/rfc/rfc4627.txt

[128]

http://www.openssl.org

[129]

Currently found at http://www.codeforpeople.com/lib/ruby/session/

[130]

All these environments require that the Tcl/Tk libraries are installed before the Ruby Tk extension can be used.