- •Programming Ruby The Pragmatic Programmer's Guide
- •Foreword
- •Preface
- •Ruby Sparkles
- •What Kind of Language Is Ruby?
- •Is Ruby for Me?
- •Why Did We Write This Book?
- •Ruby Versions
- •Installing Ruby
- •Building Ruby
- •Running Ruby
- •Interactive Ruby
- •Ruby Programs
- •Resources
- •Acknowledgments
- •Notation Conventions
- •Roadmap
- •Ruby.New
- •Ruby Is an Object-Oriented Language
- •Some Basic Ruby
- •Arrays and Hashes
- •Control Structures
- •Regular Expressions
- •Blocks and Iterators
- •Reading and 'Riting
- •Onward and Upward
- •Classes, Objects, and Variables
- •Inheritance and Messages
- •Inheritance and Mixins
- •Objects and Attributes
- •Writable Attributes
- •Virtual Attributes
- •Class Variables and Class Methods
- •Class Variables
- •Class Methods
- •Singletons and Other Constructors
- •Access Control
- •Specifying Access Control
- •Variables
- •Containers, Blocks, and Iterators
- •Containers
- •Implementing a SongList Container
- •Blocks and Iterators
- •Implementing Iterators
- •Blocks for Transactions
- •Blocks Can Be Closures
- •Standard Types
- •Numbers
- •Strings
- •Working with Strings
- •Ranges as Sequences
- •Ranges as Conditions
- •Ranges as Intervals
- •Regular Expressions
- •Patterns
- •Anchors
- •Character Classes
- •Repetition
- •Alternation
- •Grouping
- •Pattern-Based Substitution
- •Backslash Sequences in the Substitution
- •Object-Oriented Regular Expressions
- •More About Methods
- •Defining a Method
- •Variable-Length Argument Lists
- •Methods and Blocks
- •Calling a Method
- •Expanding Arrays in Method Calls
- •Making Blocks More Dynamic
- •Collecting Hash Arguments
- •Expressions
- •Operator Expressions
- •Miscellaneous Expressions
- •Command Expansion
- •Backquotes Are Soft
- •Assignment
- •Parallel Assignment
- •Nested Assignments
- •Other Forms of Assignment
- •Conditional Execution
- •Boolean Expressions
- •Defined?, And, Or, and Not
- •If and Unless Expressions
- •If and Unless Modifiers
- •Case Expressions
- •Iterators
- •Break, Redo, and Next
- •Variable Scope and Loops
- •Exceptions, Catch, and Throw
- •The Exception Class
- •Handling Exceptions
- •Tidying Up
- •Play It Again
- •Raising Exceptions
- •Adding Information to Exceptions
- •Catch and Throw
- •Modules
- •Namespaces
- •Instance Variables in Mixins
- •Iterators and the Enumerable Module
- •Including Other Files
- •Basic Input and Output
- •What Is an io Object?
- •Opening and Closing Files
- •Reading and Writing Files
- •Iterators for Reading
- •Writing to Files
- •Talking to Networks
- •Threads and Processes
- •Multithreading
- •Creating Ruby Threads
- •Manipulating Threads
- •Thread Variables
- •Threads and Exceptions
- •Controlling the Thread Scheduler
- •Mutual Exclusion
- •The Mutex Class
- •Condition Variables
- •Running Multiple Processes
- •Spawning New Processes
- •Independent Children
- •Blocks and Subprocesses
- •When Trouble Strikes
- •Ruby Debugger
- •Interactive Ruby
- •Editor Support
- •But It Doesn't Work!
- •But It's Too Slow!
- •Create Locals Outside Blocks
- •Use the Profiler
- •Ruby and Its World
- •Command-Line Arguments
- •Command-Line Options
- •Program Termination
- •Environment Variables
- •Writing to Environment Variables
- •Where Ruby Finds Its Modules
- •Build Environment
- •Ruby and the Web
- •Writing cgi Scripts
- •Using cgi.Rb
- •Quoting
- •Creating Forms and html
- •Cookies
- •Sessions
- •Embedding Ruby in html
- •Using eruby
- •Installing eruby in Apache
- •Improving Performance
- •Ruby Tk
- •Simple Tk Application
- •Widgets
- •Setting Widget Options
- •Getting Widget Data
- •Setting/Getting Options Dynamically
- •Sample Application
- •Binding Events
- •Scrolling
- •Just One More Thing
- •Translating from Perl/Tk Documentation
- •Object Creation
- •Running Ruby Under Windows
- •Windows Automation
- •Getting and Setting Properties
- •Named Arguments
- •For each
- •An Example
- •Optimizing
- •Extending Ruby
- •Ruby Objects in c
- •Value as a Pointer
- •Value as an Immediate Object
- •Writing Ruby in c
- •Evaluating Ruby Expressions in c
- •Sharing Data Between Ruby and c
- •Directly Sharing Variables
- •Wrapping c Structures
- •An Example
- •Memory Allocation
- •Creating an Extension
- •Creating a Makefile with extconf.Rb
- •Static Linking
- •Embedding a Ruby Interpreter
- •Bridging Ruby to Other Languages
- •Ruby c Language api
- •The Ruby Language
- •Source Layout
- •Begin and end Blocks
- •General Delimited Input
- •The Basic Types
- •Integer and Floating Point Numbers
- •Strings
- •Requirements for a Hash Key
- •Symbols
- •Regular Expressions
- •Regular Expression Options
- •Regular Expression Patterns
- •Substitutions
- •Extensions
- •Variable/Method Ambiguity
- •Variables and Constants
- •Scope of Constants and Variables
- •Predefined Variables
- •Exception Information
- •Pattern Matching Variables
- •Input/Output Variables
- •Execution Environment Variables
- •Standard Objects
- •Global Constants
- •Expressions Single Terms
- •Operator Expressions
- •More on Assignment
- •Parallel Assignment
- •Block Expressions
- •Boolean Expressions
- •Truth Values
- •And, Or, Not, and Defined?
- •Comparison Operators
- •Ranges in Boolean Expressions
- •Regular Expressions in Boolean Expressions
- •While and Until Modifiers
- •Break, Redo, Next, and Retry
- •Method Definition
- •Method Arguments
- •Invoking a Method
- •Class Definition
- •Creating Objects from Classes
- •Class Attribute Declarations
- •Module Definitions
- •Mixins---Including Modules
- •Module Functions
- •Access Control
- •Blocks, Closures, and Proc Objects
- •Proc Objects
- •Exceptions
- •Raising Exceptions
- •Handling Exceptions
- •Retrying a Block
- •Catch and Throw
- •Classes and Objects
- •How Classes and Objects Interact
- •Your Basic, Everyday Object
- •Object-Specific Classes
- •Mixin Modules
- •Extending Objects
- •Class and Module Definitions
- •Class Names Are Constants
- •Inheritance and Visibility
- •Freezing Objects
- •Locking Ruby in the Safe
- •Safe Levels
- •Tainted Objects
- •Reflection, ObjectSpace, and Distributed Ruby
- •Looking at Objects
- •Looking Inside Objects
- •Looking at Classes
- •Looking Inside Classes
- •Calling Methods Dynamically
- •Performance Considerations
- •System Hooks
- •Runtime Callbacks
- •Tracing Your Program's Execution
- •How Did We Get Here?
- •Marshaling and Distributed Ruby
- •Custom Serialization Strategy
- •Distributed Ruby
- •Compile Time? Runtime? Anytime!
- •Standard Library
If and Unless Expressions
An ifexpression in Ruby is pretty similar to ``if'' statements in other languages.
if aSong.artist == "Gillespie" then handle = "Dizzy" elsif aSong.artist == "Parker" then handle = "Bird" else handle = "unknown" end |
If you lay out your ifstatements on multiple lines, you can leave off thethenkeyword.
if aSong.artist == "Gillespie" handle = "Dizzy" elsif aSong.artist == "Parker" handle = "Bird" else handle = "unknown" end |
However, if you lay your code out more tightly, the thenkeyword is necessary to separate the boolean expression from the following statements.
if aSong.artist == "Gillespie" then handle = "Dizzy" elsif aSong.artist == "Parker" then handle = "Bird" else handle = "unknown" end |
You can have zero or more elsifclauses and an optionalelseclause.
As we've said before, ifis an expression, not a statement---it returns a value. You don't have to use the value of anifexpression, but it can come in handy.
handle = if aSong.artist == "Gillespie" then "Dizzy" elsif aSong.artist == "Parker" then "Bird" else "unknown" end |
Ruby also has a negated form of the ifstatement:
unless aSong.duration > 180 then cost = .25 else cost = .35 end |
Finally, for the C fans out there, Ruby also supports the C-style conditional expression:
cost = aSong.duration > 180 ? .35 : .25 |
The conditional expression returns the value of either the expression before or the expression after the colon, depending on whether the boolean expression before the question mark evaluates to trueorfalse. In this case, if the song duration is greater than 3 minutes, the expression returns .35. For shorter songs, it returns .25. Whatever the result, it is then assigned tocost.
If and Unless Modifiers
Ruby shares a neat feature with Perl. Statement modifiers let you tack conditional statements onto the end of a normal statement.
mon, day, year = $1, $2, $3 if /(\d\d)-(\d\d)-(\d\d)/ puts "a = #{a}" if fDebug print total unless total == 0 |
For an ifmodifier, the preceding expression will be evaluated only if the condition is true.unlessworks the other way around.
while gets next if /^#/ # Skip comments parseLine unless /^$/ # Don't parse empty lines end |
Because ifitself is an expression, you can get really obscure with statements such as:
if artist == "John Coltrane" artist = "'Trane" end unless nicknames == "no" |
This path leads to the gates of madness.
Case Expressions
The Ruby caseexpression is a powerful beast: a multiwayifon steroids.
case inputLine
when "debug" dumpDebugInfo dumpSymbols
when /p\s+(\w+)/ dumpVariable($1)
when "quit", "exit" exit
else print "Illegal command: #{inputLine}" end |
As with if,casereturns the value of the last expression executed, and you also need athenkeyword if the expression is on the same line as the condition.
kind = case year when 1850..1889 then "Blues" when 1890..1909 then "Ragtime" when 1910..1929 then "New Orleans Jazz" when 1930..1939 then "Swing" when 1940..1950 then "Bebop" else "Jazz" end |
caseoperates by comparing the target (the expression after the keywordcase) with each of the comparison expressions after thewhenkeywords. This test is done usingcomparison===target. As long as a class defines meaningful semantics for===(and all the built-in classes do), objects of that class can be used in case expressions.
For example, regular expressions define ===as a simple pattern match.
case line when /title=(.*)/ puts "Title is #$1" when /track=(.*)/ puts "Track is #$1" when /artist=(.*)/ puts "Artist is #$1" end |
Ruby classes are instances of class Class, which defines===as a test to see if the argument is an instance of the class or one of its superclasses. So (abandoning the benefits of polymorphism and bringing the gods of refactoring down around your ears), you can test the class of objects:
case shape when Square, Rectangle # ... when Circle # ... when Triangle # ... else # ... end |
Loops
Don't tell anyone, but Ruby has pretty primitive built-in looping constructs.
The whileloop executes its body zero or more times as long as its condition is true. For example, this common idiom reads until the input is exhausted.
while gets # ... end |
There's also a negated form that executes the body until the condition becomes true.
until playList.duration > 60 playList.add(songList.pop) end |
As with ifandunless, both of the loops can also be used as statement modifiers.
a *= 2 while a < 100 a -= 10 until a < 100 |
On page 78 in the section on boolean expressions, we said that a range can be used as a kind of flip-flop, returning true when some event happens and then staying true until a second event occurs. This facility is normally used within loops. In the example that follows, we read a text file containing the first ten ordinal numbers (``first,'' ``second,'' and so on) but only print the lines starting with the one that matches ``third'' and ending with the one that matches ``fifth.''
file = File.open("ordinal") while file.gets print if /third/ .. /fifth/ end |
produces:
third fourth fifth |
The elements of a range used in a boolean expression can themselves be expressions. These are evaluated each time the overall boolean expression is evaluated. For example, the following code uses the fact that the variable $.contains the current input line number to display line numbers one through three and those between a match of/eig/and/nin/.
file = File.open("ordinal") while file.gets print if ($. == 1) || /eig/ .. ($. == 3) || /nin/ end |
produces:
first second third eighth ninth |
There's one wrinkle when whileanduntilare used as statement modifiers. If the statement they are modifying is abegin/endblock, the code in the block will always execute at least one time, regardless of the value of the boolean expression.
print "Hello\n" while false begin print "Goodbye\n" end while false |
produces:
Goodbye |