String to Array and Back Again 10 String to Regular Expression and Back Again 12Floating-Point, Integer, and Rational Numbers 15 Checksumming a String MD5 or Otherwise 31... Without the
Trang 21 Converting Between Types 5
10 Rapid Applications Development
13 Working with Networking and
16 Working with Ruby Packages 185
Trang 4Copyright © 2009 by Pearson Education, Inc.
All rights reserved No part of this book shall be reproduced,
stored in a retrieval system, or transmitted by any means,
electronic, mechanical, photocopying, recording, or otherwise,
without written permission from the publisher No patent
liability is assumed with respect to the use of the information
contained herein Although every precaution has been taken in
the preparation of this book, the publisher and author assume
no responsibility for errors or omissions Nor is any liability
assumed for damages resulting from the use of the information
Printed in the United States of America
First Printing August 2008
Trademarks
All terms mentioned in this book that are known to be
trade-marks or service trade-marks have been appropriately capitalized.
Pearson Education, Inc cannot attest to the accuracy of this
information Use of a term in this book should not be regarded
as affecting the validity of any trademark or service mark.
Warning and Disclaimer
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
Trang 5String to Array and Back Again 10 String to Regular Expression and Back Again 12
Floating-Point, Integer, and Rational Numbers 15
Checksumming a String (MD5 or Otherwise) 31
Trang 63 Working with Collections 35
Modifying All the Values in an Array 42
Building a Hash from a Config File 44 Sorting a Hash by Key or Value 45 Eliminating Duplicate Data from Arrays (Sets) 46
Inspecting Objects and Classes 50 String Presentation of Objects 50 Ruby-Style Polymorphisms (“Duck Typing”) 51
Determining Interactive Standard Pipes 62 Synchronizing STDERR with STDOUT 63 Capturing the Output of a Child Process 64
Creating a Secured Password Prompt 66
Searching and Seeking Large File Contents 70
Trang 7When to Use Binary Mode (Win32) 73
Copying, Moving, and Deleting Files 74
Sorting the Contents of a File 80
Adding Users from a Text File 88 Delete All the Files Just Extracted 89
Trang 8Attaching a Signal Handler to a Qt
Attaching Signal Handlers to Qt Designer
Representing Data Graphically 138
Opening (and Closing) a MySQL Database
Trang 9Serializing Objects with YAML 156 Network Objects with Distributed Ruby 158
Typographic Conventions Used 177 Overriding Method Signatures in
Hiding a Module, Class, or Method 180 Providing Program Usage Help 180 Generating HTML Documentation 182 Generating and Installing Documentation for ri 183
Packaging Your Module with Hoe 189
Distributing Your Module on RubyForge 191
Trang 10About the Author
Jason Clinton has been working in the computerindustry for more than a decade He is activelyinvolved in the Kansas City Ruby Users Group(KCRUG), serving as administrator of the group’swebsite and mailing list, and he teaches a communityclass on Linux at University of Missouri-Kansas City.Clinton uses Ruby daily in system administration anddevelopment for Advanced Clustering Technologies, aLinux Beowulf cluster integrator
Trang 11Without the Pragmatic Programmers’ freely available
first edition of Programming Ruby, I would have never
discovered the wonderful world of Ruby The Pickaxebooks and the great Ruby community make projectslike this one possible
Thanks to my loving partner, Brandon S.Ward, for hisinfinite patience while I was working on this book
Trang 12We Want to Hear from You!
As the reader of this book, you are our most important
critic and commentator.We value your opinion andwant 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 passour way
You can email or write me directly to let me knowwhat you did or didn’t like about this book—as well aswhat 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’stitle and author as well as your name and phone number or email address I will carefully review yourcomments and share them with the author and editorswho 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 anyupdates, downloads, or errata that might be availablefor this book
Trang 13Audience
You can find some great Ruby books on the market
If you are new to Ruby, a friend or someone on theInternet 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 acertain 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 usefulinformation in as tight a space as is possible
Unlike any other book on the market at the time ofthis writing, this book is intended to be a (laptop-bag)
“pocket-size” resource that enables you to quicklylook up a topic and find examples of practical Rubycode—a topical quick reference, if you will In each ofthe topics covered, I try to provide as thorough anapproach 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 equalpriority are relegated to second For instance, this is
Trang 14not a tutorial; the code samples have some explanation,but I assume that you have a passing familiarity withthe language Also, when possible, I try to point outissues related to security and performance, but I make
no claim that these are my highest priority
I hope that you find this book a useful tool that you
keep next to your keyboard whenever you are
phrase-mongering in Ruby.
How to Use This Book
I have not intended for this book to be read cover tocover Instead, you should place your bookmark at theTable of Contents so you can open the book, find thetopic you are programming on at the moment, and goimmediately to a description of all the issues youmight run into
The content in the book is arranged by topic instead
of Ruby class.The benefit is that you can go to oneplace in this book instead of four or five areas inRuby’s own documentation (Not that there’s anythingwrong with Ruby’s documentation It’s just that some-times you are working with several classes at a timeand, because Ruby’s docs are arranged by class, youhave to jump around a lot.)
Trang 15Code snippets that appear in normal text are in italics.
All other code blocks, samples, and output appear as
follows:
# code sample boxes.
Parentheses are optional in Ruby in some cases—the
rule is: you must have parentheses in your method call if
you are calling another function in your list of
parame-ters, or passing a literal code block to the method In allother cases, parentheses are optional Personally, I’m a
sucker for consistency but one of the indisputable
strengths of Ruby is the flexibility of the syntax
In an attempt to have consistency between this book
and others, I will (reluctantly) use class_method()to
refer to class methods, ::class_variableto refer to classvariables,#method()to refer to instance methods, and
finally#varto refer to instance variables.When
refer-ring to variables and methods which are members of
the same class, I’ll use the appropriate @variableand
@@class_varriable
I know that some people might find these two rules
annoying—especially those coming from languages
that use the ‘::’ and ‘.’ notation everywhere In all ticality, you will never be so consistent—and rightfully
prac-so One of Ruby’s strengths is that there is a ton of
flexibility In fact, this flexibility has helped make Ruby
on Rails so popular.This allowed the creators of Rails
to make what appears to be a domain-specific language (a
language well-suited for a specific kind of work) for
web development But really, all that is going on is a
variation on Ruby syntax And this is one of the manyreasons that Ruby is more suitable for a given problemthan, say, Python Python’s rigidity (“there should be
Trang 16one—and preferably only one—obvious way to do it”)doesn’t lend itself to DSL, so the programmers in thatlanguage are forced to use other means (which might
or might not turn out to be unpleasant)
I always use single quotes (') in Ruby code unless I
actually want to make use of the double-quote (")features (interpolation and substitution)
I always put the result of the evaluation of the ment (or block) on the next line with a proceeding
state-#=>, similar to what you would find if you were usingirb or browsing Ruby’s documentation
Comments on executable lines of code start with #and
are in italics to the end of the comment Comments on
#=>lines are in parentheses and are in italics.
Acknowledgments
Without the Pragmatic Programmers’ freely available
1st Edition of Programming Ruby, I would have never
discovered the wonderful world of Ruby.The Pickaxebooks and the great Ruby community are what makeprojects like this one possible
Thanks to my loving partner, Brandon S.Ward, for hisinfinite patience while working on this book
Reporting Errata
Readers will almost certainly find topics that they wishwere covered which we were overlooked when plan-ning this book I encourage you to please contact usand let us know what you would like to see included
in later editions Criticisms are also welcome Contactinformation can be found in the front-matter of thisbook
Trang 17to yourself as you are coding: “everything is an object,everything is an object, ”.That’s the rule to swim orsink by in Ruby Absolutely everything around is anobject: an instance of a class.
Also, keep in mind that the rigidness that comes withstatically typed languages is relaxed Methods (general-ly) do not check to see whether an object that theyare working with is an instance of a particular class.For example, the #putsmethod will accept any objectwhich responds to a #to_smethod call So, if yourobject implements #to_s, you can use #puts.This is
Trang 18called “duck typing” – you’ll see more discussion aboutthis throughout the book.
Number from a String
Float If you want the input to decide whether it getsstored as a Float or Fixnum, do this instead:
class String
def to_real
if self.include? '.'
self.to_f else
self.to_i end
Trang 19Number to Formatted String
Number from a String
123.to_s
#=> "123"
(123.0).to_s
#=> "123.0"
Often a simple conversion from IntegerorFloatto
Stringwill do—as in above
However, Ruby also has the sprintfstyle of String
formatting built in C programmers will be familiar
with this Many Stringformatting codes exist; I coverjust the numeric ones here in Table 1.1; refer to Table
2.1 for an additional list of the escape codes that can
be used for formatting Strings Also try man sprintf
orri Kernel.sprintf from a terminal on your
favorite*nix (or Cygwin on Windows) for complete
documentation of this feature
Here are some examples of using sprintf():
'The price is: %10d' % 123
#=> "The price is: 123" (space padding)
'The price is: %10.2f' % 123
#=> "The price is: 123.00"
Trang 20XorX space,#,+,-,0,*d Integer argument is
converted to hexnotation.dforcesuppercase AtoF
o space,#,+,-,0,*d Integer argument is
converted to octalnotation
b space,#,+,-,0,*d Integer argument is
converted to binarynotation
f d.d, space,#,+,-, Float argument is
0,*d converted to floating
notation with a ing zero before num-bers less than 1orgreater than -1
Trang 21lead-Numeric Arguments Explanation
is less than -4orgreater than or equal
to the precision.G
forces uppercase E
unknown author, printf man page, 2000
Table 1.2 gives an explanation of what each of these
arguments does
Table 1.2 Numeric SprintF Arguments
d.d The first dis the width, the last dis
an integer representing the precision
of the floating point rendering A
-preceding this argument causes leftjustification
space Pad width with spaces
# Use alternate notation for hex, octal,
Trang 22Table 1.2 Continued
*d dmust be an integer indicating
the width of the numerical representation
Note that *is assumed for the first nonzero numberencountered in the argument list
String to Array and
surpris-to Table 1.3 for a list of ranges that can be used inside
String.[]
Trang 23Here are some results that you might not expect at
#=> "ar" (no out of range error)
Table 1.3 String Slicing Operators
String Ranges Positions (Counting starts at
0, Negative Numbers Count Position from the End)
S[{start} {end}] {start}includes the
character;{end}includesthe character
S[{start} {end}] {start}includes the
character;{end}excludesthe character
S[{start}, {count}] {start}includes the
character;{count}positionsfrom start to include Use 1
to get a one-character string.You can also assemble a Stringfrom an Arrayof
Strings:
['this','is','a','test'].join ' '
#=> "this is a test"
Also see the subsection “Replacing Substrings” in
“Working with Strings” for information about
replac-ing parts of Stringsusing slicing operators
String to Array and Back Again
Trang 24String to Regular Expression and Back Again
inter-asYYYY-MM-DDorMM/DD/YY You can make a Regexp
object from this as follows:
Regexp.new gets.chomp.gsub(%r{[^/]}, '\d')
In plain English, this means: “Get a line of input, stripwhite space from the ends, replace all occurrences ofcharacters that are not ^or/with the String \d(sin-gle quoted), and convert the resulting Stringobjectinto a Regexpobject.” Note that %r{}is another way
of writing a regular expression literal—in this case, Iused it to avoid having to escape the /
Conversely,Stringrepresentations of the content of a
Regexpcan be made
Note that the last form listed at the beginning of thissubsection is in alternate notation which is more
Trang 25human-readable; also note that feeding this alternate
notation back into Regexp.newmight not result in thesameRegexpobject
See the subsection “Searching Strings” in “Working
with Strings” for more on this topic
Array to Hash and Back Again
Array to Hash and Back Again
Hash[*[1,2,3].zip([4,5,6]).flatten]
#=> {1=>4, 2=>5, 3=>6}
{ 'foo' => 1, 'bar' => 2, 'baz' => 3 }.keys
#=> ["baz", "foo", "bar"]
{ 'foo' => 1, 'bar' => 2, 'baz' => 3 }.values
#=>[3, 1, 2]
{ 'foo' => 1, 'bar' => 2, 'baz' => 3 }.to_a()
#=> [["baz", 3], ["foo", 1], ["bar", 2]]
Above, two ordered Arrays, one with your keys—1, 2,and 3—and another with your values—4, 5, and 6—
are zipped together, flattened, and then splatted to
make the Array compatible with the Hash.[] class
method.This makes the values from the Array become,
one, the key and, the other, the value.
If you have an Arrayand you want to initialize all of
the keys of a new Hashbut not yet set the values, a
clever trick is to zip the Array of keys with an empty
Array:
Hash[*%w{a b c}.zip([]).flatten]
#=> {"a"=>nil, "b"=>nil, "c"=>nil}
Similarly, you can assign the Array’s index to the value
instead of nil – this requires an interative
approach
Trang 26#=> {1=>nil, 2=>nil, 3=>nil}
Conversely, you can get the keys and values out of the
Hashin a number of ways including those short ods listed at the beginning of this section
meth-However, you might also want to walk through the
Hashand get an Arrayfrom keys or values that meetcertain criteria:
{ 'foo' => 1, 'bar' => 2, 'baz' => 3 }.select do |k, v|
Trang 27Array to Set and Back Again
Floating-Point, Integer , and Rational Numbers
require 'set'
['foo', 'bar', 'baz'].to_set
#=> #<Set: {"baz", "foo", "bar"}>
Set.new(['foo', 'bar', 'baz']).to_a
#=> ["baz", "foo", "bar"]
ASetcan be thought of as a Hashwith no values (in
fact, it uses a Hashfor storage) As you can see,
con-verting to a Setfrom an Arrayis much easier than
Trang 28For the most part, numbers behave in Ruby exactly asthey do in other languages.You might be interested intwo exceptions, though:
If the Integerrepresentation of a number is too large
to store in the host machine’s CPU registers (largerthan 32 bits on x86), the Integerrepresentation isstored in a Bignumobject instead of a Fixnumobject.The difference between the two is that Bignumallowscomputation on very large (or very negative) numbers
at the cost of computational overhead Note that Rubyuses 1 bit for the sign and 2 more bits for the tag (usedfor internal bookkeeping)
(2**29).class
#=> Fixnum
(2**30).class
#=> Bignum
Ruby also supports storing numbers in a Rational
object that implements the normal arithmetic
Trang 29A Word on Boolean true and false
In Ruby, anything that is not either false or nil is
considered true —even 0 (zero) Let me say that again:
0 evaluates to true If you want to say that something
evaluates to false , you must explicitly state it If your
method needs to return true as the result of
some conditional test , just return the test as result
as the return value Remember, the last evaluation in a method is the return value.
As an interesting side note, in Ruby, any time you write out true or false in your code, you are referring to
singleton instances of the TrueClass and
FalseClass , respectively So, even true and false
have member methods, and variables and respond to
method calls.
Floating-Point, Integer , and Rational Numbers
Trang 31The following subsection and the next two subsectionsare closely related I first briefly present the simplesearching functions that accept Stringsas parameters;then I dive right in to regular expressions.
Trang 32#=> 3 (2 "o"s and 1 "b")
'foobar'.count 'ob', 'o'
#=> 2 (only "o" appeared in both parameters) 'foobar'.index 'ob'
by much)
In the middle two examples,#countis used to returnthe number of occurrences of a particular string.Additional parameters to #countare intersected on a
character basis (Intersection is a term from set logic It
means to only include those elements which occur inboth sets In this case, an “element” is a character.) Thismethod is not yet Unicode safe
Finally, in the last two examples,#indexreturns theposition—in count-from-zero notation—of the firstoccurrence of either a String or the numerical code of
a character.You can use #rindexto find the index ofthe last occurrence
Trang 33You can also walk through a String,File, or IOStream
performing a search Let’s say that you want to write a
simple config file parser that places each config
vari-able and its value in a Hash:
The code at #is executed only if =is present See the
subsection “Parsing a Simple Config File,” in Chapter
7, “Manipulating Text,” for a much more complete
example of this
Searching Strings with Regular
Expressions
Searching Strings with Regular Expressions
"The time is: 12:34:54\n".match
This topic is huge—entire books are devoted to
regu-lar expressions Perhaps the best one is Mastering
Regular Expressions (O’Reilly, 2002), by Jeffrey E F.
Friedl If your job includes working with a lot of cleartext, I highly recommend that you pick up this
resource and keep it within reach
Trang 34Here I cover ways in which you can use Regexpobjects
to search text in Ruby (it’s not a review of the regularexpression language)
When performing a Regexpmatch in Ruby, any ()groupings within the Regexpare set to the (thread-local) global variables $1to$9in the order they appear
in the Regexp
The#matchmethod returns a MatchDataobject for the
first match in the Stringand also sets the global variable
$~to the same object Here’s what a MatchDatahas init:
m = "The time is: 12:34:54\n".match
Trang 35"The time is: 12:34:54\n".split(/:\s/)
#=> ["The time is", "12:34:54\n"]
"Setting #{ary[0]} is currently set to #{ary[1]}."
#=> "Setting some_variable is currently set to
some_value."
"You are currently looking at #{ary[0].tr('_', '
')}."
#=>"You are currently looking at some variable."
As mentioned in the section “String to Array and BackAgain,” in Chapter 1, “Converting Between Types,”
Stringscan be treated like Arraysof characters in
many respects.This includes replacements such as
above
In the last two examples above, double-quoted Strings are used to evaluate "#{}"clauses within them and
interpolate the result of the evaluation.You can do
pretty much anything inside a "#{}" In the above
examples I show array access and even a #tr
method call.
A somewhat more obscure function, #tr allows you to
do a character-wise replacement:
Trang 36'You are currently looking at a string.'.tr 'aeiou', '_'
#=> "Y _r_ c_rr_ntly l k_ng _t _ str_ng." 'You are currently looking at a string.'.tr 'aeiou', 'uoiea'
#=> "Yea uro carrontly leeking ut u string."
The first example replaces all vowels with an “_” acter; the second switches each vowel for anothervowel.See the subsection “Sanitizing Input,” later
char-in this chapter, for another example of uschar-ing #tr and a discussion of how #tr parameters work.
Replacing Substrings using SprintF
#=> "I received a couple of strings: one, two" 'I received %25p' % [['an', 'array']]
#=> "I received [\"an\", \"array\"]" 'I received %-25p' % [['an', 'array']]
#=> "I received [\"an\", \"array\"] "
As with numbers, you can also apply sprintfstyleformatting using the “%”operator.Table 2.1 gives theallowed arguments
Trang 37Table 2.1 String SprintF Codes
String Arguments Explanation
Argument Allowed
repre-senting character code
to.inspect()And Table 2.2 lists what those arguments do
Table 2.2 String SprintF Arguments
Argument Explanation
*d dmust be an integer Specifies the
width of the field
- Left justification
I should also briefly mention a somewhat obscure
fea-ture of sprintf:You can use (position)$positional
notation to access a specific entry in the array provided.Somewhat annoyingly, however, the notation for this
access counts starting at 1instead of 0(asArraysdo):
'Hi %1$s! Today you turn %2$d! Happy birthday,
Trang 38'The current time is: 12:34:21'.sub(
/(\d\d):(\d\d):(\d\d)/, '\1\2\3')
#=> "The current time is: 123421"
Replacing Substrings using Regular Expressions
Again, you can use regular expressions for some erful functions Use \1to\9to interpolate the group-ing parentheses’ results from the match to yourreplacement Use .#gsubinstead of .#subto replace alloccurrences in a String.
pow-In the example above, the entire phrase “12:34:21” ismatch and replaced with the subgroups 1-3 with no
“:”s between them Alternatively, you could simplyreplace any occurrence of “:” appearing between digits.This could be written like this:
'The current time is: 12:34:21'.gsub(
/(\d):(\d)/, '\1\2')
#=> "The current time is: 123421"
Working with Unicode
#!/usr/bin/ruby -wKu
' цитрус'.scan(/./) { |b| print b, ' ' }
Produces:
ц и т р у с
Ruby accepts source files encoded in UTF-8 Just to
be safe, include the -Kucommand-line option in your
Trang 39shebang line to ensure that this is properly interpreted
on other OSs and other locales.The modified shebangline is the first line in the code sample above
Ruby itself is not yet fully internationalized For
instance, Ruby is unaware of the multibyte nature of
UTF-8 beyond the first 255 character codes So
#each_byte must be used with care when dealing withinternational strings; for example, it will not work on
any non-Latin languages:
puts " цитрус" # output directly to buffer
"цитрус".each_byte { |b| print(""<<b, ' ') }
Produces:
цитрус
? ? ? ? ? ?
Instead, use the Regexp engine to iterate over these
characters (it is UTF-8 aware).The code to do this is
the code sample at the beginning of this section
In general, use the Regexpengine for slicing and
matching when you’re working with Unicode Other
than that,Stringswith Unicode content should
behave exactly as you expect them to
Sanitizing Input
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
Trang 40Let’s say that you want to write a password changer for
*nix (perhaps storing to an LDAP back end) At a
login prompt, you can use almost all characters in a
password that you can generate at a keyboard But thefew that you can’t use might give your users a
headache when they discover that they can’t log inagain after changing their password In an effort tomake your life easier, you could write a passwordchanger that restricts the password to alphanumericand a few of its friends.String#count, as applied above,can help you do just that
This works by using a special syntax that’s shared by.#count,.#tr,#delete, and #squeeze A parameterbeginning with ^negates the list; the list consists ofany valid characters in the active character set and maycontain ranges formed with - If more than oneparameter list is given to these functions, the lists ofcharacters are intersected using set logic—that is, onlycharacters in both lists are used for filtering
For other types of sanitation, you might also want tosimply replace all “evil” characters with _(such as perhaps from a CGI form post):
evil_input = '`cat /etc/passwd`'
evil_input.tr('./\`', '_')
#=> "_cat _etc_passwd_"
Working with Line Endings
When dealing with clear text from the three main OStypes, you will encounter what is perhaps their oldestfile format fragmentation (see Table 2.3)