The Well-Grounded Rubyist, Third Edition
David A. Black, Joseph Leo III
  • March 2019
  • ISBN 9781617295218
  • 584 pages
  • printed in black & white
free previous edition eBook included
An eBook copy of the previous edition of this book is included at no additional cost. It will be automatically added to your Manning Bookshelf within 24 hours of purchase.

A deep and thorough dive into the Ruby programming language.

Brian Daley, University of Connecticut
The Well-Grounded Rubyist, Third Editionis a beautifully written tutorial that begins with your first Ruby program and takes you all the way to sophisticated topics like reflection, threading, and recursion. Ruby masters David A. Black and Joe Leo distill their years of knowledge for you, concentrating on the language and its uses so you can use Ruby in any way you choose. Updated for Ruby 2.5.

About the Technology

Designed for developer productivity, Ruby is an easy-to-learn dynamic language perfect for creating virtually any kind of software. Its famously friendly development community, countless libraries, and amazing tools, like the Rails framework, have established it as the language of choice for high-profile companies, including GitHub, SlideShare, and Shopify. The future is bright for the well-grounded Rubyist!

About the book

In The Well-Grounded Rubyist, Third Edition expert authors David A. Black and Joseph Leo deliver Ruby mastery in an easy-to-read, casual style. You'll lock in core principles as you write your first Ruby programs. Then, you'll progressively build up to topics like reflection, threading, and recursion, cementing your knowledge with high-value exercises to practice your skills along the way.
Table of Contents detailed table of contents

Part 1: Ruby foundations

1 Bootstrapping your Ruby literacy

1.1 Basic Ruby language literacy

1.1.1 Installing Ruby and using a text editor

1.1.2 A Ruby syntax survival kit

1.1.3 The variety of Ruby identifiers

1.1.4 Method calls, messages, and Ruby objects

1.1.5 Writing and saving a simple program

1.1.6 Feeding the program to Ruby

1.1.7 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 Standard Ruby gems and 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 The rake task-management utility

1.4.4 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


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 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


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 How extend works

4.2.5 The rules of method lookup summarized

4.2.6 Going up the method search path with super

4.2.7 Inspecting method hierarchies with method and super_method

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


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


6 Control-flow techniques

6.1 Conditional code execution

6.1.1 The if keyword and its variants

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 Multiple assignment in conditional statements

6.2.4 Looping based on a list of values

6.3 Iterators and code blocks

6.3.1 The ingredients of iteration

6.3.2 Iteration, home-style

6.3.3 The anatomy of a method call

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

6.3.5 Implementing times

6.3.6 The importance of being each

6.3.7 From each to map

6.3.8 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 Debugging with binding.irb

6.4.4 Avoiding NoMethodError with the safe navigation operator

6.4.5 Raising exceptions explicitly

6.4.6 Capturing an exception in a rescue clause

6.4.7 The ensure clause

6.4.8 Creating your own exception classes


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 and other methods defined on Object

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


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


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 slice_ family of methods

10.5.5 The cycle method

10.5.6 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 Defining sort-order logic with a block

10.8.2 Concise sorting with sort_by

10.8.3 Sorting enumerables and the Comparable module

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


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


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


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


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 Concurrent 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 and exec methods and backticks

14.6.2 Communicating with programs via open and popen3


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


16 Ruby and functional programming

16.1 Understanding pure functions

16.1.1 Methods with side effects

16.1.2 Pure functions and referential transparency in Ruby

16.1.3 Side effects in Ruby’s built-in methods

16.1.4 Modifying an object’s state

16.2 Immutability

16.2.1 Object#freeze and Object#frozen?

16.2.2 Frozen string literals

16.3 Higher-order functions

16.3.1 Method chaining

16.3.2 Kernel#itself and Kernel#yield_self

16.3.3 Functions that return functions

16.3.4 Currying and partial function application

16.4 Recursion

16.4.1 Lazy evaluation

16.4.2 Tail-call optimization


What's inside

  • Basic Ruby syntax
  • Running Ruby extensions
  • FP concepts like currying, side-effect-free code, and recursion
  • Ruby 2.5 updates

About the reader

For readers with beginner-level programming skills.

About the authors

David A. Black is an internationally known Ruby developer and author, and a cofounder of Ruby Central. Ruby teacher and advocate Joseph Leo III is the founder of Def Method and lead organizer of the Gotham Ruby Conference.

placing your order...

Don't refresh or navigate away from the page.
print book $37.49 $49.99 pBook + eBook + liveBook
includes previous edition eBook
Additional shipping charges may apply
The Well-Grounded Rubyist, Third Edition (print book) added to cart
continue shopping
go to cart

eBook $29.99 $39.99 3 formats + liveBook
includes previous edition eBook
The Well-Grounded Rubyist, Third Edition (eBook) added to cart
continue shopping
go to cart

Prices displayed in rupees will be charged in USD when you check out.
customers also reading

This book 1-hop 2-hops 3-hops

FREE domestic shipping on three or more pBooks