The Well-Grounded Rubyist, Second Edition
David A. Black
  • June 2014
  • ISBN 9781617291692
  • 536 pages
  • printed in black & white

Once again, the definitive book on Ruby from David Black. A must-have!

William Wheeler, TekSystems

The Well-Grounded Rubyist, Second Edition addresses both newcomers to Ruby as well as Ruby programmers who want to deepen their understanding of the language. This beautifully written and totally revised second edition includes coverage of features that are new in Ruby 2.1, as well as expanded and updated coverage of aspects of the language that have changed.

Table of Contents show full


preface to the first edition


about this book

about the cover illustration

Part 1 Ruby foundations

1. Bootstrapping your Ruby literacy

1.1. Basic Ruby language literacy

1.1.1. A Ruby syntax survival kit

1.1.2. The variety of Ruby identifiers

1.1.3. Method calls, messages, and Ruby objects

1.1.4. Writing and saving a simple program

1.1.5. Feeding the program to Ruby

1.1.6. Keyboard and file I/O

1.2. Anatomy of the Ruby installation

1.2.1. The Ruby standard library subdirectory (RbConfig::CONFIG[rubylibdir])

1.2.2. The C extensions directory (RbConfig::CONFIG[archdir])

1.2.3. The site_ruby (RbConfig::CONFIG[sitedir]) and vendor_ruby (RbConfig::CONFIG[vendordir]) directories

1.2.4. The gems directory

1.3. Ruby extensions and programming libraries

1.3.1. Loading external files and extensions

1.3.2. "Load"-ing a file in the default load path

1.3.3. "Require"-ing a feature

1.3.4. require_relative

1.4. Out-of-the-box Ruby tools and applications

1.4.1. Interpreter command-line switches

1.4.2. A closer look at interactive Ruby interpretation with irb

1.4.3. ri and RDoc

1.4.4. The rake task-management utility

1.4.5. Installing packages with the gem command

1.5. Summary

2. Objects, methods, and local variables

2.1. Talking to objects

2.1.1. Ruby and object orientation

2.1.2. Creating a generic object

2.1.3. Methods that take arguments

2.1.4. The return value of a method

2.2. Crafting an object: The behavior of a ticket

2.2.1. The ticket object, behavior first

2.2.2. Querying the ticket object

2.2.3. Shortening the ticket code via string interpolation

2.2.4. Ticket availability: Expressing Boolean state in a method

2.3. The innate behaviors of an object

2.3.1. Identifying objects uniquely with the object_id method

2.3.2. Querying an object’s abilities with the respond_to? method

2.3.3. Sending messages to objects with the send method

2.4. A close look at method arguments

2.4.1. Required and optional arguments

2.4.2. Default values for arguments

2.4.3. Order of parameters and arguments

2.4.4. What you can’t do in argument lists

2.5. Local variables and variable assignment

2.5.1. Variables, objects, and references

2.5.2. References in variable assignment and reassignment

2.5.3. References and method arguments

2.5.4. Local variables and the things that look like them

2.6. Summary

3. Organizing objects with classes

3.1. Classes and instances

3.1.1. Instance methods

3.1.2. Overriding methods

3.1.3. Reopening classes

3.2. Instance variables and object state

3.2.1. Initializing an object with state

3.3. Setter methods

3.3.1. The equal sign (=) in method names

3.3.2. Syntactic sugar for assignment-like methods

3.3.3. Setter methods unleashed

3.4. Attributes and the attr_* method family

3.4.1. Automating the creation of attributes

3.4.2. Summary of attr_* methods

3.5. Inheritance and the Ruby class hierarchy

3.5.1. Single inheritance: One to a customer

3.5.3. El Viejo’s older brother: BasicObject

3.6. Classes as objects and message receivers

3.6.1. Creating class objects

3.6.2. How class objects call methods

3.6.3. A singleton method by any other name…​

3.6.4. When, and why, to write a class method

3.6.5. Class methods vs. instance methods

3.7. Constants up close

3.7.1. Basic use of constants

3.7.2. Reassigning vs. modifying constants

3.8. Nature vs. nurture in Ruby objects

3.9. Summary

4. Modules and program organization

4.1. Basics of module creation and use

4.1.1. A module encapsulating "stacklikeness"

4.1.2. Mixing a module into a class

4.1.3. Using the module further

4.2. Modules, classes, and method lookup

4.2.1. Illustrating the basics of method lookup

4.2.2. Defining the same method more than once

