
- •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
Calling a Method
You call a method by specifying a receiver, the name of the method, and optionally some parameters and an associated block.
connection.downloadMP3("jitterbug") { |p| showProgress(p) } |
In this example, the object connectionis the receiver,downloadMP3is the name of the method,"jitterbug"is the parameter, and the stuff between the braces is the associated block.
For class and module methods, the receiver will be the class or module name.
File.size("testfile") Math.sin(Math::PI/4) |
If you omit the receiver, it defaults to self, the current object.
self.id |
» |
537790064 |
id |
» |
537790064 |
self.type |
» |
Object |
type |
» |
Object |
This defaulting mechanism is how Ruby implements private methods. Private methods may notbe called with a receiver, so they must be methods available in the current object.
The optional parameters follow the method name. If there is no ambiguity you can omit the parentheses around the argument list when calling a method.[Other Ruby documentation sometimes calls these method calls without parentheses ``commands.'']However, except in the simplest cases we don't recommend this---there are some subtle problems that can trip you up.[In particular, you must use parentheses on a method call that is itself a parameter to another method call (unless it is the last parameter).]Our rule is simple: if there's any doubt, use parentheses.
a = obj.hash # Same as a = obj.hash() # this.
obj.someMethod "Arg1", arg2, arg3 # Same thing as obj.someMethod("Arg1", arg2, arg3) # with parentheses. |
Expanding Arrays in Method Calls
Earlier we saw that if you put an asterisk in front of a formal parameter in a method definition, multiple arguments in the call to the method will be bundled up into an array. Well, the same thing works in reverse.
When you call a method, you can explode an array, so that each of its members is taken as a separate parameter. Do this by prefixing the array argument (which must follow all the regular arguments) with an asterisk.
def five(a, b, c, d, e) | ||
"I was passed #{a} #{b} #{c} #{d} #{e}" | ||
end | ||
| ||
five(1, 2, 3, 4, 5 ) |
» |
"I was passed 1 2 3 4 5" |
five(1, 2, 3, *['a', 'b']) |
» |
"I was passed 1 2 3 a b" |
five(*(10..14).to_a) |
» |
"I was passed 10 11 12 13 14" |
Making Blocks More Dynamic
We've already seen how you can associate a block with a method call.
listBones("aardvark") do |aBone| # ... end |
Normally, this is perfectly good enough---you associate a fixed block of code with a method, in the same way you'd have a chunk of code after an iforwhilestatement.
Sometimes, however, you'd like to be more flexible. For example, we may be teaching math skills.[Of course, Andy and Dave would have to learn math skills first. Conrad Schneiker reminded us that there are three kinds of people: those who can count and those who can't.]The student could ask for ann-plus table or ann-times table. If the student asked for a 2-times table, we'd output 2, 4, 6, 8, and so on. (This code does not check its inputs for errors.)
print "(t)imes or (p)lus: " times = gets print "number: " number = gets.to_i
if times =~ /^t/ puts((1..10).collect { |n| n*number }.join(", ")) else puts((1..10).collect { |n| n+number }.join(", ")) end |
produces:
(t)imes or (p)lus: t number: 2 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 |
This works, but it's ugly, with virtually identical code on each branch of the ifstatement. If would be nice if we could factor out the block that does the calculation.
print "(t)imes or (p)lus: " times = gets print "number: " number = gets.to_i
if times =~ /^t/ calc = proc { |n| n*number } else calc = proc { |n| n+number } end puts((1..10).collect(&calc).join(", ")) |
produces:
(t)imes or (p)lus: t number: 2 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 |
If the last argument to a method is preceded by an ampersand, Ruby assumes that it is a Procobject. It removes it from the parameter list, converts theProcobject into a block, and associates it with the method.
This technique can also be used to add some syntactic sugar to block usage. For example, you sometimes want to take an iterator and store each value it yields into an array. We'll reuse our Fibonacci number generator from page 40.
a = [] | ||
fibUpTo(20) { |val| a << val } |
» |
nil |
a.inspect |
» |
"[1, 1, 2, 3, 5, 8, 13]" |
This works, but our intention isn't quite as transparent as we may like. Instead, we'll define a method called into, which returns the block that fills the array. (Notice at the same time that the block returned really is a closure---it references the parameteranArrayeven after methodintohas returned.)
def into(anArray) | ||
return proc { |val| anArray << val } | ||
end | ||
| ||
fibUpTo 20, &into(a = []) | ||
a.inspect |
» |
"[1, 1, 2, 3, 5, 8, 13]" |