The Ruby Way takes a “how-to” approach to Ruby programming with the bulk of the material consisting of more than 400 examples arranged by topic. Each example answers the question “How do I do this in Ruby?” Working along with the author, readers are presented with the task description and a discussion of the technical constraints. This is followed by a step-by-step presentation of one good solution. Along the way, the author provides detailed commentary and explanations to aid understanding.
This edition upgrades the book from Ruby 1.8 to 1.9. The book has been fully reviewed to verify behavior and tweak code and text that are slightly obsolete as of this edition. In addition, some sections of the book have been deleted because they refer to technology that has 'fizzled' in the last three years and is no longer relevant.
Foreword xxiv
Acknowledgments xxviii
About the Authors xxxii
Introduction xxxiii
1 Ruby in Review 1
1.1 An Introduction to Object Orientation 2
1.1.1 What Is an Object? 2
1.1.2 Inheritance 4
1.1.3 Polymorphism 6
1.1.4 A Few More Terms 7
1.2 Basic Ruby Syntax and Semantics 8
1.2.1 Keywords and Identifiers 9
1.2.2 Comments and Embedded Documentation 10
1.2.3 Constants, Variables, and Types 11
1.2.4 Operators and Precedence 13
1.2.5 A Sample Program 14
1.2.6 Looping and Branching 17
1.2.7 Exceptions 22
1.3 OOP in Ruby 25
1.3.1 Objects 26
1.3.2 Built-in Classes 26
1.3.3 Modules and Mixins 28
1.3.4 Creating Classes 29
1.3.5 Methods and Attributes 34
1.4 Dynamic Aspects of Ruby 36
1.4.1 Coding at Runtime 36
1.4.2 Reflection 38
1.4.3 Missing Methods 40
1.4.4 Garbage Collection 40
1.5 Training Your Intuition: Things to Remember 41
1.5.1 Syntax Issues 41
1.5.2 Perspectives in Programming 44
1.5.3 Ruby’s case Statement 47
1.5.4 Rubyisms and Idioms 50
1.5.5 Expression Orientation and Other Miscellaneous Issues 57
1.6 Ruby Jargon and Slang 59
1.7 Conclusion 62
2 Working with Strings 63
2.1 Representing Ordinary Strings 64
2.2 Representing Strings with Alternate Notations 65
2.3 Using Here-Documents 65
2.4 Finding the Length of a String 67
2.5 Processing a Line at a Time 68
2.6 Processing a Character or Byte at a Time 68
2.7 Performing Specialized String Comparisons 69
2.8 Tokenizing a String 71
2.9 Formatting a String 73
2.10 Using Strings as IO Objects 74
2.11 Controlling Uppercase and Lowercase 74
2.12 Accessing and Assigning Substrings 75
2.13 Substituting in Strings 78
2.14 Searching a String 79
2.15 Converting Between Characters and ASCII Codes 80
2.16 Implicit and Explicit Conversion 80
2.17 Appending an Item onto a String 83
2.18 Removing Trailing Newlines and Other Characters 83
2.19 Trimming Whitespace from a String 84
2.20 Repeating Strings 85
2.21 Embedding Expressions within Strings 85
2.22 Delayed Interpolation of Strings 86
2.23 Parsing Comma-Separated Data 86
2.24 Converting Strings to Numbers (Decimal and Otherwise) 87
2.25 Encoding and Decoding rot13 Text 89
2.26 Encrypting Strings 90
2.27 Compressing Strings 91
2.28 Counting Characters in Strings 92
2.29 Reversing a String 92
2.30 Removing Duplicate Characters 93
2.31 Removing Specific Characters 93
2.32 Printing Special Characters 93
2.33 Generating Successive Strings 94
2.34 Calculating a 32-Bit CRC 94
2.35 Calculating the SHA-256 Hash of a String 95
2.36 Calculating the Levenshtein Distance Between Two Strings 96
2.37 Encoding and Decoding Base64 Strings 98
2.38 Expanding and Compressing Tab Characters 98
2.39 Wrapping Lines of Text 99
2.40 Conclusion 100
3 Working with Regular Expressions 101
3.1 Regular Expression Syntax 102
3.2 Compiling Regular Expressions 104
3.3 Escaping Special Characters 105
3.4 Using Anchors 105
3.5 Using Quantifiers 106
3.6 Positive and Negative Lookahead 109
3.7 Positive and Negative Lookbehind 110
3.8 Accessing Backreferences 111
3.9 Named Matches 114
3.10 Using Character Classes 116
3.11 Extended Regular Expressions 118
3.12 Matching a Newline with a Dot 119
3.13 Using Embedded Options 119
3.14 Using Embedded Subexpressions 120
3.14.1 Recursion in Regular Expressions 121
3.15 A Few Sample Regular Expressions 122
3.15.1 Matching an IP Address 122
3.15.2 Matching a Keyword-Value Pair 123
3.15.3 Matching Roman Numerals 124
3.15.4 Matching Numeric Constants 125
3.15.5 Matching a Date/Time String 125
3.15.6 Detecting Doubled Words in Text 126
3.15.7 Matching All-Caps Words 127
3.15.8 Matching Version Numbers 127
3.15.9 A Few Other Patterns 127
3.16 Conclusion 128
4 Internationalization in Ruby 129
4.1 Background and Terminology 131
4.2 Working with Character Encodings 135
4.2.1 Normalization 136
4.2.2 Encoding Conversions 139
4.2.3 Transliteration 141
4.2.4 Collation 141
4.3 Translations 144
4.3.1 Defaults 146
4.3.2 Namespaces 147
4.3.3 Interpolation 148
4.3.4 Pluralization 149
4.4 Localized Formatting 151
4.4.1 Dates and Times 151
4.4.2 Numbers 152
4.4.3 Currencies 153
4.5 Conclusion 153
5 Performing Numerical Calculations 155
5.1 Representing Numbers in Ruby 156
5.2 Basic Operations on Numbers 157
5.3 Rounding Floating Point Values 158
5.4 Comparing Floating Point Numbers 160
5.5 Formatting Numbers for Output 162
5.6 Formatting Numbers with Commas 162
5.7 Working with Very Large Integers 163
5.8 Using BigDecimal 163
5.9 Working with Rational Values 166
5.10 Matrix Manipulation 167
5.11 Working with Complex Numbers 171
5.12 Using mathn 172
5.13 Finding Prime Factorization, GCD, and LCM 173
5.14 Working with Prime Numbers 174
5.15 Implicit and Explicit Numeric Conversion 175
5.16 Coercing Numeric Values 176
5.17 Performing Bit-Level Operations on Numbers 177
5.18 Performing Base Conversions 179
5.19 Finding Cube Roots, Fourth Roots, and So On 180
5.20 Determining the Architecture’s Byte Order 181
5.21 Numerical Computation of a Definite Integral 182
5.22 Trigonometry in Degrees, Radians, and Grads 183
5.23 Finding Logarithms with Arbitrary Bases 184
5.24 Finding the Mean, Median, and Mode of a Data Set 185
5.25 Variance and Standard Deviation 187
5.26 Finding a Correlation Coefficient 187
5.27 Generating Random Numbers 189
5.28 Caching Functions with Memoization 190
5.29 Conclusion 191
6 Symbols and Ranges 193
6.1 Symbols 193
6.1.1 Symbols as Enumerations 195
6.1.2 Symbols as Metavalues 196
6.1.3 Symbols, Variables, and Methods 197
6.1.4 Converting to/from Symbols 197
6.2 Ranges 199
6.2.1 Open and Closed Ranges 199
6.2.2 Finding Endpoints 200
6.2.3 Iterating Over Ranges 200
6.2.4 Testing Range Membership 201
6.2.5 Converting to Arrays 202
6.2.6 Backward Ranges 202
6.2.7 The Flip-Flop Operator 203
6.2.8 Custom Ranges 206
6.3 Conclusion 209
7 Working with Times and Dates 211
7.1 Determining the Current Time 212
7.2 Working with Specific Times (Post-Epoch) 212
7.3 Determining the Day of the Week 214
7.4 Determining the Date of Easter 215
7.5 Finding the Nth Weekday in a Month 215
7.6 Converting Between Seconds and Larger Units 217
7.7 Converting to and from the Epoch 217
7.8 Working with Leap Seconds: Don’t! 218
7.9 Finding the Day of the Year 219
7.10 Validating a Date or Time 219
7.11 Finding the Week of the Year 220
7.12 Detecting Leap Years 221
7.13 Obtaining the Time Zone 222
7.14 Working with Hours and Minutes Only 222
7.15 Comparing Time Values 223
7.16 Adding Intervals to Time Values 223
7.17 Computing the Difference in Two Time Values 224
7.18 Working with Specific Dates (Pre-Epoch) 224
7.19 Time, Date, and DateTime 225
7.20 Parsing a Date or Time String 225
7.21 Formatting and Printing Time Values 226
7.22 Time Zone Conversions 227
7.23 Determining the Number of Days in a Month 228
7.24 Dividing a Month into Weeks 229
7.25 Conclusion 230
8 Arrays, Hashes, and Other Enumerables 231
8.1 Working with Arrays 232
8.1.1 Creating and Initializing an Array 232
8.1.2 Accessing and Assigning Array Elements 233
8.1.3 Finding an Array’s Size 235
8.1.4 Comparing Arrays 235
8.1.5 Sorting an Array 237
8.1.6 Selecting from an Array by Criteria 240
8.1.7 Using Specialized Indexing Functions 242
8.1.8 Implementing a Sparse Matrix 244
8.1.9 Using Arrays as Mathematical Sets 244
8.1.10 Randomizing an Array 248
8.1.11 Using Multidimensional Arrays 249
8.1.12 Finding Elements in One Array But Not Another 250
8.1.13 Transforming or Mapping Arrays 250
8.1.14 Removing nil Values from an Array 251
8.1.15 Removing Specific Array Elements 251
8.1.16 Concatenating and Appending onto Arrays 253
8.1.17 Using an Array as a Stack or Queue 254
8.1.18 Iterating over an Array 254
8.1.19 Interposing Delimiters to Form a String 255
8.1.20 Reversing an Array 256
8.1.21 Removing Duplicate Elements from an Array 256
8.1.22 Interleaving Arrays 256
8.1.23 Counting Frequency of Values in an Array 257
8.1.24 Inverting an Array to Form a Hash 257
8.1.25 Synchronized Sorting of Multiple Arrays 258
8.1.26 Establishing a Default Value for New Array Elements 259
8.2 Working with Hashes 260
8.2.1 Creating a New Hash 260
8.2.2 Specifying a Default Value for a Hash 261
8.2.3 Accessing and Adding Key-Value Pairs 262
8.2.4 Deleting Key-Value Pairs 264
8.2.5 Iterating Over a Hash 264
8.2.6 Inverting a Hash 265
8.2.7 Detecting Keys and Values in a Hash 265
8.2.8 Extracting Hashes into Arrays 266
8.2.9 Selecting Key-Value Pairs by Criteria 266
8.2.10 Sorting a Hash 267
8.2.11 Merging Two Hashes 268
8.2.12 Creating a Hash from an Array 268
8.2.13 Finding Difference or Intersection of Hash Keys 268
8.2.14 Using a Hash as a Sparse Matrix 269
8.2.15 Implementing a Hash with Duplicate Keys 270
8.2.16 Other Hash Operations 273
8.3 Enumerables in General 273
8.3.1 The inject Method 274
8.3.2 Using Quantifiers 275
8.3.3 The partition Method 276
8.3.4 Iterating by Groups 277
8.3.5 Converting to Arrays or Sets 278
8.3.6 Using Enumerator Objects 278
8.4 More on Enumerables 280
8.4.1 Searching and Selecting 280
8.4.2 Counting and Comparing 281
8.4.3 Iterating 282
8.4.4 Extracting and Converting 283
8.4.5 Lazy Enumerators 284
8.5 Conclusion 285
9 More Advanced Data Structures 287
9.1 Working with Sets 288
9.1.1 Simple Set Operations 288
9.1.2 More Advanced Set Operations 290
9.2 Working with Stacks and Queues 291
9.2.1 Implementing a Stricter Stack 293
9.2.2 Detecting Unbalanced Punctuation in Expressions 294
9.2.3 Understanding Stacks and Recursion 295
9.2.4 Implementing a Stricter Queue 297
9.3 Working with Trees 298
9.3.1 Implementing a Binary Tree 298
9.3.2 Sorting Using a Binary Tree 300
9.3.3 Using a Binary Tree as a Lookup Table 302
9.3.4 Converting a Tree to a String or Array 303
9.4 Working with Graphs 304
9.4.1 Implementing a Graph as an Adjacency Matrix 304
9.4.2 Determining Whether a Graph Is Fully Connected 307
9.4.3 Determining Whether a Graph Has an Euler Circuit 308
9.4.4 Determining Whether a Graph Has an Euler Path 309
9.4.5 Graph Tools in Ruby 310
9.5 Conclusion 310
10 I/O and Data Storage 311
10.1 Working with Files and Directories 313
10.1.1 Opening and Closing Files 313
10.1.2 Updating a File 314
10.1.3 Appending to a File 315
10.1.4 Random Access to Files 315
10.1.5 Working with Binary Files 316
10.1.6 Locking Files 318
10.1.7 Performing Simple I/O 318
10.1.8 Performing Buffered and Unbuffered I/O 320
10.1.9 Manipulating File Ownership and Permissions 321
10.1.10 Retrieving and Setting Timestamp Information 323
10.1.11 Checking File Existence and Size 325
10.1.12 Checking Special File Characteristics 326
10.1.13 Working with Pipes 328
10.1.14 Performing Special I/O Operations 329
10.1.15 Using Nonblocking I/O 330
10.1.16 Using readpartial 331
10.1.17 Manipulating Pathnames 331
10.1.18 Using the Pathname Class 333
10.1.19 Command-Level File Manipulation 334
10.1.20 Grabbing Characters from the Keyboard 336
10.1.21 Reading an Entire File into Memory 336
10.1.22 Iterating Over a File by Lines 337
10.1.23 Iterating Over a File by Byte or Character 337
10.1.24 Treating a String As a File 338
10.1.25 Copying a Stream 339
10.1.26 Working with Character Encodings 339
10.1.27 Reading Data Embedded in a Program 339
10.1.28 Reading Program Source 340
10.1.29 Working with Temporary Files 340
10.1.30 Changing and Setting the Current Directory 341
10.1.31 Changing the Current Root 342
10.1.32 Iterating Over Directory Entries 342
10.1.33 Getting a List of Directory Entries 342
10.1.34 Creating a Chain of Directories 342
10.1.35 Deleting a Directory Recursively 343
10.1.36 Finding Files and Directories 343
10.2 Higher-Level Data Access 344
10.2.1 Simple Marshaling 345
10.2.2 “Deep Copying” with Marshal 346
10.2.3 More Complex Marshaling 346
10.2.4 Marshaling with YAML 347
10.2.5 Persisting Data with JSON 349
10.2.6 Working with CSV Data 350
10.2.7 SQLite3 for SQL Data Storage 352
10.3 Connecting to External Data Stores 353
10.3.1 Connecting to MySQL Databases 354
10.3.2 Connecting to PostgreSQL Databases 356
10.3.3 Object-Relational Mappers (ORMs) 358
10.3.4 Connecting to Redis Data Stores 359
10.4 Conclusion 360
11 OOP and Dynamic Features in Ruby 361
11.1 Everyday OOP Tasks 362
11.1.1 Using Multiple Constructors 362
11.1.2 Creating Instance Attributes 364
11.1.3 Using More Elaborate Constructors 366
11.1.4 Creating Class-Level Attributes and Methods 368
11.1.5 Inheriting from a Superclass 372
11.1.6 Testing Classes of Objects 374
11.1.7 Testing Equality of Objects 377
11.1.8 Controlling Access to Methods 378
11.1.9 Copying an Object 381
11.1.10 Using initialize_copy 383
11.1.11 Understanding allocate 384
11.1.12 Working with Modules 384
11.1.13 Transforming or Converting Objects 388
11.1.14 Creating Data-Only Classes (Structs) 390
11.1.15 Freezing Objects 391
11.1.16 Using tap in Method Chaining 393
11.2 More Advanced Techniques 394
11.2.1 Sending an Explicit Message to an Object 394
11.2.2 Specializing an Individual Object 396
11.2.3 Nesting Classes and Modules 399
11.2.4 Creating Parametric Classes 400
11.2.5 Storing Code as Proc Objects 403
11.2.6 Storing Code as Method Objects 405
11.2.7 Using Symbols as Blocks 406
11.2.8 How Module Inclusion Works 406
11.2.9 Detecting Default Parameters 409
11.2.10 Delegating or Forwarding 409
11.2.11 Defining Class-Level Readers and Writers 412
11.2.12 Working in Advanced Programming Disciplines 414
11.3 Working with Dynamic Features 416
11.3.1 Evaluating Code Dynamically 416
11.3.2 Retrieving a Constant by Name 418
11.3.3 Retrieving a Class by Name 418
11.3.4 Using define_method 419
11.3.5 Obtaining Lists of Defined Entities 423
11.3.6 Removing Definitions 425
11.3.7 Handling References to Nonexistent Constants 427
11.3.8 Handling Calls to Nonexistent Methods 429
11.3.9 Improved Security with taint 430
11.3.10 Defining Finalizers for Objects 432
11.4 Program Introspection 433
11.4.1 Traversing the Object Space 434
11.4.2 Examining the Call Stack 435
11.4.3 Tracking Changes to a Class or Object Definition 435
11.4.4 Monitoring Program Execution 439
11.5 Conclusion 441
12 Graphical Interfaces for Ruby 443
12.1 Shoes 4 444
12.1.1 Starting Out with Shoes 444
12.1.2 An Interactive Button 445
12.1.3 Text and Input 446
12.1.4 Layout 448
12.1.5 Images and Shapes 450
12.1.6 Events 450
12.1.7 Other Notes 451
12.2 Ruby/Tk 452
12.2.1 Overview 452
12.2.2 A Simple Windowed Application 453
12.2.3 Working with Buttons 455
12.2.4 Working with Text Fields 459
12.2.5 Working with Other Widgets 463
12.2.6 Other Notes 467
12.3 Ruby/GTK3 467
12.3.1 Overview 467
12.3.2 A Simple Windowed Application 468
12.3.3 Working with B
Hal Fulton first began using Ruby in 1999. In 2001, he started work on The Ruby Way, which was the second Ruby book published in English. Fulton was an attendee at the very first Ruby conference in 2001 and has presented at numerous other Ruby conferences on three continents, including the first European Ruby Conference in 2003. He holds two degrees in computer science from the University of Mississippi and taught computer science for four years. He has worked for more than 25 years with various forms of UNIX and Linux. He is now at Simpli.fi in Fort Worth, Texas, where he works primarily in Ruby.
André Arko first encountered Ruby as a student in 2004, and reading the first edition of this book helped him decide to pursue a career as a Ruby programmer. He is team lead of Bundler, the Ruby dependency manager, and has created or contributes to dozens of other open source projects. He works at Cloud City Development as a consultant providing team training and expertise on Ruby and Rails as well as developing web applications.
André enjoys sharing hard-won knowledge and experience with other developers, and has spoken at over a dozen Ruby conferences on four continents. He is a regular volunteer at RailsBridge and RailsGirls programming outreach events, and works to increase diversity and inclusiveness in both the Ruby community and technology as a field. He lives in San Francisco, California.
Praise for The Ruby Way, Third Edition
“Sticking to its tried and tested formula of cutting right to the techniques the modern day Rubyist needs to know, the latest edition of The Ruby Way keeps its strong reputation going for the latest generation of the Ruby language.”
Peter Cooper
Editor of Ruby Weekly
“The authors’ excellent work and meticulous attention to detail continues in this latest update; this book remains an outstanding reference for the beginning Ruby programmer– as well as the seasoned developer who needs a quick refresh on Ruby. Highly recommended for anyone interested in Ruby programming.”
Kelvin Meeks
Enterprise Architect
Praise for Previous Editions of The Ruby Way
“Among other things, this book excels at explaining metaprogramming, one of the most interesting aspects of Ruby. Many of the early ideas for Rails were inspired by the first edition, especially what is now Chapter 11. It puts you on a rollercoaster ride between ‘How could I use this?’ and ‘This is so cool!’ Once you get on that rollercoaster, there’s no turning back.”
David Heinemeier Hansson
Creator of Ruby on Rails,
Founder at Basecamp
“The appearance of the second edition of this classic book is an exciting event for Rubyists–and for lovers of superb technical writing in general. Hal Fulton brings a lively erudition and an engaging, lucid style to bear on a thorough and meticulously exact exposition of Ruby. You palpably feel the presence of a teacher who knows a tremendous amount and really wants to help you know it too.”
David Alan Black
Author of The Well-Grounded Rubyist
“This is an excellent resource for gaining insight into how and why Ruby works. Assomeone who has worked with Ruby for several years, I still found it full of new tricks and techniques. It’s accessible both as a straight read and as a reference that one can dip into and learn something new.”
Chet Hendrickson
Agile software pioneer
“Ruby’s a wonderful language–but sometimes you just want to get something done. Hal’s book gives you the solution and teaches a good bit about why that solution is good Ruby.”
Martin Fowler
Chief Scientist, ThoughtWorks
Author of Patterns of Enterprise Application Architecture