4.2.3. How prepend works

4.2.4. The rules of method lookup summarized

4.2.5. Going up the method search path with super

4.3. The method_missing method

4.3.1. Combining method_missing and super

4.4. Class/module design and naming

4.4.1. Mix-ins and/or inheritance

4.4.2. Nesting modules and classes

4.5. Summary

5. The default object (self), scope, and visibility

5.1. Understanding self, the current/default object

5.1.1. Who gets to be self, and where

5.1.2. The top-level self object

5.1.3. Self inside class, module, and method definitions

5.1.4. Self as the default receiver of messages

5.1.5. Resolving instance variables through self

5.2. Determining scope

5.2.1. Global scope and global variables

5.2.2. Local scope

5.2.3. The interaction between local scope and self

5.2.4. Scope and resolution of constants

5.2.5. Class variable syntax, scope, and visibility

5.3. Deploying method-access rules

5.3.1. Private methods

5.3.2. Protected methods

5.4. Writing and using top-level methods

5.4.1. Defining a top-level method

5.4.2. Predefined (built-in) top-level methods

5.5. Summary

6. Control-flow techniques

6.1. Conditional code execution

6.1.1. The if keyword and friends

6.1.2. Assignment syntax in condition bodies and tests

6.1.3. case statements

6.2. Repeating actions with loops

6.2.1. Unconditional looping with the loop method

6.2.2. Conditional looping with the while and until keywords

6.2.3. Looping based on a list of values

6.3. Iterators and code blocks6.3.1. The ingredients of iteration

6.3.1. Iteration, home-style

6.3.2. The anatomy of a method call

6.3.3. Curly braces vs. do/end in code block syntax

6.3.4. Implementing times

6.3.5. The importance of being each

6.3.6. From each to map

6.3.7. Block parameters and variable scope

6.4. Error handling and exceptions

6.4.1. Raising and rescuing exceptions

6.4.2. The rescue keyword to the rescue!

6.4.3. Raising exceptions explicitly

6.4.4. Capturing an exception in a rescue clause

6.4.5. The ensure clause

6.4.6. Creating your own exception classes

6.5. Summary

Part 2 Built-in classes and modules

7. Built-in essentials

7.1. Ruby’s literal constructors

7.2. Recurrent syntactic sugar

7.2.1. Defining operators by defining methods

7.2.2. Customizing unary operators

7.3. Bang (!) methods and "danger"

7.3.1. Destructive (receiver-changing) effects as danger

7.3.2. Destructiveness and "danger" vary independently

7.4. Built-in and custom to_* (conversion) methods

7.4.1. String conversion: to_s

7.4.2. Array conversion with to_a and the * operator

7.4.3. Numerical conversion with to_i and to_f

7.4.4. Role-playing to_* methods

7.5. Boolean states, Boolean objects, and nil

7.5.1. True and false as states

7.5.2. true and false as objects

7.5.3. The special object nil

7.6. Comparing two objects

7.6.1. Equality tests

7.6.2. Comparisons and the Comparable module

7.7. Inspecting object capabilities

7.7.1. Listing an object’s methods

7.7.2. Querying class and module objects

7.7.3. Filtered and selected method lists

7.8. Summary

8. Strings, symbols, and other scalar objects

8.1. Working with strings

8.1.1. String notation

8.1.2. Basic string manipulation

8.1.3. Querying strings

8.1.4. String comparison and ordering

8.1.5. String transformation

8.1.6. String conversions

8.1.7. String encoding: A brief introduction

8.2. Symbols and their uses

8.2.1. Chief characteristics of symbols

8.2.2. Symbols and identifiers

8.2.3. Symbols in practice

8.2.4. Strings and symbols in comparison

8.3. Numerical objects

8.3.1. Numerical classes

8.3.2. Performing arithmetic operations

8.4. Times and dates

8.4.1. Instantiating date/time objects

8.4.2. Date/time query methods

8.4.3. Date/time formatting methods

8.4.4. Date/time conversion methods

8.5. Summary

9. Collection and container objects

9.1. Arrays and hashes in comparison

9.2. Collection handling with arrays

9.2.1. Creating a new array

9.2.2. Inserting, retrieving, and removing array elements

9.2.3. Combining arrays with other arrays

9.2.4. Array transformations

9.2.5. Array querying

9.3. Hashes

9.3.1. Creating a new hash

9.3.2. Inserting, retrieving, and removing hash pairs

9.3.3. Specifying default hash values and behavior

