100% found this document useful (4 votes)
48 views76 pages

(FREE PDF Sample) Hands-On Test-Driven Development: Using Ruby, Ruby On Rails, and RSpec 1st Edition Greg Donald Ebooks

Greg

Uploaded by

faluavaltea32
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (4 votes)
48 views76 pages

(FREE PDF Sample) Hands-On Test-Driven Development: Using Ruby, Ruby On Rails, and RSpec 1st Edition Greg Donald Ebooks

Greg

Uploaded by

faluavaltea32
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 76

Download Full Version ebookmass - Visit ebookmass.

com

Hands-on Test-Driven Development: Using Ruby, Ruby


on Rails, and RSpec 1st Edition Greg Donald

https://ebookmass.com/product/hands-on-test-driven-
development-using-ruby-ruby-on-rails-and-rspec-1st-edition-
greg-donald/

OR CLICK HERE

DOWLOAD NOW

Discover More Ebook - Explore Now at ebookmass.com


Instant digital products (PDF, ePub, MOBI) ready for you
Download now and discover formats that fit your needs...

Hands-on Test-Driven Development: Using Ruby, Ruby on


Rails, and RSpec 1 / converted Edition Greg Donald

https://ebookmass.com/product/hands-on-test-driven-development-using-
ruby-ruby-on-rails-and-rspec-1-converted-edition-greg-donald/

ebookmass.com

Test-Driven Development in Go Simion

https://ebookmass.com/product/test-driven-development-in-go-simion/

ebookmass.com

Gallows Hill Lois Ruby

https://ebookmass.com/product/gallows-hill-lois-ruby/

ebookmass.com

TSV 3D RF Integration: High Resistivity Si Interposer


Technology 1st Edition Shenglin Ma

https://ebookmass.com/product/tsv-3d-rf-integration-high-resistivity-
si-interposer-technology-1st-edition-shenglin-ma/

ebookmass.com
The Globalization of World Politics: An Introduction to
International Relations 9th Edition John Baylis

https://ebookmass.com/product/the-globalization-of-world-politics-an-
introduction-to-international-relations-9th-edition-john-baylis/

ebookmass.com

Systems Architecture Modeling with the Arcadia Method: A


Practical Guide to Capella 1st Edition Pascal Roques

https://ebookmass.com/product/systems-architecture-modeling-with-the-
arcadia-method-a-practical-guide-to-capella-1st-edition-pascal-roques/

ebookmass.com

Abnormal Psychology: An Integrative Approach 6th Canadian


Edition David H. Barlow

https://ebookmass.com/product/abnormal-psychology-an-integrative-
approach-6th-canadian-edition-david-h-barlow/

ebookmass.com

The Moonlight Blade Tessa Barbosa

https://ebookmass.com/product/the-moonlight-blade-tessa-barbosa/

ebookmass.com

On the Trail of Capital Flight from Africa: The Takers and


the Enablers Léonce Ndikumana (Editor)

https://ebookmass.com/product/on-the-trail-of-capital-flight-from-
africa-the-takers-and-the-enablers-leonce-ndikumana-editor/

