- •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
Looking at Classes
Knowing about objects is one part of reflection, but to get the whole picture, you also need to be able to look at classes---the methods and constants that they contain.
Looking at the class hierarchy is easy. You can get the parent of any particular class using Class#superclass . For classesandmodules,Module#ancestors lists both superclasses and mixed-in modules.
|
klass = Fixnum begin print klass klass = klass.superclass print " < " if klass end while klass puts p Fixnum.ancestors |
produces:
|
Fixnum < Integer < Numeric < Object [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel] |
If you want to build a complete class hierarchy, just run that code for every class in the system. We can use ObjectSpaceto iterate over allClassobjects:
|
ObjectSpace.each_object(Class) do |aClass| # ... end |
Looking Inside Classes
We can find out a bit more about the methods and constants in a particular object. Instead of just checking to see whether the object responds to a given message, we can ask for methods by access level, we can ask for just singleton methods, and we can have a look at the object's constants.
|
class Demo | ||
|
private | ||
|
def privMethod | ||
|
end | ||
|
protected | ||
|
def protMethod | ||
|
end | ||
|
public | ||
|
def pubMethod | ||
|
end | ||
|
| ||
|
def Demo.classMethod | ||
|
end | ||
|
| ||
|
CONST = 1.23 | ||
|
end | ||
|
| ||
|
Demo.private_instance_methods |
» |
["privMethod"] |
|
Demo.protected_instance_methods |
» |
["protMethod"] |
|
Demo.public_instance_methods |
» |
["pubMethod"] |
|
Demo.singleton_methods |
» |
["classMethod"] |
|
Demo.constants - Demo.superclass.constants |
» |
["CONST"] |
Module.constants returnsallthe constants available via a module, including constants from the module's superclasses. We're not interested in those just at the moment, so we'll subtract them from our list.
Given a list of method names, we might now be tempted to try calling them. Fortunately, that's easy with Ruby.
Calling Methods Dynamically
C and Java programmers often find themselves writing some kind of dispatch table: functions which are invoked based on a command. Think of a typical C idiom where you have to translate a string to a function pointer:
|
typedef struct { char *name; void (*fptr)(); } Tuple;
Tuple list[]= { { "play", fptr_play }, { "stop", fptr_stop }, { "record", fptr_record }, { 0, 0 }, };
...
void dispatch(char *cmd) { int i = 0; for (; list[i].name; i++) { if (strncmp(list[i].name,cmd,strlen(cmd)) == 0) { list[i].fptr(); return; } } /* not found */ } |
In Ruby, you can do all this in one line. Stick all your command functions into a class, create an instance of that class (we called it commands), and ask that object to execute a method called the same name as the command string.
|
commands.send(commandString) |
Oh, and by the way, it does much more than the C version---it's dynamic. The Ruby version will find new methods added at runtime just as easily.
You don't have to write special command classes for send: it works on any object.
|
"John Coltrane".send(:length) |
» |
13 |
|
"Miles Davis".send("sub", /iles/, '.') |
» |
"M. Davis" |
Another way of invoking methods dynamically uses Methodobjects. AMethodobject is like aProcobject: it represents a chunk of code and a context in which it executes. In this case, the code is the body of the method, and the context is the object that created the method. Once we have ourMethodobject, we can execute it sometime later by sending it the messagecall.
|
trane = "John Coltrane".method(:length) | ||
|
miles = "Miles Davis".method("sub") | ||
|
| ||
|
trane.call |
» |
13 |
|
miles.call(/iles/, '.') |
» |
"M. Davis" |
You can pass the Methodobject around as you would any other object, and when you invokeMethod#call , the method is run just as if you had invoked it on the original object. It's like having a C-style function pointer but in a fully object-oriented style.
You can also use Methodobjects with iterators.
|
def double(a) | ||
|
2*a | ||
|
end | ||
|
| ||
|
mObj = method(:double) | ||
|
| ||
|
[ 1, 3, 5, 7 ].collect(&mObj) |
» |
[2, 6, 10, 14] |
As good things come in threes, here's yet another way to invoke methods dynamically. The evalmethod (and its variations such asclass_eval,module_eval, andinstance_eval) will parse and execute an arbitrary string of legal Ruby source code.
|
trane = %q{"John Coltrane".length} | ||
|
miles = %q{"Miles Davis".sub(/iles/, '.')} | ||
|
| ||
|
eval trane |
» |
13 |
|
eval miles |
» |
"M. Davis" |
When using eval, it can be helpful to state explicitly the context in which the expression should be evaluated, rather than using the current context. You can obtain a context by callingKernel#binding at the desired point.
|
class CoinSlot def initialize(amt=Cents.new(25)) @amt = amt $here = binding end end
a = CoinSlot.new eval "puts @amt", $here eval "puts @amt" |
produces:
|
$0.25USD nil |
The first evalevaluates@amtin the context of the instance of classCoinSlot. The secondevalevaluates@amtin the context ofObject, where the instance variable@amtis not defined.