9.3.4. Combining hashes with other hashes

9.3.5. Hash transformations

9.3.6. Hash querying

9.3.7. Hashes as final method arguments

9.3.8. A detour back to argument syntax: Named (keyword) arguments

9.4. Ranges

9.4.1. Creating a range

9.4.2. Range-inclusion logic

9.5. Sets

9.5.1. Set creation

9.5.2. Manipulating set elements

9.5.3. Subsets and supersets

9.6. Summary

10. Collections central: Enumerable and Enumerator

10.1. Gaining enumerability through each

10.2. Enumerable Boolean queries

10.3. Enumerable searching and selecting

10.3.1. Getting the first match with find

10.3.2. Getting all matches with find_all (a.k.a. select) and reject

10.3.3. Selecting on threequal matches with grep

10.3.4. Organizing selection results with group_by and partition

10.4. Element-wise enumerable operations

10.4.1. The first method

10.4.2. The take and drop methods

10.4.3. The min and max methods

10.5. Relatives of each

10.5.1. reverse_each

10.5.2. The each_with_index method (and each.with_index)

10.5.3. The each_slice and each_cons methods

10.5.4. The cycle method

10.5.5. Enumerable reduction with inject

10.6. The map method

10.6.1. The return value of map

10.6.2. In-place mapping with map!

10.7. Strings as quasi-enumerables

10.8. Sorting enumerables

10.8.1. Where the Comparable module fits into enumerable sorting (or doesn’t)

10.8.2. Defining sort-order logic with a block

10.8.3. Concise sorting with sort_by

10.9. Enumerators and the next dimension of enumerability

10.9.1. Creating enumerators with a code block

10.9.2. Attaching enumerators to other objects

10.9.3. Implicit creation of enumerators by blockless iterator calls

10.10. Enumerator semantics and uses

10.10.1. How to use an enumerator’s each method

10.10.2. Protecting objects with enumerators

10.10.3. Fine-grained iteration with enumerators

10.10.4. Adding enumerability with an enumerator

10.11. Enumerator method chaining

10.11.1. Economizing on intermediate objects

10.11.2. Indexing enumerables with with_index

10.11.3. Exclusive-or operations on strings with enumerators

10.12. Lazy enumerators

10.12.1. FizzBuzz with a lazy enumerator

10.13. Summary

11. Regular expressions and regexp-based string operations

11.1. What are regular expressions?

11.2. Writing regular expressions

11.2.1. Seeing patterns

11.2.2. Simple matching with literal regular expressions

11.3. Building a pattern in a regular expression

11.3.1. Literal characters in patterns

11.3.2. The dot wildcard character (.)

11.3.3. Character classes

11.4. Matching, substring captures, and MatchData

11.4.1. Capturing submatches with parentheses

11.4.2. Match success and failure

11.4.3. Two ways of getting the captures

11.4.4. Other MatchData information

11.5. Fine-tuning regular expressions with quantifiers, anchors, and modifiers

11.5.1. Constraining matches with quantifiers

11.5.2. Greedy (and non-greedy) quantifiers

11.5.3. Regular expression anchors and assertions

11.5.4. Modifiers

11.6. Converting strings and regular expressions to each other

11.6.1. String-to-regexp idioms

11.6.2. Going from a regular expression to a string

11.7. Common methods that use regular expressions

11.7.1. String#scan

11.7.2. String#split

11.7.3. sub/sub! and gsub/gsub!

11.7.4. Case equality and grep

11.8. Summary

12. File and I/O operations

12.1. How Ruby’s I/O system is put together

12.1.1. The IO class

12.1.2. IO objects as enumerables


12.1.4. A little more about keyboard input

12.2. Basic file operations

12.2.1. The basics of reading from files

12.2.2. Line-based file reading

12.2.3. Byte- and character-based file reading

12.2.4. Seeking and querying file position

12.2.5. Reading files with File class methods

12.2.6. Writing to files

12.2.7. Using blocks to scope file operations

12.2.8. File enumerability

12.2.9. File I/O exceptions and errors

12.3. Querying IO and File objects

12.3.1. Getting information from the File class and the FileTest module

12.3.2. Deriving file information with File::Stat

12.4. Directory manipulation with the Dir class

12.4.1. Reading a directory’s entries

12.4.2. Directory manipulation and querying

12.5. File tools from the standard library

12.5.1. The FileUtils module

12.5.2. The Pathname class

12.5.3. The StringIO class