ebookmass.com
The Acolyte’s Education (Kontra's Menagerie Book 32)
Charlie Richards

https://ebookmass.com/product/the-acolytes-education-kontras-
menagerie-book-32-charlie-richards/

ebookmass.com
Hands-on
Test-Driven
Development
Using Ruby, Ruby on Rails, and RSpec

Greg Donald
Hands-on Test-Driven Development
Greg Donald

Hands-on Test-Driven
Development
Using Ruby, Ruby on Rails, and RSpec
Greg Donald
Clarksville, TN, USA

ISBN-13 (pbk): 978-1-4842-9747-6 ISBN-13 (electronic): 978-1-4842-9748-3


https://doi.org/10.1007/978-1-4842-9748-3

Copyright © 2024 by Greg Donald


This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the material is concerned,
specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in
any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by
similar or dissimilar methodology now known or hereafter developed.
Trademarked names, logos, and images may appear in this book. Rather than use a trademark symbol with every occurrence of
a trademarked name, logo, or image we use the names, logos, and images only in an editorial fashion and to the benefit of the
trademark owner, with no intention of infringement of the trademark.
The use in this publication of trade names, trademarks, service marks, and similar terms, even if they are not identified as such,
is not to be taken as an expression of opinion as to whether or not they are subject to proprietary rights.
While the advice and information in this book are believed to be true and accurate at the date of publication, neither the authors
nor the editors nor the publisher can accept any legal responsibility for any errors or omissions that may be made. The publisher
makes no warranty, express or implied, with respect to the material contained herein.

Managing Director, Apress Media LLC: Welmoed Spahr


Acquisitions Editor: Melissa Duffy
Development Editor: James Markham
Coordinating Editor: Gryffin Winkler

Cover designed by eStudioCalamar


Cover image by [email protected]

Distributed to the book trade worldwide by Apress Media, LLC, 1 New York Plaza, New York, NY 10004, U.S.A. Phone
1-800-SPRINGER, fax (201) 348-4505, e-mail [email protected], or visit www.springeronline.com. Apress Media,
LLC is a California LLC and the sole member (owner) is Springer Science + Business Media Finance Inc (SSBM Finance
Inc). SSBM Finance Inc is a Delaware corporation.
For information on translations, please e-mail [email protected]; for reprint, paperback, or audio rights,
please e-mail [email protected].
Apress titles may be purchased in bulk for academic, corporate, or promotional use. eBook versions and licenses are also
available for most titles. For more information, reference our Print and eBook Bulk Sales web page at http://www.apress.com/
bulk-sales.
Any source code or other supplementary material referenced by the author in this book is available to readers on GitHub (https://
github.com/Apress). For more detailed information, please visit https://www.apress.com/gp/services/source-code.

Paper in this product is recyclable


This book is dedicated to my wife and best friend, Sunni. She
has been a constant source of support and encouragement
during the writing of this book.
Contents

1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
What Are We Building? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Our Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Why Another TDD Book? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
My Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Why You Should Trust Me . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Audience and Prerequisites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
How to Read This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 What Is Test-Driven Development? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Problems with Late Test Writing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
No Code Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Passing by Accident . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Code Spikes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
The Red-Green-Refactor Development Cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Red Phase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Green Phase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Refactor Phase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Wash, Rinse, and Repeat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Advantages of Building Software Using TDD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Code Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
No Broken Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Maintaining Focus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Acceptance Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Code Quality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3 Getting Started with Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Installing Homebrew . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Installing rbenv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Installing Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4 Getting Started with Ruby on Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Installing Bundler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Installing Ruby on Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Installing PostgreSQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Installing Node.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

vii
viii Contents

Creating a New Ruby on Rails Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20


Running Rails Locally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
5 Setting Up RSpec and FactoryBot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Installing RSpec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Installing FactoryBot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
6 Adding Initial Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
The User Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
The User Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
How to Run Specs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
User Model Validations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
The Page Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
RSpec’s “subject” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
FactoryBot “build” vs. “create” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
The Page slug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Page Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Final Thoughts on Model Validations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
User Spec Cleanup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Advice on Writing Too Many Specs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7 Creating Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
8 Build Homepage Contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
View Specs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
Shared Partial Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
More View Specs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Homepage Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
CSS Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Layout Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Sidebar Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
9 Sidebar Contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Search As a Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Implementing a Search Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Searching by Term . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Searching for Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Adding a Search Form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Displaying Search Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Pages Archive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Creating a List of Months and Years . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Archive List View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Archive Integration Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Contents ix

10 Page Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105


Adding Page Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Creating the Tag Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Creating the Page Tag Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Jumping Over Relationships . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Tagging Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
After Save Callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Display Tags on Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
Handling Missing Tags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
Refactor Duplicate Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
11 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Active Storage for Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Getting Started with Active Storage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Adding an Image Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Serving Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Adding an Image Controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
12 User Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Improving Our User Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Password Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Adding Fields to Our User Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Add a Hashing Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Authenticating a User . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Improving Authentication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Password Confirmation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Skipping Password Validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
13 Administration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
Adding Active Admin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
Adding the Gem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
Testing Active Admin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Logging In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Admin Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Managing Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Overriding Active Admin Form Field Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Factoring Out Duplicate Login Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
Previewing a Page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Managing Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Viewing Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Uploading New Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
Editing Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
Deleting Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
Admin Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
Managing Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Adding New Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
x Contents

Deleting Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185


Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
14 Odds and Ends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Build a Sitemap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Pagination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
Fixing Specs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
Styling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
CSS in Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Styling Our Public Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
Styling Our Admin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Wrapping Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Code Highlighting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
Stimulus and Turbo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
15 Bonus: Deploy to Production . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Why This Non-cloud Approach? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Acquiring a Production Linux Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Getting Production Ready . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
Installing Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
Installing Ruby . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
Installing Phusion Passenger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
Configuring PostgreSQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Storing Code on GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Installing Capistrano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Configuring Capistrano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
Deploying Our Rails Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Configuring Our Apache Virtual Host . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Logging In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
About the Author

Greg Donald has been a professional software engineer since


1996 and has worked with Ruby on Rails since 2006. Prior
to that, he worked on many Perl and PHP projects, finding
the applications messy and hard to maintain. He became a test
driven development enthusiasts after having seen the results of
software written in various forms, and determining no style was
more successful than TDD. He decided to write this book to
promote TDD and share his positive experiences using it.

xi
About the Technical Reviewer

Eldon is a senior technologist, creator, and United States Marine Corps veteran. He’s the author
of two software development books. His career has allowed him to work across a wide variety of
technologies and domains. His career has taken him from working in successful startups in roles
ranging from senior developer to chief architect to working as a principal technologist for a global
software consulting firm, helping enterprises and companies rescue software projects and/or evolve
their software delivery capabilities.
These days, he works as a technical fellow with a mission of raising the bar of software
development globally for a technology company providing tools, AI, and research to advance the
rule of law.
Currently, he lives in Florida where he enjoys scuba diving and creating video content.

xiii
Introduction
1

Hello, and welcome! I’m very excited to share with you my book about test-driven software
development. I’ve been a software engineer for over 25 years, and I’ve seen the results of software
written using many different styles and techniques. It wasn’t so long ago that we didn’t even write tests
for our software. At some point, some very smart people realized that writing tests for our software
was actually a good idea. Our tests would provide us with confidence that our software worked as
expected and even more importantly keep us from getting phone calls in the middle of the night to fix
things that would break in production.
Not long after we started writing tests, some other very smart people realized that writing our tests
first would give us an opportunity to think about what we were going to write before we would write
it. It would give us a target to aim at, in effect. This was the birth of test-driven development (TDD).
TDD is a style of software development where we write our tests first, see that they are failing as
expected, and only then do we pursue writing the code to make those tests pass.
This is the central idea of my book. We will think about what we want to do, then we will capture
that idea with a properly failing test. We will then write the code to make the test pass. We will then
refactor our code to make it better, or prettier, or less repetitive, and then we will write another test.
We will repeat this process over and over until we have a fully functional web application.
Let’s get started!

What Are We Building?

As you pick up this book and look inside at the first few pages, you may be wondering, what exactly
are we going to build? I’m so glad you asked! We will build a fully functional blog! A “blog,” short
for “weblog” or “web log,” is a web application that allows its administrator (you) to post articles to
the Web. Instead of downloading premade blog software, for example, WordPress,1 we’re going to
build our own blog software from scratch.
We’ll be using the Ruby2 programming language and the Ruby on Rails3 web framework to build
everything. These are the best tools for the job, in my opinion. Ruby is a very expressive and powerful
programming language, and Ruby on Rails is a very powerful web framework.
1 https://wordpress.org/
2 https://ruby-lang.org/
3 https://rubyonrails.org/

© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2024 1
G. Donald, Hands-on Test-Driven Development,
https://doi.org/10.1007/978-1-4842-9748-3_1
2 1 Introduction

Figure 1-1 Blog database diagram

We’ll use the test-driven development (TDD) style of software design and engineering, which
means we’ll write failing tests first and only then write our implementation code to get our failing
tests passing.
We’ll use the RSpec4 testing framework to write our tests. RSpec describes itself as a tool for
performing “behavior-driven development” and is in fact the best tool available for testing Ruby
code. If you’ve never used RSpec before, don’t worry; in this book, I’ll cover more than enough to
get you started and productive.
Our blog will be a simple web application overall, but will be complex enough to demonstrate the
test-driven development style of software development. Our blog will allow us to create and update
pages as well as search them by keyword and tag them for easy categorization.
Let’s take a look at our database design next.

Our Database

We will use the PostgreSQL5 relational database management system (RDBMS) to store our blog’s
data. When it comes to free and open source databases, PostgreSQL is best in its class.
At the end of the book, our database design will look similar to the diagram in Figure 1-1.

4 https://rspec.info/
5 https://postgresql.org/
Why Another TDD Book? 3

We’ll go into much more detail about our database design in later chapters, but I want to give you
an initial idea of what we’ll be building early on. We will have database tables for users, pages, tags,
and images. Our image table is not connected to any of the other previously mentioned tables. Our tag
table is connected to our page table through our page_tags join table. Each of our pages will belong
to a single user. With this design, we can have more than one user, and each user can be an author of
many pages.
We will build up our database design as we go, adding new tables and columns as we need them,
as that’s the nature of practicing proper test-driven development.

Why Another TDD Book?

The main goal of this book is to provide a practical, hands-on introduction to the test-driven style of
software design and engineering.
Many books on the topic of test-driven development have been written before this one. Some of
them are actually very good, but none of them were written in the practical, hands-on style of this
book. In fact, very few books are written in this style.
This book covers the building of an entire web application, from start to finish. Our focus
application, a blog, is the most practical thing I could think to build, given my audience of software
engineering readers. Every software engineer has their own online blog, right? A blog is simple
enough that it can be covered in a single book, but complex enough that it can be used to demonstrate
my favorite parts of RSpec while building a Ruby on Rails web application.
Most other TDD books are written in a small-problem sort of writing style. They cover single
topics, one at a time, barely more than a reference guide. They do not cover the entire process of
building a software application from start to finish, using TDD. As a result, they are, in my opinion,
less practical and much less useful for actually learning practical TDD.

My Motivation

About five years ago, I joined a team of software engineers who were busy working on a very
important (people’s lives were at stake) large-scale web application. They were several months into
it at the time, and there were deadlines. The application was being written in Ruby on Rails, and the
team was using TDD to build it. They were practicing Extreme Programming (XP),6 as described
by Kent Beck,7 pretty much to the letter. I had heard of TDD and XP before, but had never actually
practiced them. I had never even pair-programmed8 an entire day prior to joining the team. I was
eager to learn, and little did I know at the time that I was working with some of the best software
engineers I had ever met.
These girls and guys were amazingly agile. They refactored their code constantly. They were
always looking for ways to reduce code complexity and improve their processes. They
were constantly learning new things, and they were always teaching each other new things. They
were very passionate about their craft and passionate about leveling up their team even more so.
I soon realized I was in over my head. I was a n00b to the team, to TDD, and to XP. They did not
seem to care about any of that.

6 www.extremeprogramming.org/
7 http://wiki.c2.com/?KentBeck
8 http://wiki.c2.com/?PairProgramming
4 1 Introduction

They were very patient with me, and they were completely willing to teach me everything they
knew. They taught me how to practice proper TDD and XP. They taught me how to write failing tests
first and how to write code that is easy to test. They taught me how to write tests that supplied courage
for when I would later perform fearless refactoring.
Over the next couple of years, I became a much better software engineer. The first major change I
noticed in myself was how I had stopped worrying about code I had recently pushed into production.
I was no longer thinking about code breaking in production during my off hours or how these sorts
of breakages were often followed by an emergency phone call to hurry to fix things. I had stopped
worrying about code breaking because I knew my code was completely covered with well-written
tests and very unlikely to break.
Another change I noticed in myself was that I was writing cleaner code. I was writing code that
was easier to later understand. I was only writing the code that was necessary to get my tests into a
passing state. I got into a pattern of knowing that once my tests pass, my implementation work was
done. Moving on to refactor any ugliness or code duplication is my only next step before writing my
next failing test.
I’m telling you all of this because I want you to understand how I’ve come to learn and respect
test-driven development. I learned it by doing it, for many years, with a team of experts. I know that
developing software using TDD produces better software than not using TDD to develop software. I
know this because I have seen the final results with my own eyes.

Why You Should Trust Me

I’ve been a computer programmer for a long time. If we’re counting uncompensated work, then I’ve
been programming since 1984, when I wrote my first bits of BASIC9 code on my grandpa’s TI-
99/4A,10 saving my work to a cassette tape. I went on to write more advanced programs and games
in BASIC at my junior high school, where we had Apple IIe11 computers in our computer lab. A few
years later, in high school, we had IBM PC clones12 in our computer lab, and it was at this time I
learned to program in Turbo Pascal.13
I joined the US Navy in 1990 and didn’t have much contact with computers for the next few years.
When I got out in 1995, I discovered the World Wide Web was a thing, and I became instantly hooked
on my newly found favorite hobby, building web pages. I taught myself HTML and enough Perl14
to be dangerous, and by late 1996, I had landed my first paid programming job as a web developer
working on Perl-based shopping cart software.
So if we’re counting compensated work, I’ve been a self-taught professional software engineer
since late 1996. I’ve written code in almost every major programming language and on many different
operating systems and platforms. My focus has been on web development for the most part, but I
also have significant experience working on mobile applications and games, in native code, for both
Android and iOS. I was an early publisher on Google’s Android platform, having the first published
Blackjack game there.

9 http://hopl.info/showlanguage.prx?exp=176
10 www.ti994.com/
11 www.old-computers.com/museum/computer.asp?st=1&c=83
12 www.ibm.com/ibm/history/exhibits/pc/pc_1.html
13 http://progopedia.com/implementation/turbo-pascal/
14 www.perl.org/
How to Read This Book 5

In 2005–2006, I started working with Ruby on Rails. I had some experience with Ruby before
that, automating processes on Linux, but didn’t get into Ruby seriously until I started working with
Rails. Rails was my first experience using the model-view-controller15 (MVC) design pattern, and it
was a revelation to me. Prior to that, I had worked on many Perl and PHP16 projects, and I had always
thought they were messy and hard to maintain, and there was certainly never anything like a test-suite
present.
I tell you all this to instill a bit of trust. I’ve seen software written many different ways, and I’ve
seen the results of those pursuits. I’ve never seen software written using TDD that was not better than
software written without TDD.

Audience and Prerequisites

I’ve seen many programmers, both beginners and those who have been programming for years, who
can’t begin to imagine writing failing tests first and only then writing the code to make those tests
pass. This book will be useful in teaching you how to do that, providing confidence and courage.
This book is written for beginner to intermediate Ruby programmers who are interested in learning
to use TDD with RSpec to test and build web applications using Ruby on Rails. It is assumed that
you have some experience with Ruby and Ruby on Rails and that you have read the Getting Started
with Rails17 guide that’s available for Rails, or something similar.

How to Read This Book

This book should be read in a linear fashion, from beginning to end. The chapters are numbered and
should be read in that order. This is not a reference guide and does not provide complete coverage of
any particular topic. Those kinds of reference books already exist for many of the topics discussed in
this book, and duplicating their authors’ efforts would not provide much value.
This is a work-along book. Every effort has been made to make this book self-contained, so that
you can read it from cover to cover and not need to refer to any other resources. Nevertheless, I’ve
been quite liberal in my use of footnote references to other resources, so please do visit them for
additional information on unfamiliar topics.

15 http://wiki.c2.com/?ModelViewControllerHistory
16 www.php.net/
17 https://guides.rubyonrails.org/getting_started.html
What Is Test-Driven Development?
2

Test-driven development is a powerful software development technique where the test (or spec) is
written before the implementation code. Initially, the test will fail because the implementation code
does not exist yet. This situation with a failing test is expected and is known to drive out minimal
implementation code changes or additions to get the failing test into a passing state. It may sound
complicated at first, but with a thorough demonstration of the benefits, followed by a bit of practice,
you’ll begin to prefer doing things this way.
But what happens when we don’t do test-driven development? What happens when we write our
tests after the implementation code? I’m so glad you asked!

Problems with Late Test Writing

In this section, we will explore some of the problems with writing tests after the implementation code
has already been written.

No Code Design

When we write our tests after the implementation code, we’re typically not thinking too much about
the design of our code. We’re mostly just writing tests to make sure our code works. This is a very
different, and less thoughtful, mindset than when we’re writing tests before the implementation code.
When we write our tests before the implementation code, we’re thinking about the design of our
code, how we want our code to behave and be used. We’re hopefully also thinking about how we want
our code to be extended in the future, probably by us.

Passing by Accident

When we write our tests after the implementation code, we will inevitably write tests that pass by
accident. This is because we’re not taking the time to see the tests fail first.
We should never trust tests that we did not see fail first.

© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2024 7
G. Donald, Hands-on Test-Driven Development,
https://doi.org/10.1007/978-1-4842-9748-3_2
8 2 What Is Test-Driven Development?

The one exception is that you may work on a team where you will not actually see every single
newly written test fail before it’s made to pass. Obviously, we don’t want to delete our team members’
tests and hard work. But we should always have a high degree of skepticism about our own tests and
pursue seeing them fail appropriately.
This advice of trusting tests based on individual experience with seeing them fail also comes with
a bit of counter advice: don’t ever be afraid to delete a test that cannot be made to fail by temporarily
reversing or commenting implementation code. Delete unfailable tests as soon as possible. Your
continuous integration1 server will thank you.

Code Spikes

Sometimes, we may need to explore our ideas, with code, a bit before we know what our desired
behavior will be. Often, we write this sort of code knowing up front we will eventually throw it away.
This act of writing throwaway code is known as code spiking and almost certainly also accompanies
the deliberate act of not writing any tests beforehand. This situation should be rare, and we should
always be thinking about how we can write tests that will fail when we’re done with exploring our
ideas.
Practically speaking, anytime I’m done with spiking some bits of code, I will comment the code
out and then proceed with writing failing tests. I then uncomment my spiked code, a bit at a time
usually, to get my tests passing. This is a very useful technique for safely exploring ideas with code
and for junior TDD practitioners who have a hard time with test writing.
We’ve now explored some of the problems with writing tests after the implementation code. Let’s
now explore the benefits of writing tests before the implementation code.

The Red-Green-Refactor Development Cycle

Test-driven development can be thought of as a three-phase development cycle as shown in


Figure 2-1.

1. Red
2. Green
3. Refactor

Let’s step through each phase and discuss it in more detail, with simple but practical code
examples.

Red Phase

In the red phase of the TDD cycle, an initially failing test is written. Most often, this will just be a
single test. It should be a minimal concept or idea designed to drive out a desired behavior.

1 http://wiki.c2.com/?ContinuousIntegration
The Red-Green-Refactor Development Cycle 9

Figure 2-1 The red-green-refactor development cycle

Imagine we want a method that adds two numbers and returns a result. An example failing test
might be

it 'equals 4' do
result = add(2, 2)
expect(result).to eq(4)
end

This test will fail because there is a call to an add method that currently does not exist. This is a very
useful test because it will focus our attention on our implementation code changes.
Notice how most of the test is written in a way that it flows like a couple of English sentences.
This is RSpec’s developer-friendly DSL in action; our tests are easy to read and understand.

Green Phase

Next, we add our implementation code changes to turn the red failing test into a green passing test.
The first error we get is the missing method error. We could add an empty method to satisfy the
error:

def add
end

After adding that, our test will still fail, but it will fail with a different error. It will complain about
the argument signature being wrong. We can fix that error with adding the desired arguments to the
method signature:

def add(x, y)
end
10 2 What Is Test-Driven Development?

Finally, we get to the end where the empty method returns nil instead of the value of the arguments
being added together. We can fix that by implementing the arguments being added together and
returned:

def add(x, y)
x + y
end

It depends on the situation and your own experience level with the codebase for how fine-grained you
choose to have your red-green-refactor loop operate. I think stepping through it like this is a good
way to get started with TDD, but I certainly don’t always do it this way. I sometimes even find myself
writing more than one test at a time and then implementing the code to make them all eventually pass.

Refactor Phase

After the green phase, we reach the refactor phase. In this simple example, there’s not much that can
be refactored, but that’s a very short-term problem for anything larger than a small hello-world sort of
program. Imagine if you decided to rename the add method’s x and y arguments to something else;
perhaps you decide a and b would be more appropriate. The updated add method might then look
like this:

def add(a, b)
a + b
end

This sort of refactoring is considered safe since there is a test that will inform us if we break the
implementation code. When we run the test again, it should still pass, letting us know our refactor
was successful.
Any sort of refactoring done outside of proper test coverage is dangerous. Software engineers who
practice test-driven development always prefer implementation code changes be done under test, even
if that means backfilling missing test coverage first.
This section gave you a small taste of what you’re in store for with the rest of the book.

Wash, Rinse, and Repeat

The instructions from the back of the shampoo bottle “wash, rinse, and repeat” imply an endless cycle
of performing the same tasks again and again (or just twice depending on how literal you take the
meaning of the word “repeat”). In TDD, this is exactly what we do. At the end of our refactor phase,
Advantages of Building Software Using TDD 11

we’re ready to repeat the cycle and begin with a new failing test. A repetitious red, green, refactor
loop informs us sooner rather than later when we’ve accidentally gone down a wrong path.

Advantages of Building Software Using TDD

In the next section, we will discuss the benefits of practicing test-driven development. These benefits
are not just for the individual software engineer but also for the entire software engineering team.

Code Design

Test-driven development encourages us to think through our code design before implementing the
code.
Every experienced software engineer has at some point in their career worked on a poorly designed
application codebase. Often, these codebases have little or no test coverage, making refactoring and
improving them very dangerous. Even the smallest change over here could cause some unexpected
breakage over there. Surprisingly, broken code in production is no fun at all!
TDD gives us the power to make small incremental changes to our code design without the danger
of unknowingly breaking things. Sure, sometimes our changes do break things; that’s to be expected.
But with adequate test coverage, these breakages will alert us as soon as possible, so we can make
informed decisions about the direction of our evolving code design. We may choose to stop and undo
our latest change, or we may choose to push on and repair our test failures. Either way, it causes us to
consider everything more carefully before we proceed.

No Broken Windows

Broken Window Theory refers to urban decay. A single unfixed broken window in a neighborhood
leads to a second broken window and so on until the entire community is in complete disrepair. This
way of thinking about urban decay easily lends itself to how we think about software engineering.
The idea of a broken window in software engineering can manifest itself in different ways.
The first way that comes to my mind is when a bug appears in the codebase and (hopefully) causes
a test to begin to fail, and then for whatever reason no one immediately pursues fixing it. The longer
the unfixed bug lives on, the more risk there is for data corruption, user dissatisfaction, lesser revenues,
or security issues.
Another type of broken window is a sort of generic term we software engineers refer to as technical
debt, the unpaid time we still owe to the codebase that we didn’t pay when it was initially due. This
can be anything like outdated software dependencies or duplicate lines of code that need to be factored
out into a shared code module.
The longer these broken windows accumulate on top of each other, the more effort required to fix
them when we do eventually get around to it.
“I’ll fix it, no need to remind me every 8 months!”
The most important phase of TDD is the green phase. We never proceed with new development
without getting our tests back to a fully passing state. A “green” codebase tells us all known software
bugs are fixed and any recently updated software dependencies are working as expected.
Good software engineers fix their broken windows as soon as possible!
12 2 What Is Test-Driven Development?

Maintaining Focus

TDD keeps you focused on the problem, and just the problem.
There’s a software principle and acronym YAGNI2 that means you aren’t gonna need it. TDD-
focused software engineers believe, when given a user story with well-written acceptance criteria, it
is the job of the software engineers to do no more and no less than exactly what the story requests be
done.
There is real danger in adding things not explicitly requested in the user story. Another engineer
could already be adding the thing you are thinking to add while working on their current story. Or
there could be a follow-on story where the new behavior is described there. We just don’t know what
tomorrow’s user stories will bring, and with finite resources, it’s better to not guess.

Acceptance Testing

TDD lends itself naturally to automatically validated code. If your tests closely match the given
acceptance criteria, then your implementation code will usually result in the desirable feature changes
and behavior.
A very nice side effect of using TDD is that it tells you exactly when you are done working on a
user story. If all the new acceptance criteria tests are passing and there are no more refactorings in
sight, then it’s usually time to deliver the story. So ship it!

Code Quality

TDD helps software engineers maintain high code quality. Implementation code is typically leaner
and more precise than code written outside of TDD. Code refactorings occur more often when tests
exist, helping to reduce the danger of change. We software engineers feel more empowered to pursue
code cleanups and refactorings when we know the risk of breaking anything is lower.
These ways of thinking about software engineering play out across software engineering teams
too. An individual team member is much more likely to care about code quality if other engineers are
seen caring too.

Summary

This chapter discussed the philosophy and benefits of practicing test-driven development. You may
not yet be convinced that TDD is the right way to go; I certainly wasn’t convinced early on. It just
takes time to reprogram your brain to think to write the tests first, especially if you’ve been writing
software for a while already. Don’t give up; the struggle is worth the reward!

2 www.techtarget.com/whatis/definition/You-arent-gonna-need-it
Getting Started with Ruby
3

In this chapter, we’ll install tools we can use to manage our installed Ruby versions. We will
issue commands to our local operating system via the command-line interface1 (CLI) using a shell
interpreter, which depending on your operating system is likely to be bash or zsh. On macOS, we
can use the Terminal application to issue these commands, and similar programs are available on
other operating systems:

1. Homebrew
2. rbenv
3. Ruby

Installing Homebrew

On macOS, I use Homebrew2 to install most of my local software development stack. There’s a shell
command on the Homebrew homepage I use to install it:

URL=https://raw.githubusercontent.com/Homebrew/\
install/HEAD/install.sh
bash -c "$(curl -fsSL $URL)"

If trusting this way of installing Homebrew via direct URL execution makes you nervous, you can
install Homebrew manually too:
https://docs.brew.sh/Installation

1 www.codecademy.com/article/command-line-interface
2 https://brew.sh

© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2024 13
G. Donald, Hands-on Test-Driven Development,
https://doi.org/10.1007/978-1-4842-9748-3_3
14 3 Getting Started with Ruby

Homebrew also works on Linux and Windows Subsystem for Linux. If you’re working under those
environments, you can find more information here:
https://docs.brew.sh/Homebrew-on-Linux

Installing rbenv

As previously mentioned, we will use rbenv3 to manage our Ruby installations. Using rbenv
allows us to easily switch between different versions of Ruby with just a few keystrokes. This provides
us with flexibility to use different versions of Ruby for different projects and to easily upgrade to
newer versions of Ruby when they are released.
We can install rbenv with the following brew command:

brew install rbenv

After installation, rbenv then requires initialization in our command SHELL.4 We will place this
initialization in our .bashrc or .zshrc file depending on which SHELL we are using. This will
allow us to use rbenv commands in our shell and have it automatically configured anytime we start
a new shell.
zsh users will add this to the end of “.zshrc” and then source it:

echo 'eval "$(rbenv init - zsh)"' >> ~/.zshrc

. ~/.zshrc

bash users will perform a similar command and then source it:

echo 'eval "$(rbenv init - bash)"' >> ~/.bashrc

. ~/.bashrc

If you’re not sure which SHELL you’re using, you can find out by issuing the command echo
$SHELL.

Installing Ruby

Installing Ruby will require we first install some dependencies. Our Ruby compilation will fail or may
not function correctly if we are missing these dependencies. We can install them from Homebrew
using the brew command again:

3 https://github.com/rbenv/rbenv
4 https://datacarpentry.org/shell-genomics/01-introduction/
Installing Ruby 15

brew install openssl readline

Now that we have our dependencies installed, we can use “rbenv” to install the latest version of Ruby:

rbenv install 3.2.2

This command may take several minutes or longer to complete depending on our system specs.
Please note, the latest version will probably be different by the time you’re reading this book. You
can find the current latest version by running the rbenv list command with grep and sort, like
this:

rbenv install -L | grep -E '^\d' | sort -V

Using grep with the proper regular expression removes all the more exotic implementations of Ruby
from the list: jruby, rbx, and others. In this book, we’re going to use the original MRI5 (Matz’s Ruby
Interpreter) version of Ruby, created by Yukihiro Matsumoto6 and friends.
After rbenv installs your chosen version of Ruby, you can set that version to be your global
version:

rbenv global 3.2.2

Now we can run the which command and see that ruby and the Ruby gem command are both
installed via rbenv shims:

which ruby
/Users/gd/.rbenv/shims/ruby

which gem
/Users/gd/.rbenv/shims/gem

If these which commands do not work for you right away, you may need to restart your terminal or
shell application.
rbenv shims are used to provide us the ability to easily switch between Ruby versions. If you
would like to pursue a deeper understanding of rbenv shims, more information is available.7

5 www.ruby-lang.org
6 https://dbpedia.org/page/Yukihiro_Matsumoto
7 https://github.com/rbenv/rbenv#understanding-shims
16 3 Getting Started with Ruby

Let’s now check that ruby is actually executable and is the version we installed:

ruby -v
ruby 3.2.2 (2023-03-30 revision e51014f9c0)

Success!
It’s also worth mentioning that there are other ways to accomplish what we’ve done here. For
example, instead of using rbenv we could have instead used rvm.8 On macOS, instead of Homebrew
we could have used MacPorts.9 My point is that if you already have a preference for other tooling to
manage your Ruby installations, then feel free to use them.

Summary

In this chapter, we installed Homebrew, rbenv, and Ruby. We learned to use the brew command
to install software on our local machine. We also learned how to use rbenv to manage our Ruby
versions. In the future, we can easily switch to a new version of Ruby with a single command. We
can also now have any number of Ruby projects using different versions of Ruby.

8 https://rvm.io/
9 www.macports.org/
Getting Started with Ruby on Rails
4

In the previous chapter, we installed the Ruby language itself. In this chapter, we will install the
Ruby on Rails1 framework and some additional tools Ruby software engineers use for Ruby on Rails
application development:

1. Bundler
2. Ruby on Rails
3. PostgreSQL

Installing Bundler

Bundler2 is a package manager that manages the dependencies of a Ruby application. Our project
will use Bundler to manage the dependencies of our Ruby on Rails application specifically. Bundler
is a Ruby gem, so we will use the Ruby gem3 command to install it:

gem install bundler

This should produce output similar to the following:

Successfully installed bundler-2.4.10


Parsing documentation for bundler-2.4.10
Done installing documentation for bundler after 0 seconds
1 gem installed

By the time you read this, the latest version of Bundler may be different, so feel free to use the latest
version of Bundler available.
1 https://rubyonrails.org/
2 https://bundler.io/
3 https://guides.rubygems.org/rubygems-basics/

© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2024 17
G. Donald, Hands-on Test-Driven Development,
https://doi.org/10.1007/978-1-4842-9748-3_4
18 4 Getting Started with Ruby on Rails

Installing Ruby on Rails

Ruby on Rails is a web application framework written in the Ruby programming language. It’s
designed to make programming web applications faster and easier by making opinionated decisions
about what every web developer needs in their web development software stack. Ruby on Rails
enables us to write less code while accomplishing more than any other language and framework,
given similar effort.
As a framework, Ruby on Rails consists of several individual Ruby gems, but we only need to
install a single gem to pull everything in at once. Let’s use the Ruby gem command to install the
“rails” gem:

gem install rails

This command will generate a lot of output and should include something similar to the following:

Successfully installed rails-7.0.4.3


Parsing documentation for rails-7.0.4.3
Done installing documentation for rails after 0 seconds
1 gem installed

By the time you read this, the latest version of Rails may be different, so feel free to use the latest
available version.

Installing PostgreSQL

PostgreSQL4 is a very popular relational database management system (RDBMS) used by a lot of
Ruby on Rails applications and other web applications in general. We will use PostgreSQL to store
the data for our Ruby on Rails application.
As you may have guessed, PostgreSQL is available to install from Homebrew, so we will use the
brew command to install it:

brew install postgresql@14

Again, by the time you read this, the latest version of PostgreSQL may be different, so feel free
to use the latest version of PostgreSQL available. The PostgreSQL developers put a lot of effort
into maintaining backwards compatibility, so there’s usually no need to worry about using the latest
version of PostgreSQL.

4 www.postgresql.org/
Installing Node.js 19

After installing PostgreSQL, we need to start the PostgreSQL server:

brew services start postgresql@14

As a sanity check, we should use the PostgreSQL psql command to connect to the PostgreSQL
server and run a simple SQL query:

psql -d postgres -c "SELECT version()"

This should yield a result containing the version of PostgreSQL we just installed. If it does not work,
and you get an error message about psql not being found, you may need to add the directory where
the psql program was installed to your PATH environment variable. You can do this by adding the
following line to your .∼/.zshrc (or .∼/.bashrc) file:

export PATH="/opt/homebrew/bin:$PATH"

This will prepend the directory path /opt/homebrew/bin to your existing $PATH and then
re-export the new value to your shell environment. After making the change, run the following
command to source and re-apply the contents of your .∼/.zshrc file:

. ~/.zshrc

If /opt/homebrew/bin is not the location where psql was installed, you will need to change
the path to the location where psql was actually installed. It may, for example, be installed
in /usr/local/bin or some other location like /opt/homebrew/Cellar/postgresql/
14.x/bin.

Installing Node.js

Node.js5 is a JavaScript runtime environment that we need for our Ruby on Rails application
development. We won’t be using Node directly, but Rails commands, like the rails new command,
will use Node.js to compile JavaScript assets for our application as we develop.
Node.js is available to install from Homebrew, so we will use the brew command to install it:

brew install node

5 https://nodejs.org/
20 4 Getting Started with Ruby on Rails

After the install completes, we can see that Node.js is installed by running the following commands:

which node
node --version
npm --version
npx --version

If these commands do not complete with successful output, you may need to add the directory where
the node, npm, and npx programs were installed to your PATH environment variable, as we did with
the psql program earlier.
Now that we have Node.js installed, we can use it to install some Javascript packages Rails will
need for our Ruby on Rails application development and very importantly when we run the rails
new command shortly. We will use the npm command to install the esbuild, nodemon, and sass
packages:

npm install -g esbuild nodemon sass

Now we are ready to create our new Ruby on Rails application.

Creating a New Ruby on Rails Application

In this book, we will create a Ruby on Rails application in the form of a blog. We will use the Ruby
on Rails rails new6 command to create our new Ruby on Rails application. Before proceeding,
let’s discuss our rails new command-line options we will use to configure our new Ruby on Rails
application creation.
We are going to use RSpec for testing, so for now we will use the -skip-test option to tell Ruby
on Rails to skip creating its own test directory and related files. There’s currently no command-line
option to add RSpec (which is preposterous), so we will add RSpec manually later.
We will use the --database=postgresql option to tell Ruby on Rails to configure our app
for use with PostgreSQL as the database for our application. This option will cause a Gemfile entry
to be added for the pg gem and will preconfigure our config/database.yml file for use with
PostgreSQL.
JavaScript support has evolved a lot in recent years, and Ruby on Rails has a new way of handling
JavaScript dependencies called importmap.7 We will use that along with esbuild8 for our
JavaScript bundler.
Finally, we will use Bootstrap9 for our CSS framework. Bootstrap is a very popular CSS framework
that provides lots of convenient CSS classes to make building web applications easier for both web
and mobile apps.

6 https://guides.rubyonrails.org/command_line.html#rails-new
7 https://github.com/rails/importmap-rails
8 https://esbuild.github.io/
9 https://getbootstrap.com
Creating a New Ruby on Rails Application 21

Before you blast me with an email about how Bootstrap is the old way and how Tailwind is the
new hotness, I know. I’m going to use Bootstrap for this book because it’s what I know best and it’s
what I like.
Another consideration is in what directory do we run the rails new command? The simple
answer is that it doesn’t matter much. The rails new command we’re going to use makes a
directory and puts everything inside it. You can then move this directory around to where you want it
to live, now or in the future.
I personally like to store my Rails-specific code in my .∼/workspace/rails directory, so I
will run the rails new command after I change into that directory, like this:

cd ~/workspace/rails

Be sure to use whatever directory you prefer for your Rails code.

rails new \
--skip-test \
--database=postgresql \
--javascript=esbuild \
--css=bootstrap \
blog

Running the rails new command will take some time to complete. When it’s done, you will have
a new directory called blog containing the files for our new Ruby on Rails blog application.
Change directory into the blog directory:

cd blog

We can now start our local Rails server using Bundler’s bundle exec command. This command
will run Rails in the context of our application’s Gemfile, which will ensure that we are using the
correct version of Ruby and Rails and all of the other gems we need for our application.

bundle exec rails server

We should see output similar to the following:

=> Booting Puma


=> Rails 7.0.4.3 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.5 (ruby 3.2.2-p53) ("Birdie's Version")
22 4 Getting Started with Ruby on Rails

Figure 4-1 The Ruby on Rails error page

* Min threads: 5
* Max threads: 5
* Environment: development
* PID: 11869
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop

If we visit the Rails welcome page at http://127.0.0.1:3000 in our web browser, we should see
an error page like the one in Figure 4-1. This is because we haven’t created the database named
blog_development that is defined in our config/database.yml file yet. We can fix the
error by creating the database using the rails db:create command:

bundle exec rails db:create

Running this command will produce the following output:

Created database 'blog_development'


Created database 'blog_test'

As seen in the output, we actually get two databases, a development database and a test database. The
development database is used for development, and the test database is used strictly for running tests
that we will begin writing shortly.
Now if we visit the Rails welcome page at http://127.0.0.1:3000 in our web browser, we should
see the Rails welcome page as referenced in Figure 4-2.
Creating a New Ruby on Rails Application 23

Figure 4-2 The Ruby on Rails welcome page

Running Rails Locally

As mentioned previously, we can run our Ruby on Rails application locally using the rails
server command along with the bundle exec prefix. That’s fine for developing Ruby code,
but our Rails application will also contain CSS and JavaScript code. There are many different ways
to handle CSS and JavaScript in a modern Ruby on Rails application, in both development and
production environments.
To ease the burden of managing Ruby, CSS, and JavaScript in our local development environment,
the rails new command we ran earlier automatically created a Procfile.dev file in our
application’s root directory. This file contains process definitions we need to run our Rails application
in our local development.
If we have a look at the Procfile.dev file, we will see the following contents:

web: unset PORT && env RUBY_DEBUG_OPEN=true bin/rails server


js: yarn build --watch
css: yarn watch:css

As is often the case with other files generated by the rails new command, this file tends to evolve
over time and may appear slightly different in the future. For now, we can see there are three lines,
and each line represents a process that will be run when we do something with this file, but how do
we run it?
24 4 Getting Started with Ruby on Rails

Figure 4-3 Running Rails using foreman

We need to use the foreman10 command to run the processes defined in our Procfile.dev
file. The foreman command can be acquired by installing the foreman gem using the command:

gem install foreman

Before we can actually run foreman we need to install yarn, a tool that will be used to automate
compiling our Javascript and CSS files for us. There are many ways to install yarn. I install it from
Homebrew using the command:

brew install yarn

Once we have foreman and yarn installed, we can run foreman using the command:

foreman start -f Procfile.dev

This will start the web, js, and css processes defined in our Procfile.dev file. The output
will look similar to Figure 4-3. The web process is the Rails server we started earlier. The js process
is a process that will watch for changes to our JavaScript files and rebuild them as needed. The css
process is a process that will watch for changes to our CSS files and rebuild them as needed. This will
save us a lot of time when developing our Ruby on Rails application.

10 https://github.com/ddollar/foreman
Summary 25

We’re almost ready to begin writing our Ruby on Rails blog application in earnest. If you are
planning on following along with the rest of the book, you would be wise to commit your code to a
version control system at this point.

Summary

In this chapter, we installed the Ruby on Rails framework, along with Bundler and PostgreSQL. We
created a new Ruby on Rails application using the rails new command. We also learned how to
run our Ruby on Rails app in our local development environment using the foreman command.
Next, we will add RSpec and FactoryBot to our Ruby on Rails application to facilitate writing our
first tests.
Setting Up RSpec and FactoryBot
5

RSpec1 is a testing framework for Ruby. RSpec is available to us as a domain-specific language2


(DSL) written in Ruby and used to test Ruby code. RSpec tests the behavior of Ruby code and
considers itself to be a “behavior-driven development” (BDD) framework. RSpec is most widely
used for testing Rails applications, but can be used to test any Ruby code.
Ruby on Rails by default comes with the testing framework Test::Unit. In the previous
chapter when we used the rails new command to create our Rails blog application, we added
the --skip-test option to disable Test::Unit. Like most Rails developers, we will use RSpec
instead of Test::Unit.

Installing RSpec

RSpec is a gem, so we can add it to our Rails blog application by adding it to our Gemfile and
running the bundle command. We want RSpec to be available in both the Rails development
and test environments, so we will add it to both the :development and :test groups in our
Gemfile. The Gemfile addition should then look like this:

group :development, :test do


gem 'rspec-rails'
end

Let’s save our Gemfile changes and run the bundle command:

bundle

Running the bundle command or more verbosely the bundle install command will install
the rspec-rails gem and all of its dependencies. The bundle command will also update the
Gemfile.lock which is used to lock our gems and their dependent gems to specific versions. This
1 https://rspec.info
2 www.jetbrains.com/mps/concepts/domain-specific-languages/

© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2024 27
G. Donald, Hands-on Test-Driven Development,
https://doi.org/10.1007/978-1-4842-9748-3_5
28 5 Setting Up RSpec and FactoryBot

is important because it ensures we are using the gem versions we mean to in all of our potential Rails
environments.
RSpec has now been added to our blog application, but we need one more step to get it working.
We need to run the rails generate rspec:install command:

bundle exec rails generate rspec:install

We should see the following output:

create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb

We can now verify that RSpec is installed and configured by running the rspec command:

bundle exec rspec

We should see the following output:

No examples found.

Finished in 0.0002 seconds (files took 0.0351 seconds to


load)
0 examples, 0 failures

This output tells us that RSpec is installed and properly configured within our Rails application. We
have no tests yet, so RSpec lets us know that it found “no examples” to run. In RSpec lingo, an
example is actually a test, or a spec. We will soon be adding our first RSpec example, so let’s move
on for now.

Installing FactoryBot

FactoryBot3 is a Ruby gem that provides a framework and domain-specific language (DSL) for
defining and using factories. A FactoryBot factory is a blueprint for creating instances of Ruby
classes, or in our use case, Rails ActiveRecord objects. FactoryBot is most widely used for testing
Rails applications, but can also be used to test any sort of Ruby code.
But do we really need FactoryBot? Can’t we just create our test data using Rails and ActiveRecord
objects directly?

3 https://github.com/thoughtbot/factory_bot
Installing FactoryBot 29

We could indeed create all of our test data for our blog application using only Rails ActiveRecord
objects. For example, we could create a new user object using User.new, and then we could create
a new page object using Page.new(user:) that belongs to that user. But this is tedious and error
prone, especially if we need to create similar test data many times over or if we need test data with
particular traits.
FactoryBot provides more concise and flexible ways to create test data. We can define reusable
traits that we can then mix and match to reliably create exactly the test data that we need. We can
also choose to only build (not create) test data, without actually saving it to our database. This is very
useful when we want to test field validation errors, for example.
FactoryBot is of course packaged as a Ruby gem, and there is a Rails-specific version called
factory_bot_rails that we can use. Let’s add it to our application by adding the following line
to our Gemfile and running the bundle command:

gem 'factory_bot_rails'

We want FactoryBot to be available in all Rails environments, so we will add it outside of any group
blocks in our Gemfile.
Anytime we add a gem to our Gemfile, we need to then run the bundle command to install the
new gem and its dependencies. We’ve done it a few times already, so to save space we won’t show
the output anymore.
We need to integrate FactoryBot into our Rails application. So let’s add it to our spec/spec_
helper.rb file. Near the top, add the following line:

require 'factory_bot_rails'

Then, just inside our RSpec.configure block, let’s add the following:

config.include FactoryBot::Syntax::Methods

These additions to our RSpec configuration will allow us to use all of the FactoryBot methods in
our specs. For example, instead of using the fully qualified “FactoryBot” namespace to call the
FactoryBot.create method, we can instead just use the create method directly. This will
make our specs cleaner and save us some keystrokes as well.
Another FactoryBot integration we want to take advantage of is the ability for Rails code generators
to create factories for our models. To enable this, we need to add the following near the top of our
config/application.rb file:

require 'factory_bot_rails'
30 5 Setting Up RSpec and FactoryBot

Further down, inside the same file, let’s add this bit of extra configuration inside our Application
class:

config.generators do |g|
g.test_framework :rspec, fixture: true
g.fixture_replacement :factory_bot, dir: 'spec/factories'
end

This will configure the Rails model generator to use FactoryBot to create factory file stubs whenever
we generate a new model. We Ruby developers (especially the author) love saving keystrokes
whenever possible. Maybe not as much as Perl developers, but still.
To further facilitate our spec writing, let’s install another gem called shoulda-matchers.4
This gem provides a collection of RSpec-compatible one-liners and helpers that will aid us in testing
common Rails functionality.
Let’s modify our Gemfile to add the following line inside the :test group:

group :test do
gem 'shoulda-matchers'
end

Then let’s run the bundle command to install the new gem.
To integrate the shoulda-matchers gem into RSpec and Rails, we need to add the following
configuration into our spec/rails_helper.rb file; anywhere near the bottom is fine:

Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end

Debugging

While writing our RSpec tests and Rails application code, we will inevitably encounter errors and
other confusing situations where we may not understand exactly what is happening. When we do, we
will need to be able to debug our code. We will need to be able to dig deeper into the code’s execution
and see what is happening at each step. We will need to be able to inspect the variable values and
follow different code paths.

4 https://github.com/thoughtbot/shoulda-matchers
Installing FactoryBot 31

The easiest way to debug Ruby code is to use binding.irb. This is built into Ruby and allows
us to drop into an irb session at any point in our code execution. An example call to binding.irb
may look something like this:

# debug.rb

def add(a, b)
binding.irb
a + b
end

puts add(1, 2)

In our file debug.rb, we define a method called add that takes two variables and adds them
together, returning the result. We then call our method and use puts to print the result. We can
run this code by executing it with the ruby command:

ruby debug.rb

When our code executes, it will stop at the binding.irb line and drop us into an irb session. It
will look something like this:

From: debug.rb @ line 3 :

1:
2: def add(a, b)
=> 3: binding.irb
4: a + b
5: end
6:
7: puts add(1, 2)
8:

irb(main):001:0>

We can then inspect our variables and execute any Ruby code we want. When we are done, we can
exit the irb session by pressing ctrl-d or by typing the more verbose exit command:

irb(main):001:0> a
=> 1
irb(main):002:0> b
=> 2
irb(main):003:0> a + b
=> 3
irb(main):004:0>
3
Random documents with unrelated
content Scribd suggests to you:
Captain Patch escorted Mrs. Harter to the house in Queen Street....
Nancy Fazackerly told me the fact when she gave me the account of
the party. And I have wondered so often what took place in the
course of that short walk—the first time that those two people were
ever alone together, after the evening of music and talk and laughter
—that I have come to evolve a sort of imaginary conversation. It is
based, like almost all my conception of Mrs. Harter’s personality, on
conjecture, on the judgments of Mary Ambrey, who alone, of all
those who watched the events of that summer, combined clear
vision with pitifulness—and on what I saw of Bill Patch.
I don’t know—no one ever will know—what passed between them as
they went up the still, moonlit street and across the little open
square of the market place, their footsteps sounding very clearly in
the absolute quiet of the night-time.
But I have sometimes fancied that I could reconstruct the lines of
that conversation—and, for what it is worth, I shall put down what
they may have said, as though I knew that they really did say it.
Certainly, Diamond Harter dropped her guard that night. I am sure
of that. Perhaps it was something as follows—but perhaps not.
“Were you long in the East?”
“Nearly five years. This is the first time I’ve been home. Cross
Loman hasn’t changed much.”
“You must be glad of that, I should think.”
“Why?” said Mrs. Harter sharply. “I think it’s a horrid little country
town, and the people in it mostly snobs.”
“Why do you think that? I’ve found them all so kind and friendly.”
“You! Yes. That’s different. But you don’t suppose I should have
been asked to-night if I hadn’t happened to sing at the concert the
other day. That Mrs. Fazackerly is a kind little soul, and everyone
knows she’s had a hell of a time. But I’ve not any use for the
Ambreys—especially that girl.”
“I’m sorry you feel like that.”
“I know you all thought I was going to spoil the evening, at first. I
couldn’t help it.” Her voice softened a little in the darkness. “I felt
such a fish out of water.”
“Sometimes I’ve felt like that myself. I used to when I was in the
Army, very often. But one gets over it. People are awfully kind,
really.”
“Martyn Ambrey is all right, and Mrs. Ambrey, that girl’s mother. Do
you know her?”
“I’ve met her.”
“They weren’t living here when I was at home. As for the high and
mighty Lady Flower, you saw what she was like that night at the
concert.”
“Was she especially—anything?”
Mrs. Harter gave a short laugh.
“I don’t know why I’m giving myself away like this, I’m sure. Only
you somehow got things going to-night, just when I was cursing
myself for having been such a fool as to come.”
“I’m glad you did come, and I hope you’ll come to the Ambreys.”
He spoke simply and deliberately, and her reply was equally devoid
of any hint of conventional intention.
“Why?”
“Because you sound so lonely,” said Captain Patch. “I expect you’d
like people better if you saw them more often.”
“One doesn’t generally,” she said with an odd laugh.
“Give it a try, anyhow.”
“I shall. I always found this a dead-alive place, and after the East it
would be duller than ever if one didn’t know people.”
“But you must have plenty of friends if your home is here.”
“I was away at school before I got married, and anyhow, I never
was much of a one to make friends. The people I wanted to know
didn’t care particularly about me, and the ones that did want to
make friends I wasn’t particularly keen on. You see, my people sent
me to a school where there were a set of girls that thought
themselves a great deal better than the tradesmen’s daughters, and
that sort. I was with them, mostly, at school, but after I left it was
different. I was supposed to be going to teach, and one girl wrote
and asked if I’d like to come as governess to her little sister. When
we were at school, she’d invited me to go and stay as a friend, and
I’d spent the holidays there. So I knew what it was like. And I wasn’t
going to go back there as the governess after being a visitor in the
house, thank you.”
“What did you do then?”
“Nothing. Stayed at home and did the typing in the office. I hated
Cross Loman.”
“Did you like Egypt?”
“Yes,” said Diamond Harter slowly. “I liked Egypt. I got all the
dancing and the riding and the parties out there that I’d wanted and
hadn’t been able to get down here. Have you ever been to the
East?”
“No.”
“It’s all quite different, of course. Everyone knows everyone, in a
way. There aren’t ‘county’ people and other cliques, like there are
here. One got the chance of knowing people whom one wouldn’t
even have met at home.”
“Then,” said Captain Patch, rather doubtfully, “you’ll be glad to go
back there again, I suppose?”
“For some things. This is my door, Captain Patch. Thanks for
bringing me back. I suppose you wouldn’t care to come in and see
me one day?”
“I’d like to very much,” said the red-haired young man, with his
friendly smile. “Can’t I come and call on you?”
“I’m always here. Come and have tea with me on Thursday.”
“Yes, I will. Thank you very much.”
Her face in the moonlight looked strangely softened. “Have you got
a latchkey?”
“Yes. Good-night.”
Mrs. Harter held out her hand and he took it for an instant. It was a
strong hand, unusually broad, and capable of transmitting in contact
a faint, magnetic thrill.
“Good-night,” she repeated as she went up the three shallow steps
that led up to the neat, mean little door, with its liver-colored paint
and tarnished brass.
Captain Patch, on the pavement, watched the door open, saw the
tall, square-shouldered figure for a moment against the light that
hung in the narrow entrance, and then heard the slam of the door
and saw, through the ground-glass fan-light, the light go out.
Then he turned down the road again, softly whistling to himself “The
Bluebells of Scotland.”

Sallie Ambrey has not her mother’s intuition, nor, naturally, has she
Mary’s experience. But she has great acumen, and—that rarest and
most invaluable asset—a mind trained from babyhood to clear
thinking.
And, personally, I hold that she was absolutely right when she once
called Captain Patch a hopeless and temperamental romantic,
capable of a grande passion. One doesn’t associate it, somehow,
with red curly hair, and a slouch, and a very frank smile on a boyish
mouth and behind a pair of strong glasses.
Incongruity, in a way, was the keynote of the whole thing.
Diamond Harter wasn’t in the least beautiful, and certainly not
charming. She was his senior by a year or two and, as Mrs. Kendal
said later on, with her extraordinary gift for emphasizing the
unessential:
“Mrs. Harter was not, in any sense of the word, a lady.”
One is left wondering how many “senses of the word” exist, and
what they all are.
A few days after the concert, we decided that we would give a
dance.
The Ambreys had come up to tea, as they often do on Sundays, and
Mrs. Fazackerly came, and Bill Patch. I remember that Nancy
Fazackerly looked pretty that day, in a hat trimmed with blue daisies
and a blue cotton frock that seemed to be striped with a darker blue.
(Amy Kendal, who walked up later, with Mumma, of course, said to
her, “How smart you look!” in a reproving way. And Christopher
Ambrey, to whom the Kendal manner is not the familiar thing that it
is to us, asked me what that odious woman meant.)
“This is the very place for a dance,” said Sallie, looking round the
hall. “I can’t imagine why no one has thought of it before.”
Sallie is always rather apt to assume that because she has not
thought of a thing herself, nobody else has done so, and this is a
trick, among many others, that exasperates Claire.
“There were dances here before you were born or thought of, my
child. It may seem very strange to you,” said Claire ironically, “but I
happen to have been rather an unusually good dancer.”
Her annoyance was so obvious in her voice and manner—Claire
never attempts to dissemble her feelings—that Nancy Fazackerly
characteristically came to the rescue.
“I love to see you dance, Lady Flower,” she said earnestly. “I believe
you’d even make the new jazz dances look graceful.”
She said it so naturally and sincerely that I felt I was an ungrateful
brute for reflecting that she had probably never in her life seen
Claire dance a step.
Sometimes I think that a long course of being told that she is the
worst housekeeper, or the most inadequate manager, in the world,
varied only by the nerve-shattering experience of plates hurled at
her head, has altogether destroyed Nancy’s capacity for
distinguishing fact from fiction. I am sure that she does not
consciously fib. It is simply that her sense of expediency has
completely got the better of her. Truthful, she undoubtedly is not,
but I have always believed in her sincerity. And we were all secretly
grateful to her for restoring Claire’s good humor.
“I may not have a staff of A. D. C’s., but I have had quite as much
experience in entertaining as Lady Annabel Bending, I imagine,” said
Claire, with some elasticity of statement. “And I should like to do
something of the kind.”
“The difficulty will be to get men,” Mrs. Kendal stated, with all the
Kendal directness. “You know how few men there are anywhere near
Cross Loman. The girls often say that it’s next door to impossible to
get a man for anything round here. Of course, Ahlfred would come
down for it, and perhaps he could bring a friend—that would be two
men.”
We tried to look encouraged.
“Let’s make a list of the people you want to invite, Cousin Claire.”
Claire dictated names, and Sallie wrote them down, and we all made
suggestions. The monosyllable “men” must have resounded through
the hall fifty times, in Mumma’s emphatic contralto.
The list approximated to about forty couples when it was done. I
said that I thought we ought to do the thing properly and invite the
whole neighborhood, not merely dancers. “Can’t we have bridge or
something to amuse the older people?” said I, not without a thought
to my own entertainment.
“I know!” cried Martyn. “Let’s have theatricals—ask everybody to
come and see them, and then have a dance afterwards for those
who like it.”
Christopher, Mrs. Fazackerly, Sallie, and Captain Patch received the
suggestion with such clamorous enthusiasm that Claire and I
exchanged a glance and a word under cover of it.
“Would you care to, Claire? I’m quite ready, if you are, and it would
amuse Christopher.”
“Yes, it would. We haven’t done anything for a long time, either, and
Cross Loman really has had enough of the Drill Hall entertainments,
I should imagine.”
I knew that she was thinking of Lady Annabel again.
“You can have theatricals, Martyn,” said Claire graciously. “I think it’s
rather a good idea, and we’ll have dancing in the saloon afterwards.”
The list was revised, added to and discussed all over again.
“But who will act in the theatricals?” Mary said. “And what are you
going to act?”
“Captain Patch will write something—Oh yes, you must, or what’s
the good of having an author here at all?—and we’d better tell him
just how many people there are who can act, and then he can have
the right number of parts,” said Sallie rapidly. “And anyone who can’t
act, and wants to, can be told that there isn’t a part.”
“None of us can act to save our lives,” Amy Kendal superfluously
informed us.
“I cannot write a play,” said Bill Patch very firmly indeed. “But we
could get up something musical, if you liked, and write our own
libretto, and just set it to any tune that fits. I’ve seen that done very
successfully at short notice, and it’s all there’ll be time for, if Lady
Flower’s dance is to be three weeks from to-day.”
“Fancy your saying that you couldn’t write a play! I’m sure you could
write a play, Captain Patch,” said Mrs. Kendal amiably. “If a book,
why not a play?”
Bill Patch looked rather desperate, and said he didn’t know why not,
but he couldn’t, and Mumma remarked again, three or four times,
that she was quite sure he could easily write a play.
“Miles, why don’t you stage-manage it for them?” said Mary Ambrey.
“They’ll want someone....”
In the end, they settled it that way, after talking until nearly eight
o’clock.
The last thing I heard, as everyone took leave of us at the same
moment, was Mumma reiterating, pleasantly but steadily, her
conviction (a) that it would be difficult to get enough men, and (b)
that she was quite sure Captain Patch could easily write a play.
Chapter Five
Two days later, Bill Patch and Mrs. Fazackerly came to consult us
about their joint production.
“It isn’t a play,” Captain Patch said, his red hair standing up on end.
“Whatever Mrs. Kendal may think about it, I cannot write a play. But
we’ve strung something together, more or less—mostly a few songs.”
“We thought you’d know more about it than anybody else and would
advise us,” said Nancy Fazackerly prettily.
“Even Mrs. Kendal has never suggested that I could write a play, my
dear.”
“But I’ve sometimes wondered whether I oughtn’t to have gone in
for writing,” said Claire. “Only I haven’t had the time.”
“It’s more about the performers than the actual play that we want
advice,” explained Captain Patch. “Though even that isn’t going to
be all plain sailing. General Kendal—”
“Most kindly,” said Nancy Fazackerly.
“Most kindly,” Bill repeated, in a worried, obedient sort of way, “most
kindly turned up last night with a pair of Hessian boots.”
“Hessian boots?”
“He thought they’d make such a good stage property and that we
ought to write something that would make use of them. He really
was most awfully keen, poor old fellow, and of course it isn’t a bad
idea, in its way. Hessian boots, you know—you don’t see them
nowadays.”
To this we assented.
“One could do something with a uniform, and the boots would give a
finish, as it were,” Mrs. Fazackerly suggested.
“Hessian boots, and a belt, and a busby, would give the idea of a
Russian, I thought,” Bill Patch explained. “And we thought of doing
something with that old song, ‘The Bulbul Ameer.’ You could make
quite a lot out of it, and it would be much easier to dress up to that
sort of thing than to a regular play. You remember the song I
mean?”
“I brought it with me,” said Mrs. Fazackerly. And then and there she
read it aloud to us, in her pleasant, rather pathetic voice.

“The sons of the Prophet are hardy and bold,


And quite unaccustomed to fear;—
But, of all, the most reckless of life or of limb,
Was Abdul, the Bulbul Ameer.
When they wanted a man to encourage the van,
Or to shout ‘hull-a-loo’ in the rear—
Or to storm a redoubt, they straightaway sent out
For Abdul, the Bulbul Ameer.

“There are heroes in plenty and well-known to fame


In the ranks that are led by the Czar;
But among the most reckless of name or of fame
Was Ivan Petruski Skivah.
He could imitate Irving, play euchre or pool,
And perform on the Spanish guitar;
In fact, quite the cream of the Muscovite team
Was Ivan Petruski Skivah.

“One morning the Russian had shouldered his gun


And put on his most cynical sneer,
When, going down town, he happened to run
Into Abdul, the Bulbul Ameer.
Said the Bulbul, ‘Young man, is your life then so dull
That you’re anxious to end your career?
For, infidel, know that you’ve trod on the toe
Of Abdul, the Bulbul Ameer.’

“Said the Russian, ‘My friend, your remarks in the end


Will only prove futile, I fear;
For I mean to imply that you’re going to die,
Mr. Abdul, the Bulbul Ameer.’
The Bulbul then drew out his trusty chibouque,
And, shouting out, ‘Allah Akbar,’
Being also intent upon slaughter, he went
For Ivan Petruski Skivah.

“When, just as the knife was ending his life—


In fact, he had shouted ‘Huzza!’—
He found himself struck by that subtle calmuck,
Bold Ivan Petruski Skivah.
There’s a grave where the wave of the Blue Danube flows,
And on it, engraven so clear,
Is, ‘Stranger, remember to pray for the soul
Of Abdul, the Bulbul Ameer.’

“Where the Muscovite maiden her vigil doth keep


By the light of the true lover’s star,
The name she so tenderly murmurs in sleep
Is ‘Ivan Petruski Skivah.’
The sons of the Prophet are hardy and bold
And quite unaccustomed to fear;
But, of all, the most reckless of life or of limb,
Was Abdul, the Bulbul Ameer.”

“It’s not a bad tune,” said Captain Patch. “You see, someone comes
on and sings the whole thing straight off—just to put the audience in
touch with the general hang of affairs—and then, I thought, we’d act
it. This fellow Abdul, you know, full of swagger—dressed up like a
Turk—nothing easier than to dress like a Turk, on the stage—a towel
twisted round your head, and shoes turning up at the toes, and a
bill-hook or something for a scimitar, and everyone tumbles to it
directly. Well, Abdul could get quite a lot of laughs by putting on
tremendous side and all that sort of thing. Then the Russian chap—
or we could just call him Slavonic, if you think Russians are rather a
slump in the market just now—of course he’s in love with Abdul’s
girl, the Muscovite maiden. He’d have to be the hero of the piece—
Ivan Petruski Skivah—flourishing about with a sword and that kind of
thing—and in uniform—”
“The Hessian boots?”
“Exactly. The Hessian boots. A note of realism introduced at once—”
“And what about the Muscovite maiden?” said Claire.
“She’ll sing duets with Ivan Petruski, of course, and she’s easy to
dress, too. A veil over her head, and slave-bangles, and perhaps a
Yashmak. An eastern get-up is always effective, and so very
economical to arrange,” said Mrs. Fazackerly with satisfaction.
“We’re going to put in extra parts as well—chorus of Eastern
maidens, and Cossacks, and things like that. But those are the
principals.”
“And how have you cast it?” I inquired.
“Sallie must be the Muscovite maiden. She’ll look sweet,” said Mrs.
Fazackerly, “and she can sing, too.”
“Will Major Ambrey take on the Bulbul Ameer?” Captain Patch asked.
Christopher was not present. We were both positive that he would
refuse the suggested honor, and we knew well, moreover, that
Christopher is no musician. I have heard him sing in church.
“You’ll have to do it yourself, Captain Patch,” said Claire. “How about
the Hessian boots?”
“We thought of Martyn. And someone will be wanted to sing the
song itself, as a kind of prologue, before the curtain goes up,” said
Mrs. Fazackerly.
I remember that she looked as much pleased and excited over their
plans as a child over a party.
“You see, that song is meant to be a sort of recurring motif
throughout the whole show,” Bill said. “When we’re at rather a loose
end, someone can play the refrain or sing it, and it will buck things
up at once. It’s extraordinary how pleased an audience always is
with anything that’s repeated often enough. They know where they
are, I suppose, when they recognize an old friend. And at the end,
we can all stand in a row across the stage and sing the chorus
together. You know the kind of thing—just to bring down the
curtain.”
He looked just as much pleased and excited as Nancy Fazackerly did.
They were like two very nice children.
“It sounds all right,” I said. “I take it that we really want to do the
acting among ourselves, as much as possible, and entertain the rest
of the people and then all wind up with a dance.”
“Exactly,” said Claire.
“The only outside talent, as far as one can see at present, will be
Mrs. Harter,” said Bill Patch—and he was genuinely quite
unconcerned about it, too.
But I saw that Nancy Fazackerly knew well enough that Claire wasn’t
going to stand for that.
“Mrs. Harter?”
There was more than one note of interrogation in Claire’s way of
saying it—quite three or four.
“You remember how rippingly she sang ‘The Bluebells of Scotland’
the other night?”
“Oh, yes, I remember that.”
“We thought of her, for the ‘Bulbul Ameer’ song at the beginning
because one really does want someone who’ll pronounce all the
words distinctly. And she’s got a good ‘carrying’ voice, if ever I heard
one.”
“I daresay,” said Claire distantly.
Bill Patch looked from one to another of us, and I remembered how,
the first time I saw him, he had reminded me of a Clumber spaniel—
so young, and awkward, and eager—and now, evidently, so much
puzzled as well.
“Her voice really is a very good one,” said Mrs. Fazackerly pleadingly.
“And I’m rather sorry for her, do you know. After all, in Egypt she
must have had a very amusing time and known heaps of people—
and now to come back to Cross Loman—”
“Where she came from!” ejaculated Claire.
“I know—but that makes it harder, in a way. She’s outgrown the
people whom she saw most of when she was Diamond Ellison—and
after all, she wasn’t so very much more than a schoolgirl when she
married and went away. I think she feels a little bit stranded
sometimes.”
“Where is Mr. Harter—and what is he?” Claire demanded.
“He is a solicitor—and he’s still in the East, but he may come home
this summer. I don’t think the marriage is a very happy one,” said
Mrs. Fazackerly, looking down.
I fancy that to all of us there then came a momentary vision of
crockery, propelled violently through space, after the reckless habit
that report had imputed to Mrs. Fazackerly’s excitable partner.
“It would be so very kind of you, Lady Flower, to say that we may
ask her to help with the show,” said Nancy, raising her pretty eyes to
Claire’s face, and speaking with her habitual flattering deference.
“You see, if once you gave a lead, Mrs. Harter wouldn’t feel out of
things any more.”
“And,” said Captain Patch, not quite so diplomatically, “it would be
such a shame to waste that beautiful voice.”
“Who is going to play your accompaniments—or do you rise to an
orchestra?” I interrupted.
“I can play the accompaniments,” said Mrs. Fazackerly radiantly. “It’s
all I’m good for. I have no voice and I can’t act. Which reminds me
that some of the Kendals really ought to be asked to take part,
oughtn’t they, after General Kendal has so very kindly provided those
boots?”
“Perhaps Alfred and two of the girls might do something in the
chorus without damaging it.”
“We must go and find out. And—what about Mrs. Harter?”
Claire shrugged her shoulders.
“I think it’s rather a mistake to ask her, myself. But please do exactly
as you like about it. If her voice is essential, then I suppose she
must be asked.”
“Now, what about the stage itself?”
Nancy Fazackerly was quite wise enough not to press the question of
Mrs. Harter any further, and they went off into a discussion as to the
structure and position of the stage.
I asked Claire afterwards if she really objected very much to letting
old Ellison’s daughter take part in the performance.
“She won’t expect to be asked here afterwards, if that’s what you’re
afraid of.”
“How do you know she won’t? I thought that she looked like a
pushing sort of woman, and common.”
“Do you remember how they did those portraits of her in Sallie’s
game the other day?”
“Yes. Why?”
“It struck me as odd that they’d all thought enough about her to find
it worth while—although not one of them knows her in the least
intimately.”
“As I said at the time, Miles, she has personality. I suppose I have
personality myself. It’s an indefinable sort of thing.”
We left it at that.
Mrs. Fazackerly and Captain Patch were to have a week in which to
prepare their program, and after that there was to be a general
assembly of the prospective performers.
“And you’ll preside, won’t you, to settle about parts, and then no one
will be hurt or offended,” said Mrs. Fazackerly, speaking, I fear, from
a wide past experience of the wonderful capacities of other people
for being hurt or offended on the very slightest provocation.
I asked them to hold the meeting in the library and promised to do
my best that no one should be either hurt or offended.
On the day that I was expecting them I drove down to the Mill
House in the morning to see Mary. I drive out in a low basket-
carriage drawn by a very old pony, because that is the only safe way
in which I can convey my semi-helpless person about without
assistance.
She was in the garden, as usual, doing something with a trowel.
Mary never seems surprised to see me, only pleased, and she does
not stand by with an anxious frown, brightly and carefully talking
about other things while I adjust my crutches and lower myself out
of the pony carriage.
“Sallie and Martyn are rolling the tennis lawn. Isn’t it energetic of
them on such a lovely day? Let’s sit in the shade.”
There is a big beech tree on Mary’s lawn, and we sat under it and
watched the tiny little stream that runs at the bottom of the garden.
The sound of it, more than any other sound I know, always recalls to
me the summer days of childhood.
Presently I consulted Mary about the theatricals and the assignment
of the parts.
“Sallie for the heroine, of course—she can act and she can sing.
Nancy Fazackerly can’t act and can’t sing, but she’s going to play the
accompaniments for all the songs. They suggested Martyn for the
hero and Patch for the villain—dressed as a Turk. I don’t know what
other parts they’ll put in, but apparently the whole thing is perfectly
elastic and can be added to or taken away from as desired. It’s all to
be Eastern dress, more or less—as being easy to arrange. And
they’re very keen to have Mrs. Harter to sing the ‘Bulbul Ameer’
song. It’s the keynote of the whole thing, that song.”
“What does Claire say?”
“She says they may do as they like, but she doesn’t care for the idea
very much. For one thing, she thinks Mrs. Harter—Diamond Ellison—
will feel out of her element.”
“I wonder. After all, she’s been for years in Cairo, and must have met
all sorts of people. And I’m convinced that she’s intelligent, Miles,
and probably very adaptive. Martyn says that she’s an exceptional
person altogether.”
“Does he know her?”
“No. But both my children tell me that they are natural psychologists
of a high order.”
We laughed and then Mary said:
“I sometimes wonder if it’s a mistake to have let their critical
faculties—Sallie’s especially—develop quite unchecked. She finds
people more interesting than anything else, but it’s all so very
impersonal and analytical.”
“You might divide humanity into those who put people first, those
who put things first, and those who put ideas first.”
“Which do you put first, Miles?”
(Claire would have said, “Which do I put first?”)
“People, of course. So do you. But it’s the people who put things first
who are in the majority. In the ultimate issue, they weigh what Mr.
Wemmick called portable property—things like houses, and furniture,
and money—against the personal relations, and the portable
property counts most.”
“I know. They are called practical people because they would never
postpone a business appointment on account of a child’s birthday
party. The birthday party would have to be postponed. And what
about the ones who put ideas first?”
Of course, Mary knew as well as I did—or better—what about them.
But she also knew that I like long, wandering, impersonal
discussions of the kind that I can indulge in with no one else.
I smiled at her, just to show that I knew quite well how she was
humoring me.
“The people who put ideas first are, I think fortunately, in a very
small minority. Religious enthusiasts, of course—and perhaps the
few people who really are thorough-going, matter-of-fact
conventionalists.”
“You are thinking of the Kendals,” said Mary unerringly.
I admitted that she was right.
“Can you imagine Mumma, for instance, on a jury, admitting
‘extenuating circumstances’? ‘A crime is crime,’ she would probably
say, and as she would say it not less than fourteen times, she would
end in hypnotizing all the other eleven into agreeing with her. People
like that ought really never to be allowed to have any say in any
question affecting their fellow-creatures, but unfortunately there’s
generally a sort of spurious worth and solidity about them that
compels attention.”
“I remember,” said Mary, “that once at Dheera Dhoon we were
talking about a man who had become a Catholic, and someone said
that it would be very difficult and require a good deal of moral
courage to take a step of that sort. And Mrs. Kendal answered, ‘How
can there be any courage in deliberately going from the true to the
false? Nothing of the kind.’ And one felt that she would never, by any
possibility, see it in any other light.”
I made Mary promise that she would come and help me at the
meeting in the library that afternoon. Sallie and Martyn were to be
there, of course, and the authors of the production; and we felt that
it was probable that one or two of the Kendals might appear in order
to inform us that they couldn’t act.
“What about Mrs. Harter?”
“Oh, no. You see, she won’t be actually in the play, anyhow. They
only want her to sing before the curtain goes up and then again at
the end.”
“Do you know that they are all coming here this evening to sing?
Sallie invited them that time they went to Nancy Fazackerly’s. Mrs.
Harter, too.”
“I’m glad.”
So I was. What Nancy had told me of Diamond Harter made me feel
sorry for her, in spite of her aggressive airs. I wanted her to go to
Mary Ambrey’s house, in the atmosphere of sanity, and kindness,
and serenity, that belongs to Mary.
When I got home, I found Claire entertaining Lady Annabel Bending.
I felt sure that she had come to hear about the dance that we
proposed to give. The invitations had only just been sent out, but in
Cross Loman we are never long in ignorance of one another’s
arrangements.
Miss Emma Applebee, before now, has darted out of her shop and
inquired of me solicitously how her Ladyship’s cold is, when I myself
had only been made aware of its existence about an hour earlier.
Lady Annabel was inclined to be rather grave, although courteous,
about our entertainment. Did we realize quite what we were
undertaking, especially—if she might say so—with an invalid in the
house?
She glanced at me.
I have reason to believe that Lady Annabel speaks of me behind my
back as “our afflicted friend, Sir Miles Flower.”
“I have done so much—so very much—entertaining myself, and
necessarily on such an enormous scale, that I perhaps realize better
than most people what it all means. When I heard what you were
contemplating, I felt that it would be friendly to come round at once
and offer you the benefit of my experience.”
“Thank you,” said Claire.
Her eyes were so large and scornful and her voice held so satirical
an intonation that I interposed.
“Claire’s young cousins are very anxious to get up some theatricals
and to take advantage of having that young fellow here—Patch—to
do some writing for them. They’re working up something musical.”
“Delightful, indeed,” said Lady Annabel in a severe and melancholy
voice. “And is there much musical talent hereabouts?”
“Sallie Ambrey sings rather nicely, and Mrs. Fazackerly is really
musical—she is adapting Captain Patch’s libretto—and then there are
one or two others.”
“Let me warn you—” began Lady Annabel.
She suddenly glanced to the right and to the left of our not very
large drawing-room as though we might be suspected of having
concealed one of the servants behind a bookcase.
Then she sank her always low voice to a pitch that was all but a
whisper and most impressive.
“You understand that I am speaking in the utmost confidence? It
must never go beyond the walls of this room”—we all three
instinctively gazed with deep distrust at the walls—“I’m not thinking
of myself, but of what it might do for the Rector if it got round that I
had said anything about one of his people—you understand what I
mean—in the Rector’s position—”
Of course I said at once that I quite understood what she meant,
although one couldn’t help feeling that this was one of the moments
when Lady Annabel was perhaps confusing the Rector with “H. E.,”
the late Sir Hannabuss Tallboys. (We have all learned to think of him
as “H. E.”)
Claire did not join in my protestations. I judged from her expression
that she was, once more, living upon the edge of a volcano.
“Absolutely between ourselves, I should very strongly advise you not
to let anyone suggest that the young woman whom I most
mistakenly allowed to sing at the concert the other night—Mrs.
Harter—should be asked to perform. I should think it most
inadvisable.”
“May I ask why?”
Lady Annabel looked distressed.
“You do understand that I am speaking entirely unofficially?”
Not only did we understand, but, personally, I really did not see how
she could speak in any other way.
“Then,” said Lady Annabel, “the fact is that I have, since the concert,
heard one or two things about her. Naturally, I have links all over the
Empire, as I may say, and this Mrs. Harter, as you know, has just
come from the Near East. It seems that she and her husband are on
most unhappy terms—no doubt there are faults on both sides; in
fact, my correspondent said as much—but she has made herself
quite notorious in a place where everyone in the European colony is
of course watched and commented upon. And I noticed at the
concert the other evening that there was a tendency to bring her
into notice, simply, I suppose, because Cross Loman thinks it a fine
thing for Ellison the plumber’s daughter to have married a man
socially above her—Mr. Harter is a solicitor—and to have lived
abroad. If they only knew what I know as to the sort of people one
is obliged to receive out there!”
Lady Annabel Bending is not a spiteful woman. She would just as
readily, I am sure, have come to the Manor House in order to sing
the praises of Mrs. Harter as to disparage her. All that she ever
wants is still to be as important as she believes herself to have been
in her colonial service days.
Her admonitions clinched the question of Mrs. Harter’s inclusion in
the theatricals. Claire sent a note to Mrs. Fazackerly that afternoon, I
believe, to the effect that Mrs. Harter must by all means be asked to
sing, and if possible to act as well.
And if Nancy Fazackerly was at all taken aback by so rapid and
complete a volte-face, she was far too tactful ever to give any signs
of it.
Lady Annabel was not offended when Claire made her intentions
evident. She is never offended; she only becomes more remote and
her graciousness less smiling.
“I shall speak to the Rector about your invitation as soon as I can,
and hope to send you an answer to-morrow. You know what the
correspondence of a man in his position is. Pray don’t get up, Sir
Miles. Good-by—Good-by. So very glad—it all sounds charming. I
hope—we both hope—that it will be the very greatest success. But
I’m sure it will be. Good-by again.”
I rather think that she bowed, in an absent-minded way, to the
footman who opened the hall door for her.
The rectory possesses only a small governess cart and pony, and
Lady Annabel is driven out by the gardener’s boy. But she always, by
means of smiles and bows, and small waves of the hand, makes a
kind of royal progress for herself. It is her boast that she never
forgets a face, and in consequence a great number of the
tradespeople in Cross Loman are gratified by the marks of
recognition lavishly showered upon them from the rectory pony
carriage.
I was told afterwards by Miss Applebee, who saw it happen, that on
that particular day Lady Annabel was nearly run down by General
Kendal’s new motor car, which he was slowly driving up Fore Street.
Mumma was at her usual post of observation, beside him, and no
doubt she had said, “There’s the rectory pony cart coming towards
you, dear—I should sound the horn, if I were you.” But perhaps she
said it too soon, or repeated it so often that poor Puppa’s senses
became rather dazed and he ceased to take in the meaning of the
words. At all events, he appeared to drive the car deliberately, and
very, very slowly, straight at Lady Annabel.
But she never flinched at all, even when the gardener’s boy almost—
but not quite—drove her into the gutter in order to avoid a collision.
And when she subsequently mentioned the incident to Mary Ambrey,
Lady Annabel said that she did not wish any official notice to be
taken of it. Her manner distinctly gave Mary the impression that
General Kendal had narrowly escaped excommunication at the hands
of the Rector.
Chapter Six
Mrs. Harter did not come to discuss the play with the others that
afternoon, but Captain Patch went straight from the meeting to the
house in Queen Street and told her about it, and made her promise
to sing the “Bulbul Ameer” song.
Again I shall have to fall back upon what, in reality, can only be
guess-work, based upon what was afterward told me by Mary
Ambrey.
It was their second meeting, and it clinched matters, so far as Bill
Patch was concerned. Mrs. Harter may have known, too—probably
she did—but she held complexities in her nature that would make
her surrender a less simple and less instantaneous affair than his.
I can imagine that, realizing as she certainly did, the strength of the
extraordinary thing that was coming, inevitably, to overwhelm them
both, she may have hesitated for a moment—not from doubt or fear,
but simply in order to gauge, in one breathless instant, the smashing
force of the storm before it should break.
He went to see her, and they walked out of the narrow Queen Street
house and up Loman Hill to the crossroads there. She told him about
her life.
I have put together what I heard in the time, later on, when we
were all talking about her, and the little that she said to Mary in their
one interview, and the facts that afterward Nancy Fazackerly gave
me. And, knowing her turns of phraseology, which remained
characteristic of her class and of the defiant streak that ran all
through her, I have made out my own version of what she said.
She had been an ambitious girl. Cross Loman had not liked her and
she had not liked Cross Loman. Although she was not beautiful, she
possessed very powerful sex magnetism and had love affairs from
her schooldays onward. But the hard, practical vein that had come
to her direct from Ellison, the successful tradesman, never failed her.
She never lost her head. She despised her country-town lovers, even
while she flaunted their admiration in the face of all Cross Loman.
But she knew very well that only marriage could give her her
chance. Mr. Harter (I am sure that she spoke of him as “Mr. Harter”
throughout) was the uncle of one of her school friends. Diamond
Ellison went to stay with this girl at her home in one of the London
suburbs, and the solicitor—twenty years her senior—came to the
house and fell under one of the brief, incomprehensible spells that
young women of a certain type sometimes exercise over men no
longer in their first youth.
He misjudged her from first to last, probably misled by the boldness
of her mere physical outlines and the mixture of contempt and
familiarity in her manner towards men. His first proposals were
received by her with no sense of shock—she was both too
experienced in men and too ruthlessly cynical for that—but with
utter disdain.
“You can ask me to marry you—or you can clear out,” said Diamond
Ellison.
He married her.
In the East, she had all the success that she had expected and
intended to have. The women never liked her, but she knew herself
to be essentially a man’s woman, and she was indifferent then and
always to the opinions of her surroundings. The men fell in love with
her, and Harter was furiously jealous.
On her own showing, Harter had everything to complain of in his
wife. She did not pretend to care for him, she flirted with other men,
she was notorious, even judged by the lax standards of the East,
and she replied to his incessant, nagging remonstrances with sulky,
curt indifference. The only thing that he could never charge her with
was extravagance, for she was far too practical a woman to
squander money, and perhaps also too proud, since she had not a
penny of her own. (Mrs. Ellison was dead, and she had long ago
quarreled with old Ellison, who gave her nothing at all.)
Harter threatened to send her home, and she replied that she would
not go. Nor did she.
A far stronger man than Harter would have found it impossible to
get the better of her. A combination of recklessness and absolute
determination made her very nearly impervious.
She even took her pleasures sulkily and without enthusiasm,
although she never missed an entertainment or an expedition.
They had no child, of course.
Harter got her back to England at last, after nearly five years of it,
by pretending to book his passage as well, and then backing out of it
at the last minute.
She despised him all the more for the subterfuge. She herself was
never anything but absolutely direct.
She told Patch that she would not have come home even then, but
that she was ill, and it is very certain that only a woman of iron
physique and resolute will could have stood the climate, and the
racket of her days and nights, for that length of time.
As it was, she’d been a month in a London nursing home before she
came to Cross Loman. It was in the nursing home, I imagine, that
Diamond Harter took stock of life. She’d been in that home for
weeks and not a soul had been to see her. There was no one to
come. Her father had retired from business and lived by himself at
Torquay. They hadn’t even corresponded for years.
I have heard Mrs. Harter’s speaking voice—a voice stronger and
more abrupt than that of most women—and her tones ring in my
ears now, sometimes, so that I think I can distinguish the very
words that she may have used on the day that she and Bill Patch
went up Loman Hill together. But there must have been an
intonation in her voice then that neither I, nor anyone else, ever
heard there.
She told him that she’d never been in love. Men had stirred her
senses, and one or two of them had excited her half-resentful
admiration. She had a most acute power of distinguishing nuances
of breeding, and in the East she came into contact with a class of
man of very different caliber from that of Harter.
Not once, in all her twenty-eight years, had she even wished to
establish a permanent link between herself and a fellow creature.
And Bill Patch, who liked everybody and who was everybody’s friend,
listened to her.
I suppose it was just that element in Bill Patch that made a writer of
him, which enabled him to understand. Something rather beyond the
apprehension of most of us, to whom he was simply a good-
tempered, red-headed boy with an unexpected brain power. Only
Sallie, justifying her determination to specialize in psychology, had
seen rather further than other people when she said that Captain
Patch was a temperamental romantic, capable of a grande passion.
He listened to Diamond Harter and came, I suppose, as near to
perfect comprehension of her as one soul can ever come to perfect
comprehension of another. That is to say that he not only
understood what her words told him, but that he saw far beyond
them to the Diamond Harter that she might have been, and that—
almost unknown to herself—she must, sometimes, have dimly felt a
wish to be.
Whatever else there is to say about Mrs. Harter, it is indisputable
that she possessed a character of unusual strength and that there
were in her latent possibilities almost frightful in their intensity.
Bill Patch saw straight past everything, accepted everything, and
somehow made her see that he understood and that he accepted.
He was passionately in love with her—but that day on Loman Hill he
did not speak a word of love to her. There were no preliminary
explanations or tentative confidences between them. The whole
thing was too vital for that.
At the top of Loman Hill, at the crossroads, is a beech tree, on which
lovers have carved their initials for generations. It stands beside a
low hedge in which is set a rickety five-barred gate. It was at that
gate that they must have stood, as everyone stands, gazing at the
blue haze that lies over the hills beyond and at the square, red
sandstone tower of Cross Loman church below them.
I have stood at the crossroads on Loman Hill many and many a time
and looked over the five-barred gate, at the tower of St. Andrew’s,
and when I went there last, I thought of those two who must have
stood there together—Mrs. Harter and Captain Patch.
She was a tall woman, and her shoulders and his were nearly on a
level; and his red head topped hers only by a matter of a quarter of
an inch. I never saw Bill Patch wear a cap or a hat. Her clothes were
rather distinctive, and she wore them well. She had a figure for
tailor-made suits, and they were nearly always dark in color, and she
wore with them a white silk shirt, open at the throat. Her hats were
always severe—dark velours, of the plainest possible contour. Mary
says that she knew her style, and stuck to it.
It was characteristic of her to keep her hands thrust into her coat
pockets, and I always fancy that it was so that she leaned against
the rickety gate, her shoulders as erect as Bill’s were slouched.
He was so short-sighted that he never took off his glasses, and
through those queer, thick lenses he must have looked at her, as he
listened. His eyes always had that friendly smile in them, and that
odd, pathetic look that had reminded me of a Clumber spaniel. It
was his mouth that betrayed him, with the sensitive line of lip that
was visible only when he was not laughing. That gave one Bill Patch,
the writer and dreamer—Sallie’s potential romantic.
They stood at the crossways for a very long while, and, after a time,
in silence. Bill Patch knew, absolutely for certain, that he loved her,
and that they belonged to one another. The supreme importance of
it, in his eyes, made everything else of so little account that he did
not even wonder what would happen.
Mrs. Harter was different. She had never waited as Bill, quite
unconsciously, had waited. The thing had come upon her unawares,
and part of her—the part that had made her marry Harter, and then
flirt with other men—had absolutely denied the existence of the one
supreme reality.
But the capacity for recognizing it had been there all the time,
smothered under her cheap cynicism, her ruthless ambition, and the
streak in her of sheer, iron hardness.
She had to recognize it, when it came, and to surrender to it.
And so she was frightened, or at least overwhelmed, at first. Bill’s
intuition told him that, and he gave her time.
He told her that he’d been very happy all his life, even during the
war. His mother had died when he was too little to remember her,
and his father had married again. He was friends with his
stepmother. She and his father had two jolly little kids.
He had heaps of friends. A good many of them had gone west in the
war.
His writing, Bill Patch said, was a frightfully real thing in its way, but
it actually only took a bit of him to do it—he looked on it as a sort of
trick. He thought perhaps his subconscious self did most of it, and
that was why he could write so easily, and didn’t mind old Carey
chatting about poisoners all the time, or people talking in the room,
or anything. He knew it was a form of self-expression, for some
people, but it wasn’t for him. He didn’t, in fact, think he needed a
form of self-expression. He had always, he said again, been very
happy.
And all the time he had known that he was waiting for something,
and that it was something very great. But he hadn’t known at all
what it would be.
Sometimes I have wondered what Mrs. Harter made of it all, as she
listened to him. He was so much younger than she, in experience,
and in knowledge, and most of all in spirit. Mrs. Harter was, one
might say, temperamentally sophisticated, and Bill Patch, who was
two years her junior, was most essentially childlike. It is the only
adjective I can think of that comes anywhere near to describing that
quality in him that had made him, all his life, always happy.
There had never been any woman at all, “to count” he said. He had
gone straight from school into the Army, and he hadn’t thought
about girls much, although he greatly admired the pretty ones.
Always he came back to it again—he’d had that queer feeling of
waiting for something. He didn’t mean someone—a person—no, it
was more like a job, something that only he could do. It sounded
odd, Bill admitted, but there it was. Something to do, in a way, with
God. Yes, he believed in God.
And Mrs. Harter, who didn’t, and who never had, didn’t say a word.
It was Bill Patch who said at last that they ought to go. One
supposes that no single one of all the men whom Mrs. Harter had
known would have been sufficiently lacking in the technique of that
sort of situation, to propose putting an end to it. She wouldn’t have
given them the chance, probably saying it herself, with her most
disconcerting air of suddenly finding their company not at all worth
her while.
But when Bill Patch said that it was late, and that he ought to take
her home again, Mrs. Harter acquiesced, simply. They must have
taken a last look over the five-barred gate at the evening sky,
against which the red church tower always stands out with peculiar,
clear-cut precision of outline, before they turned away, and went
down the long slope of Loman Hill, which lies between high banks
where the green almost meets overhead.
Bill asked her about her singing, and she said that she’d learned at
school, and taken a few lessons just before she married. She used to
sing a good deal, in Cairo, because the men she knew liked it. Did
he understand, she asked him, that she was the sort of person who
sang only for that sort of reason? Once, at a party in a man’s rooms,
they’d put her right up on the top of the piano, and she’d sung
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade

Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.

Let us accompany you on the journey of exploring knowledge and


personal growth!

ebookmass.com

You might also like