Ruby Phrasebook
Ruby Phrasebook
Ruby Phrasebook
com
CONTENTS AT A GLANCE
1 Converting Between Types 5
2 Working with Strings 19
3 Working with Collections 35
4 Working with Objects 49
5 Working with Pipes 61
6 Working with Files 69
7 Manipulating Text 77
8 Ruby One-Liners 83
9 Processing XML 91
10 Rapid Applications Development
with GUI Toolkits 107
11 Simple CGI Forms 127
12 Connecting to Databases 143
13 Working with Networking and
Sockets 151
14 Working with Threads 163
15 Documenting Your Ruby 175
16 Working with Ruby Packages 185
www.allitebooks.com
Ruby
P H R A S E B O O K
Jason Clinton
www.allitebooks.com
Ruby Phrasebook Editor-in-Chief
Copyright © 2009 by Pearson Education, Inc. Mark Taub
All rights reserved. No part of this book shall be reproduced, Development
Editor
stored in a retrieval system, or transmitted by any means,
Michael Thurston
electronic, mechanical, photocopying, recording, or otherwise,
without written permission from the publisher. No patent Managing Editor
liability is assumed with respect to the use of the information Patrick Kanouse
contained herein. Although every precaution has been taken in Project Editor
the preparation of this book, the publisher and author assume Jennifer Gallant
no responsibility for errors or omissions. Nor is any liability Copy Editor
assumed for damages resulting from the use of the information Geneil Breeze/
contained herein. Krista Hansing
ISBN-13: 978-0-672-32897-8 Indexer
ISBN-10: 0-672-32897-6 Tim Wright
Library of Congress Cataloging-in-Publication Data: Proofreader
2005938020 Carla Lewis/
Leslie Joseph
Printed in the United States of America
Technical Editor
First Printing August 2008 Robert Evans
Trademarks Publishing
All terms mentioned in this book that are known to be trade- Coordinator
marks or service marks have been appropriately capitalized. Vanessa Evans
Pearson Education, Inc. cannot attest to the accuracy of this Multimedia
information. Use of a term in this book should not be regarded Developer
as affecting the validity of any trademark or service mark. Dan Scherf
Book Designer
Warning and Disclaimer Gary Adair
Every effort has been made to make this book as complete
and as accurate as possible, but no warranty or fitness is
implied. The information provided is on an “as is” basis.
The author and the publisher shall have neither liability nor
responsibility to any person or entity with respect to any loss or
damages arising from the information contained in this book.
Bulk Sales
Pearson Education, Inc. offers excellent discounts on this book
when ordered in quantity for bulk purchases or special sales.
For more information, please contact
U.S. Corporate and Government Sales
1-800-382-3419
corpsales@pearsontechgroup.com
For sales outside of the U.S., please contact
International Sales
international@pearson.com
www.allitebooks.com
Table of Contents
Introduction 1
Audience 1
How to Use This Book 2
Conventions 2
Acknowledgments 4
www.allitebooks.com
iv Contents
7 Manipulating Text 77
Parsing an LDIF 77
Parsing a Simple Config File 78
Interpolating One Text File 79
Sorting the Contents of a File 80
Processing a passwd File 81
8 Ruby One-Liners 83
Simple Search 84
Counting Lines in a File 84
Head or Tail of a File 84
MD5 or SHA1 Hash 85
Simple HTTP Fetch 86
Simple TCP Connect 87
Escaping HTML 87
Deleting Empty Directories 88
Adding Users from a Text File 88
Delete All the Files Just Extracted 89
9 Processing XML 91
Opening an XML File 92
Accessing an Element (Node) 93
Getting a List of Attributes 95
Adding an Element 96
Changing an Element’s Enclosed Text 97
Deleting an Element 98
Adding an Attribute 98
Changing an Attribute 99
Deleting an Attribute 99
Escaping Characters for XML 100
vi Contents
Index 195
About the Author
Jason Clinton has been working in the computer
industry for more than a decade. He is actively
involved in the Kansas City Ruby Users Group
(KCRUG), serving as administrator of the group’s
website and mailing list, and he teaches a community
class on Linux at University of Missouri-Kansas City.
Clinton uses Ruby daily in system administration and
development for Advanced Clustering Technologies, a
Linux Beowulf cluster integrator.
Acknowledgments
Without the Pragmatic Programmers’ freely available
first edition of Programming Ruby, I would have never
discovered the wonderful world of Ruby. The Pickaxe
books and the great Ruby community make projects
like this one possible.
Thanks to my loving partner, Brandon S.Ward, for his
infinite patience while I was working on this book.
We Want to Hear from You!
As the reader of this book, you are our most important
critic and commentator.We value your opinion and
want to know what we’re doing right, what we could
do better, what areas you’d like to see us publish in,
and any other words of wisdom you’re willing to pass
our way.
You can email or write me directly to let me know
what you did or didn’t like about this book—as well as
what we can do to make our books stronger.
Please note that I cannot help you with technical problems
related to the topic of this book, and that due to the high
volume of mail I receive, I might not be able to reply to
every message.
When you write, please be sure to include this book’s
title and author as well as your name and phone
number or email address. I will carefully review your
comments and share them with the author and editors
who worked on the book.
Email: feedback@developers-library.info
Mail: Mark Taber
Pearson Education, Inc.
800 East 96th Street
Indianapolis, IN 46240 USA
Reader Services
Visit our website and register this book at
informit.com/register for convenient access to any
updates, downloads, or errata that might be available
for this book.
Introduction
Audience
You can find some great Ruby books on the market.
If you are new to Ruby, a friend or someone on the
Internet has probably already listed some favorite
Ruby books—and you should buy those books. But
every book has its niche: Each attempts to appeal to a
certain need of a programmer.
It is my belief that the best thing this book can do for
you is show you the code. I promise to keep the chat to a
minimum, to focus instead on the quality and quantity
of actual Ruby code. I’ll also keep as much useful
information in as tight a space as is possible.
Unlike any other book on the market at the time of
this writing, this book is intended to be a (laptop-bag)
“pocket-size” resource that enables you to quickly
look up a topic and find examples of practical Ruby
code—a topical quick reference, if you will. In each of
the topics covered, I try to provide as thorough an
approach to each task as the size allows for; there’s not
as much room for coverage of topical solutions as there
is in much larger books with similar goals, such as The
Ruby Way, 2nd Edition (Sams, 2006), by Hal Fulton.
Because of this, other issues that are often given equal
priority are relegated to second. For instance, this is
2 Ruby Phrasebook
Conventions
Phrases throughout the book are put in dark gray
boxes at the beginning of every topic.
Acknowledgments
Without the Pragmatic Programmers’ freely available
1st Edition of Programming Ruby, I would have never
discovered the wonderful world of Ruby.The Pickaxe
books and the great Ruby community are what make
projects like this one possible.
Thanks to my loving partner, Brandon S.Ward, for his
infinite patience while working on this book.
Reporting Errata
Readers will almost certainly find topics that they wish
were covered which we were overlooked when plan-
ning this book. I encourage you to please contact us
and let us know what you would like to see included
in later editions. Criticisms are also welcome. Contact
information can be found in the front-matter of this
book.
1
Converting
Between Types
class String
def to_real
if self.include? '.'
self.to_f
else
self.to_i
end
end
end
'123'.to_real
#=> 123
'123.0'.to_real
#=> 123.0
'1.23e10'.to_real
#=> 12300000000.0
Number from a String 7
'test'.each_byte do |byte|
puts '%02x' % byteend
8 CHAPTER 1 Converting Between Types
Sample output:
74
65
73
74
'foobar'[4]
#=> 97 (this is the ASCII value of ‘a’)
'foobar'[4,0]
#=> "" (a null string)
'foobar'[4..200]
#=> "ar" (no out of range error)
/\d\d:\d\d/.inspect
#=> "/\\d\\d:\\d\\d/"
/\d\d:\d\d/.source
#=> "\\d\\d:\\d\\d"
/\d\d:\d\d/.to_s
#=> "(?-mix:\\d\\d:\\d\\d)"
Hash[*%w{a b c}.zip([]).flatten]
#=> {"a"=>nil, "b"=>nil, "c"=>nil}
my_hash = Hash.new
['a','b','c'].each_with_index { |e, i| my_hash[e] =
i }
my_hash
#=> {"a"=>0, "b"=>1, "c"=>2}1
class Array
def to_hash
Hash[*self.zip([]).flatten]
end
end
[1,2,3].to_hash
#=> {1=>nil, 2=>nil, 3=>nil}
Conversely, you can get the keys and values out of the
Hash in a number of ways including those short meth-
ods listed at the beginning of this section.
However, you might also want to walk through the
Hash and get an Array from keys or values that meet
certain criteria:
Math::PI
#=> 3.14159265358979
Math::PI.to_i
#=> 3
Math::PI.ceil
#=> 4
Math::PI.floor
#=> 3
Math::PI.round
#=> 3
16 CHAPTER 1 Converting Between Types
Math::E
#=> 2.71828182845905
Math::E.round
#=> 3
7.to_f
#=> 7.0
(2**29).class
#=> Fixnum
(2**30).class
#=> Bignum
require 'rational'
Rational(3, 4) + Rational(1, 8)
#=> Rational(7, 8)
Rational(3, 4) / 2
#=> Rational(3, 8)
Floating-Point, Integer, and Rational Numbers 17
2 * Rational(3, 8)
#=> Rational(3, 4)
Rational(3, 7).to_f()
#=> 0.428571428571429
Rational(3, 7).to_s()
#=> "3/7"
Searching Strings
'foobar'.include? 'fo'
#=> true
'foobar'['fo']
#=> "fo "(true because not nil or false)
'foobar'.count 'ob' # 'ob' is taken to be a list of
characters
#=> 3 (2 "o"s and 1 "b")
'foobar'.count 'ob', 'o'
#=> 2 (only "o" appeared in both parameters)
'foobar'.index 'ob'
#=> 2
'foobar'.index 98 # 98 is the ASCII code for 'b'
#=> 3
You can also access the entire match with m[0], and
each of the submatches with m[1] to m[9].
If you want to return all the matches in the String, use
.#scan.This returns an Array that looks like this:
Replacing Substrings
s = 'foobar'
s[-1] = 'z'
s #=> "foobaz"
s[0,4] = 'ja'
s #=> "jaaz"
s[2] = 122
s #=> "jazz"
ary = ['some_variable','some_value']
Produces:
ц и т р у с
Produces:
цитрус
? ? ? ? ? ?
Sanitizing Input
new_password = gets
if new_password.count '^A-Za-z._' != 0 then
puts "Bad Password"
else
#do something like in subsection “Encrypting a
String"
end
28 CHAPTER 2 Working with Strings
"a\r\nb\r\nc\r\n".each_line { |line|
puts(line.inspect)
}
Produces:
"a\r\n"
"b\r\n"
"c\r\n"
"a\r\nb\r\nc\r\n".each_line { |line|
puts(line.chomp.inspect) # chomp safely removes
both
}
Produces:
"a"
"b"
"c"
"a\nb\nc\n".each_line { |line|
puts(line.inspect)
}
30 CHAPTER 2 Working with Strings
Produces:
"a\n"
"b\n"
"c\n"
Produces:
"a\r"
"b\r"
"c\r"
require 'stringio'
string_stream = StringIO.new my_string
string_stream.read 256
#=> "\351@\300g\251\326\036\314| *\335jJ\017 ...
Comparing Strings
"Who" <=> "who"
#=> -1
%w{'who' 'is' 'on' 'first?' 'Who'}.sort
#=> ["Who", "first?", "is", "on", "who"]
'foobar'.casecmp 'Foobaz'
#=> -1
'foobar'.casecmp 'FooBar'
#=> 0
%w{'who' 'is' 'on' 'first?' 'Who'}.sort { |a,b|
a.casecmp b }
#=> ["first?", "is", "on", "who", "Who"]
require 'digest/md5'
Encrypting a String
password = 'f00bar'
password.crypt(salt)
#=> "jM7qRC1ulBPhc"
input_password = 'f00bar'
crypted_password = 'jM7qRC1ulBPhc'
salt = crypted_password[0,2]
#=> "jM"
# test password
input_password.crypt(salt) == crypted_password
Slicing an Array
This section has a lot of analogs to the earlier section
“String to Array and Back Again,” in Chapter 1,
“Converting Between Types.”You can slice an Array a
number of ways:
36 CHAPTER 3 Working with Collections
[1, 2, 3, 4, 5, 6, 7, 8, 9][4]
#=> 5 (a Fixnum object)
[1, 2, 3, 4, 5, 6, 7, 8, 9][4,1]
#=> [5] (single element Array)
[1, 2, 3, 4, 5, 6, 7, 8, 9][4,2]
#=> [5, 6]
[1, 2, 3, 4, 5, 6, 7, 8, 9][-4,4]
#=> [6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9][2..5]
#=> [3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7, 8, 9][-4..-1]
#=> [6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9][2...5]
#=> [3, 4, 5]
[1, 2, 3, 4, 5, 6, 7, 8, 9][-4...-1]
#=> [6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9][4..200]
#=> [5, 6, 7, 8, 9] (no out of range error!)
require ‘enumerator’
ary = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ary.each_slice(5) { |element| p element }
Outputs:
[0, 1, 2, 3, 4]
[5, 6, 7, 8, 9]
require 'enumerator'
class NumberStore
include Enumerable
def each
@neg_nums.each { |i| yield i }
@pos_nums.each { |i| yield i }
end
def initialize
@neg_nums = []
@pos_nums = []
end
end
mystore = NumberStore.new
mystore.add 5
mystore.add 87
mystore.add(-92)
mystore.add(-1)
p mystore.neg_nums
p mystore.pos_nums
p mystore.grep -50..60
Produces:
[-92, -1]
[5, 87]
[-1, 5]
Sorting an Array
[5, 2, 1, 4, 3].sort
#=> [1, 2, 3, 4, 5]
Produces:
ArgumentError: comparison of Fixnum with Float
failed
class Array
def each_recur(&block)
each do |elem|
if elem.is_a? Array
elem.each_recur &block
else
block.call elem
end
end
end
end
Produces:
1 2 3 4 5 6 7 8
42 CHAPTER 3 Working with Collections
class Array
def collect_recur(&block)
collect do |e|
if e.is_a? Array
e.collect_recur(&block)
else
block.call(e)
end
end
end
end
their elements for either all elements being less than all
elements in the other Array or vice-versa (if neither
condition is met they are considered equal). It doesn’t
descend in to the Arrays to sort them. Here is what
would happen if we didn’t flatten:
Once again, the first code will work in most cases but a
recursive implementation is able to accommodate
working with the Array in place without destroying the
heirarchy (note that this sorts in place, for simplicity):
class Array
def sort_recur!
sort! do |a,b|
a.sort_recur! if a.is_a? Array
b.sort_recur! if b.is_a? Array
a <=> b
end
end
end
Produces:
[[1, 4, 9], [16, 25, 36]]
44 CHAPTER 3 Working with Collections
p tmp_ary
p my_hash
Produces:
[“c”, “3”] (from the last loop)
{“a”=>”1”, “c”=>”3”, “b”=>”2”}
variable1 = foo
variable2 = bar
variable3 = baz
my_hash.sort
#=> [[“a”, “1”], [“b”, “2”], [“c”, “3”]]
require ‘set’
myset = Set::new [1, 1, 2, 3, 4, 4]
#=> #<Set: {1, 2, 3, 4}>
Working with Nested Sets 47
myset.add 4
#=> #<Set: {1, 2, 3, 4}>
class Set
def each_recur(&block)
each do |elem|
if elem.is_a? Set
elem.each_recur(&block)
else
block.call(elem)
end
end
end
end
3, 4}>}>
Produces:
1 2 3 4 1 2 3 4
4
Working with
Objects
n Kernel
50 CHAPTER 4 Working with Objects
n Class
n Module
# Instance inspections
my_string = String.new
my_string.instance_methods
my_string.constants
my_string.instance_variables
my_string.object_id
Ruby-Style Polymorphisms
(“Duck Typing”)
def second_item(obj)
return obj[1] if obj.respond_to? :[]
end
def second(obj)
return obj[1] if obj.kind_of? Array
end
second_item mystring
#=> 101 # the ASCII code for ‘e’
Comparing Objects
a = 42
b = 42
a == b
#=> true
a.object_id == b.object_id #=> false
a = 42
b = a
a == b
#=> true
a.object_id == b.object_id
#=> true
Serializing Objects
h = { “a” => 123 }
Marshal.dump h
#=> “\004\b{\006\”\006ai\001{“
Marshal.load “\004\b{\006\”\006ai\001{“ #=>
{“a”=>123}
Duplication
a = Hash.new
b = a.dup
a.object_id
#=> -605558818
b.object_id
#=> -605579038
d = c.dup
#=> {“foo”=>{“bar”=>”baz”}}
d = Marshal.load(Marshal.dump(c))
#=> {“foo”=>{“bar”=>”baz”}}
Garbage Collecting
GC.start
Using Symbols
method(:foobar).call()
def foobar
# pass
end
n = 500000
Benchmark.bmbm do |bench|
bench.report(‘Symbol’) do
n.times { method(:foobar).call() }
end
bench.report(‘String’) do
n.times { method(‘foobar’).call() }
end
end
class Foobar
def initialize n
@inst_var = n
end
my_attr_reader :inst_var
end
a = Foobar.new ‘baz’
puts a.inst_var
DEBUG = true
# ...
STDERR.puts ‘An event of some kind has occurred.’ if
DEBUG
Determining Interactive
Standard Pipes
if STDIN.tty?
puts ‘Press RETURN to continue.’
STDIN.readline
end
On your system, you may find that the progress bar does
not immediately render each update.To fix that use:
STDOUT.sync
Yeah, ugly.
This page intentionally left blank
6
Working with Files
unless f.closed?
f.rewind
puts f.gets
# ...
end
ensure
f.close unless f.closed?
end
search = “foo”
File.open(‘rubbish.svg’, ‘r’) do |file|
while chunk = file.read 1024 if pos =
chunk.index(search)
pos = file.pos – (1024 – pos)
file.seek((pos – 40), IO::SEEK_SET)
puts file.read(80 + search.size)
file.seek((pos + search.size), IO::SEEK_SET)
else
file.seek(-search.size, IO::SEEK_CUR)
end
end
end
# remove directory
rmdir(directory, options)
# move file
mv(source, destination, options)
force
rm(list, options)
rm_r(list, options)
rm_rf(list, options)
Parsing an LDIF
current_entry = {}
all_entries = []
file = File.open ‘test.ldif’
file.each_line do |line|
if line[0,1] == ‘ ‘ or line[0,1] == “\t”
current_entry[$1] += line.chomp[1..-1]
else
/(.+): (.+)/ =~ line
if ( $1 == nil and $2 == nil )
all_entries.push current_entry.dup unless
current_entry == {}
current_entry = {}
else
current_entry[$1] = $2
end
end
end
p all_entries
78 CHAPTER 7 Manipulating Text
dn: baztar
test: something2
baztar: line continuation with a
tab
baztag: line continuation with a
space
config = {}
config[“globals”] = {}
insert_point = “globals”
while gets do
if $_.match /\[(.+?)\]/
insert_point = $1
config[insert_point] = {}
elsif $_.match /(.+?)=(.+)/
config[insert_point][$1.strip] = $2.strip
end
end
pp config
Interpolating One Text File into Another 79
[stanza1]
stanza1 var = boo
[stanza2]
stanza2 var = baz
str = File.read(‘template.txt’)
config.keys.each do |key|
str.gsub! “%#{key}%”, config[key]
end
ary = []
Simple Search
ruby -n -e ‘print “LINE #{$.}: #{$_}” if /Jason/i’\
public_html/index.html
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transi-
tional.dtd”>
</body>
</html>
(blank line)However, a much better and faster imple-
mentation uses the operating system’s file-seek meth-
ods to intelligently jump to the end of the file. It can
be found in the experimental module called file-tail
available in the Ruby Application Archives. Here is a sam-
ple of using this module:
require ‘file/tail’
# ...
File::Tail::Logfile.open (‘public_html/index.html’)
do |file|
file.rewind(5).tail { |line| puts line }
end
Escaping HTML
ruby -0777 -n -rcgi -e ‘print CGI.escapeHTML($_)’\
public_html/index.html
You can merge this file into your *nix passwd database
and create home directories by invoking this one-liner.
Delete All the Files Just Extracted 89
<?xml version='1.0'?>
<!DOCTYPE html PUBLIC "-//W3C//DTD ...
<html xmlns='http://www.w3.org/1999/xhtml'>
<head>
<title>mail.jasonclinton.com</title>
...
Or, you can single out the element by its order (such
as I show here using the [2]):
myxml.elements['html/body/p[2]/a']
#=> <a href='mailto:root@jasonclinton.com'> …
You can also use the XPath support to access the first
element that matches. In the previous example, I
specifically ask for the second paragraph by using a
[2]. If there were more than one <a> in that paragraph,
only the first would return.
"href"
"mailto:root@jasonclinton.com"
myxml.elements['//a'].attributes['href']
#=> "mailto:root@jasonclinton.com"
Finally, you can also access the text values (the stuff
between <tag> … </tag>) of an Element:
myxml.elements['//a'].text
#=> root@jasonclinton.com
Adding an Element
myxml.elements['html/body'].add_element 'p'
#=> <p/>
myxml.elements['html/body'].\
add_element('p', {'id'=>'thanks'}).\
text = 'Thank you for visiting!'
Changing an Element’s
Enclosed Text
myxml.elements['html/body/p[3]'].add_text ' '
myxml.elements['html/body/p[3]'].add_element('a',
{'href'=>'/'}).text = 'Home'
myxml.elements['html/body/p[3]'].\
add_text ' Come again!'
print myxml.to_s
Deleting an Element
myxml.delete_element 'html/body/p[2]/a'
# is equivalent to
myxml.elements['html/body/p[2]'].delete_element 'a'
Adding an Attribute
myxml.elements['html/body/p[2]/a'].add_attribute(
'id', 'emaillink')
Escaping Characters for XML 99
Changing an Attribute
myxml.elements['html/body/p[2]/a'].\
attributes['id'] = 'somelink'
Deleting an Attribute
myxml.elements['html/body/p[2]/a'].\
delete_attribute 'id'
Evaluates to:
...:root@jasonclinton.com'>root@jasonc...
Produces
<rootnode><<illegal characters>>
</rootnode>
<xsl:element name="body">
<xsl:for-each select="html/body/p">
<paragraph>
<xsl:apply-templates />
</paragraph>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="a">
<xsl:element name="link">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0"?>
<document>
<title>mail.jasonclinton.com</title>
<body>
<paragraph>This server is host to a number
of projects, all of which are not publicly
accessible. Perhaps one day I will make
myself a nice home page on the
web.</paragraph>
response = http.get('/rss/rss.php?id=1001')
myxml = REXML::Document.new response.body
puts "NPR News Stories"
puts '---------------------------------------------'
myxml.each_element('rss/channel/item') do |elem|
puts elem.elements['title'].text
end
require 'gtk2'
Gtk.init
vbox = Gtk::VBox.new 2
button = Gtk::Button.new 'Hello World'
entry = Gtk::Entry.new
entry.text = 'Hello?'
window = Gtk::Window.new 'A Hello World App.'
window.border_width = 5
window.add vbox
vbox.pack_start button
vbox.pack_start entry
window.show_all
110 CHAPTER 10 Rapid Applications Development with GUI
Toolkits
window.signal_connect("destroy") do
Gtk.main_quit
end
button.signal_connect("clicked") do
entry.text = 'Hello! Hello!'
end
Gtk.main
Using Glade
$ ruby-glade-create-template helloworld.glade >
helloworld.rb
require 'libglade2'
Gtk.init
def on_hello_button_clicked
@hello_entry.text = 'Hello! Hello!'
end
@glade = GladeXML.new("/home/jclinton/Projects/pr\
oject1/project1.glade", 'window1') { |handler|
method(handler)
}
@hello_entry = @glade.get_widget 'hello_entry'
@window1 = @glade.get_widget("window1")
@window1.signal_connect('destroy') {
Gtk.main_quit
}
Gtk.main
@vgroup.addWidget @button
@vgroup.addWidget @edit
self.setLayout @vgroup
end
def button_clicked()
Attaching a Signal Handler to a Qt Widget Slot 117
Using Qt Designer
$ rbuic -x -o qttest.rb qttest.ui
require 'Qt'
class Ui_window
attr_reader :qvboxLayout
attr_reader :button
attr_reader :edit
def setupUi(window)
window.setObjectName("window")
window.resize(Qt::Size.\
new(115,78).expandedTo\
(window.minimumSizeHint()
))
@qvboxLayout = Qt::VBoxLayout.new(window)
@qvboxLayout.spacing = 6
@qvboxLayout.margin = 9
@qvboxLayout.setObjectName("qvboxLayout")
@button = Qt::PushButton.new(window)
@button.setObjectName("button")
@qvboxLayout.addWidget(@button)
@edit = Qt::LineEdit.new(window)
@edit.setObjectName("edit")
@qvboxLayout.addWidget(@edit)
retranslateUi(window)
Qt::MetaObject.connectSlotsByName(window)
end # setupUi
def retranslateUi(window)
window.setWindowTitle(Qt::Application.\
translate(
Using Qt Designer 123
"window",
"Form",
nil,
Qt::Application::UnicodeUTF8
))
@button.setText(Qt::Application.translate(
"window",
"Hello World!",
nil,
Qt::Application::UnicodeUTF8
))
@edit.setText(Qt::Application.translate(
"window",
"Hello?",
nil,
Qt::Application::UnicodeUTF8
))
end # retranslateUi
end
if $0 == __FILE__
a = Qt::Application.new(ARGV)
u = Ui_window.new
w = Qt::Widget.new
u.setupUi(w)
w.show
a.exec
end
<Files *.rhtml>
SetHandler ruby-object
RubyHandler Apache::ERubyRun.instance
</Files>
</IfModule>
<h2>Processing submission:</h2>
<p>Your message
<blockquote><%= cgi['message'] %></blockquote>
has been saved to file
<%
File.open('/tmp/message.txt', 'w') do |f|
f.write cgi['message']
print f.path
end
%>
.</p>
</body>
</html>
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 St...
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>localhost mailbox</title>
</head>
<body>
</div>
</form>
</body>
</html>
...
<form method="post" action="cgi-bin/submit.rhtml">
<div>
...
<h2>Processing submission:</h2>
n += 1
print "<tr><th>#{n}</th><td>#{line}</td></tr>"
end
%>
</table>
</body>
</html>
<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 St...
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>table lines of a file</title>
</head>
<body>
<h2>Iterate by line.</h2>
</div>
</form>
</body>
</html>
Line one.
Line two.
Line three.
Line four. Hurray!
Escaping Input
require 'cgi'
mystring = 'This is some hello/goodbye world text?'
CGI.unescape encstring
#=> "This is some hello/goodbye world text?"
Escaping Input 135
require 'cgi'
mystring = '<script "scriptkiddie.js" />
Ow3z0r!</script>'
CGI.unescapeHTML encstring
#=> "<script \"scriptkiddie.js\" />
Ow3z0r!</script>"
<h2>Saving file.</h2>
f << uploaded.read
end %>
<p>Save completed.</p>
</body>
</html>
<h2>Processing submission:</h2>
<% data = []
File.open('/tmp/times.txt', 'r') do |f|
f.each_line do |line|
data.push line.to_f
end
end
plotcmd = ''
Gnuplot::Plot.new(plotcmd) do |plot|
plot.add_data Gnuplot::DataSet.new(data)
plot.terminal 'svg'
end
<p>
<% time = Time.now - Apache.request.request_time;
puts "This request took #{time}. Added to end of
record."%>
</p>
</body>
</html>
140 CHAPTER 11 Simple CGI Forms
handler = DBI.connect('dbi:Mysql:test:localhost',
'user', 'pass')
handler.disconnect
Creating a Table
require 'dbi'
DBI.connect('dbi:mysql:test:localhost', 'root',
'password') do | handler |
query = <<-QUERY
CREATE TABLE `vegetables` (
`ID` INT( 8 ) NOT NULL AUTO_INCREMENT
PRIMARY KEY ,
`type` VARCHAR( 255 ) NOT NULL ,
`vendor` VARCHAR( 255 ) NOT NULL
) ENGINE = MYISAM ;
QUERY
handler.do(query)
end
["vegetables"]
["vendors"]
Deleting Rows
DBI.connect('dbi:mysql:test:localhost', 'root',
'password') do | handler |
handler.do("DELETE FROM `vegetables`\
WHERE `ID` =2 LIMIT 1 ; ")
end
Deleting a Table
require 'dbi'
DBI.connect('dbi:mysql:test:localhost', 'root',
'password') do | handler |
handler.do('DROP TABLE `vegetables`')
end
Unless you are sure that the response will be short, you
do not want to use #recv without a parameter. #recv
will read until the socket terminates. If the response is
long—perhaps megabytes—your program will hard-
block until that operation finishes. As mentioned in
Chapter 14, “Working with Threads,” this is a symptom
of Ruby not having “real” threading capabilities.
sock_events[0].each do |sock|
if sock == STDIN
puts 'Server shutdown by console input.'
exit 1
elsif sock == server
Thread.new(server.accept) do |client|
client.send 'You are '
client.send client.peeraddr[2]
client.send '\n'
client.close
end
end
end
end
Trying 127.0.0.1...
Connected to localhost.
if sock == server
# add a new client handle to track
# by using server.accept
else
# it's a client so do client stuff
end
end
end
156 CHAPTER 13 Working with Networking and Sockets
matrix = [[0,1,4],
[9,5,2],
[3,8,7]]
require 'yaml'
require 'socket'
require 'pp'
Received a Array
[[0, 1, 4], [9, 5, 2], [3, 8, 7]]
class Die
def roll(sides)
return rand(sides)
end
end
mydice = Die.new
DRb.start_service('druby://:7777', mydice)
DRb.thread.join
require 'drb'
DRb.start_service
remotedice =
DRbObject.new(nil, 'druby://localhost:7777')
Using Net::HTTP
require 'net/http'
Net::HTTP.start('www.gnome.org') do |httpsock|
rsp = httpsock.get('/robots.txt')
puts rsp.body
end
require 'net/http'
Net::HTTP.start('www.gnome.org') do |httpsock|
rsp = httpsock.get('/foobar')
if rsp.code != 200
puts "Server error was: #{rsp.code}"
end
end
Using Webrick
require 'webrick'
server = WEBrick::HTTPServer.new(
:Port => 1234,
:DocumentRoot => Dir.pwd)
trap 'INT' do
server.shutdown
end
server.start
The last point is likely the one you often will run
headlong in to. For example, if you write a GUI appli-
cation that reads data from the network and you do
not make careful implementation decisions, the UI
may appear to “freeze” while Ruby is waiting to read
164 CHAPTER 14 Working with Threads
Creating a Thread
@return_value = 0
@magicStuff = Thread::new do
puts 'A long process goes here.'
sleep 5
@return_value = 255
end
# do other stuff while other thread is running ...
# now we want to wait for the thread to finish
@magicStuff.join
puts "The return code was #{@return_value}."
Using a Timer
@pid = fork do
puts 'A long process goes here.'
sleep 25
exit 255
end
@timer = Thread.new { sleep 3 }
loop do
if Process::wait @pid, Process::WNOHANG:
puts "The exit code was #{$?.exitstatus}."
@timer.kill
break
elsif not @timer.alive?:
puts 'Timed out; killing subprocess.'
Process::kill 'SIGTERM', @pid
break
166 CHAPTER 14 Working with Threads
end
sleep 1
end
@timer.join
require 'timeout'
include Timeout
class OuterTimeout < Timeout::Error
end
class InnerTimeout < Timeout::Error
end
begin
timeout 10, OuterTimeout do
begin
loop do
timeout 5, InnerTimeout do
# simulate some work
loop {}
end
end
rescue InnerTimeout
puts 'Inner Expired'
retry
end
end
rescue OuterTimeout
puts 'Outer Expired'
end
Killing a Thread
@threadA = Thread::new do
begin
puts 'Thread A does some work.'
sleep 5
ensure
puts 'Clean up code for A goes here.'
end
end
@threadB = Thread::new do
begin
puts 'Thread B does some work.'
sleep 5
ensure
puts 'Clean up code for B goes here.'
end
end
@threadA.kill
@threadB.kill!
Synchronizing Thread
Communication
require 'thread'
require 'monitor'
light_switch = 0
light_switch_handle = Monitor.new
light_on_bureaucrat = Thread.new do
100000.times do
light_switch_handle.synchronize do
if light_switch == 0
light_switch += 1
end
end
end
end
light_off_bureaucrat = Thread.new do
100000.times do
light_switch_handle.synchronize do
if light_switch == 1
light_switch -= 1
end
end
end
end
light_on_bureaucrat.join
light_off_bureaucrat.join
puts light_switch
170 CHAPTER 14 Working with Threads
if light_switch == 0
light_switch += 1
end
require 'thread'
require 'monitor'
class LightSwitch
include MonitorMixin
Synchronizing Thread Communication 171
attr :state
def initialize
@state = 0
super
end
def switch_on
synchronize { @state += 1 }
end
def switch_off
synchronize { @state -= 1 }
end
end
light_switch = LightSwitch.new
light_on_bureaucrat = Thread.new do
100000.times do
if light_switch.state == 0:
light_switch.switch_on
end
end
end
light_off_bureaucrat = Thread.new do
100000.times do
if light_switch == 1:
light_switch.switch_off
end
end
end
light_on_bureaucrat.join
light_off_bureaucrat.join
puts light_switch.state
Multithreaded Exception
Gathering
require 'thread'
class SnakeEye < Exception
end
exception_queue = Queue.new
worker = Thread.new do
@counter = 0
begin
until @counter==100 do
@counter += 1
sleep 0.02
if rand(6) == 0
raise SnakeEye.new(
"Snake Eye at #{@counter}!")
end
end
rescue SnakeEye => e
exception_queue.enq e
retry
end
end
while worker.alive? or
not exception_queue.empty? do
# Check for waiting exceptions every second.
# Report them to the user if found.
until exception_queue.empty? do
e = exception_queue.deq
puts e
end
sleep 1
end
worker.join
Snake Eye at 4!
Snake Eye at 9!
Snake Eye at 12!
Snake Eye at 18!
Snake Eye at 19!
(and so on)
or
=begin rdoc
This is my *Foo* class!
=end
class Foo
=begin rdoc
This is my *Foo* method, *Foo#bar*
It does lots of interesting things to
instances.
=end
def bar
end
=begin rdoc
This is my *Foo* class method, *Foo.baz*
It touches class variables.
=end
def Foo.baz
end
end
# :yield:
# event_to_handle
#
# My listen method!
def listen &event_handler
#... some kind of IO listener here
yield received_event
end
180 CHAPTER 15 Documenting Your Ruby
require 'rdoc/usage'
require 'optparse'
user_options = OptionParser.new
user_options.on('-h', '--help') { RDoc.usage 1 }
user_options.parse ARGV
Usage:
example.rb [--help]
begin
user_options.parse ARGV
rescue
RDoc.usage 1
end
Generating HTML
Documentation
$ rdoc
$ rdoc specific.rb
require 'rexml/document'
##
# RSSSimple, an example
#
module RSSSimple
##
# This method takes an RSS document and
# outputs plain text with links.
#
def RSSSimple::prettyprint(rssdata)
myxml = REXML::Document.new rssdata.body
myxml.each_element('rss/channel/item') do |e|
Installing a Module 187
puts e.elements['title'].text
puts "=> #{e.elements['link'].text}"
end
end
end
Installing a Module
gem install rails
Removing a Module
gem uninstall rails
Updating Modules
gem update
Examining a Module
gem unpack modulename
gem fetch modulename
gem specification modulename
require 'rubygems'
require 'hoe'
require './lib/rsssimple.rb'
190 CHAPTER 16 Working with Ruby Packages
# vim: syntax=Ruby
require 'net/http'
http = Net::HTTP.new 'www.npr.org'
http.open_timeout = 30
http.start
raise "Connection failed." unless http
response = http.get('/rss/rss.php?id=1001')
RSSSimple::prettyprint(response)
PKG_NAME = 'rsssimple'
PKG_VERSION = RSSSimple::VERSION
PKG_FILES = []
Rake::RDocTask.new do |rd|
rdoc_files = []
Find.find('lib/') do |file|
rdoc_files << file if file =~ /.*rb$/
end
rd.rdoc_files.include(rdoc_files)
rd.options << '--all'
end
p.package_files = PKG_FILES
end
begin
require 'rubygems'
require 'hoe'
# vim: syntax=Ruby
E F
elements (XML) false, 17
adding, 96 feeds, RSS, 104-105
attributes, 99 files
deleting, 98 binary mode (Win32),
enclosed text, when to use, 73
modifying, 97 closing, 69-70
198 files