12.5.4. The open-uri library

12.6. Summary

Part 3 Ruby dynamics

13. Object individuation

13.1. Where the singleton methods are: The singleton class

13.1.1. Dual determination through singleton classes

13.1.2. Examining and modifying a singleton class directly

13.1.3. Singleton classes on the method-lookup path

13.1.4. The singleton_class method

13.1.5. Class methods in (even more) depth

13.2. Modifying Ruby’s core classes and modules

13.2.1. The risks of changing core functionality

13.2.2. Additive changes

13.2.3. Pass-through overrides

13.2.4. Per-object changes with extend

13.2.5. Using refinements to affect core behavior

13.3. BasicObject as ancestor and class

13.3.1. Using BasicObject

13.3.2. Implementing a subclass of BasicObject

13.4. Summary

14. Callable and runnable objects

14.1. Basic anonymous functions: The Proc class

14.1.1. Proc objects

14.1.2. Procs and blocks and how they differ

14.1.3. Block-proc conversions

14.1.4. Using Symbol#to_proc for conciseness

14.1.5. Procs as closures

14.1.6. Proc parameters and arguments

14.2. Creating functions with lambda and →

14.3. Methods as objects

14.3.1. Capturing Method objects

14.3.2. The rationale for methods as objects

14.4. The eval family of methods

14.4.1. Executing arbitrary strings as code with eval

14.4.2. The dangers of eval

14.4.3. The instance_eval method

14.4.4. Using class_eval (a.k.a. module_eval)

14.5. Parallel execution with threads

14.5.1. Killing, stopping, and starting threads

14.5.2. A threaded date server

14.5.3. Writing a chat server using sockets and threads

14.5.4. Threads and variables

14.5.5. Manipulating thread keys

14.6. Issuing system commands from inside Ruby programs

14.6.1. The system method and backticks

14.6.2. Communicating with programs via open and popen3

14.7. Summary

15. Callbacks, hooks, and runtime introspection

15.1. Callbacks and hooks

15.1.1. Intercepting unrecognized messages with method_missing

15.1.2. Trapping include and prepend operations

15.1.3. Intercepting extend

15.1.4. Intercepting inheritance with Class#inherited

15.1.5. The Module#const_missing method

15.1.6. The method_added and singleton_method_added methods

15.2. Interpreting object capability queries

15.2.1. Listing an object’s non-private methods

15.2.2. Listing private and protected methods

15.2.3. Getting class and module instance methods

15.2.4. Listing objects' singleton methods

15.3. Introspection of variables and constants

15.3.1. Listing local and global variables

15.3.2. Listing instance variables

15.4. Tracing execution

15.4.1. Examining the stack trace with caller

15.4.2. Writing a tool for parsing stack traces

15.5. Callbacks and method inspection in practice

15.5.1. MicroTest background: MiniTest

15.5.2. Specifying and implementing MicroTest

15.6. Summary


About the Technology

This is a good time for Ruby! It's powerful like Java or C++, and has dynamic features that let your code react gracefully to changes at runtime. And it's elegant, so creating applications, development tools, and administrative scripts is easier and more straightforward. With the long-awaited Ruby 2, an active development community, and countless libraries and productivity tools, Ruby has come into its own.

About the book

The Well-Grounded Rubyist, Second Edition is a beautifully written tutorial that begins with your first Ruby program and goes on to explore sophisticated topics like callable objects, reflection, and threading. The book concentrates on the language, preparing you to use Ruby in any way you choose. This second edition includes coverage of new Ruby features such as keyword arguments, lazy enumerators, and Module#prepend, along with updated information on new and changed core classes and methods.

What's inside

  • Clear explanations of Ruby concepts
  • Numerous simple examples
  • Updated for Ruby 2.1
  • Prepares you to use Ruby anywhere for any purpose

About the author

David A. Black is an internationally known Ruby developer, author, trainer, speaker, event organizer, and founder of Ruby Central, as well as a Lead Consultant at Cyrus Innovation.

combo $44.99 pBook + eBook
eBook $35.99 pdf + ePub + kindle

FREE domestic shipping on three or more pBooks

All wheat, no chaff—takes you from Ruby programmer to full-fledged Rubyist.

Doug Sparling, Andrews McMeel Universal

Provides powerful insights and digs into Ruby’s quirks. Revelatory.

Ted Roche, Ted Roche & Associates, LLC

The best way to learn Ruby fundamentals.

Derek Sivers,