Building a Social Networking Website
with Ruby on Rails™
Michael Hartl
Aurelius Prochazka
Upper Saddle River, NJ • Boston • Indianapolis • San Francisco
New York • Toronto • Montreal • London • Munich • Paris • Madrid
Capetown • Sydney • Tokyo • Singapore • Mexico City
New York • Toronto • Montreal • London • Munich • Paris • Madrid
Capetown • Sydney • Tokyo • Singapore • Mexico City
Thanks to Debra Williams Cauley for shepherding us through the publishing process.
We would also like to thank our technical reviewers, Francis Hwang and Michael Vanier,
for their careful reading and critiques.
Introduction
RailsSpace teaches you Ruby on Rails by developing a real-world application: RailsSpace, a social networking website aimed at the Rails community itself. We take you step by step, from the virtually static front page, through user registration and authentication, up to a highly dynamic site with user profiles, image upload, simple blogs, full-text and geographical search, and a friendship request system. Though certainly not intended as a serious competitor to the social networking heavyweights, RailsSpace is not a toy; it’s designed to show how to use Rails to make a web application suitable for deployment into a production environment.
1.1 Why Rails?
Ruby on Rails is a tool for making web applications—and it’s freakishly good at it. If
you’re reading this (which, evidently, you are), you’re probably wondering what makes
Rails so special. When you ask this of a programmer who has already fallen in love with
Rails, maybe he’ll tell you it’s “agile,” and you’ll have to read up on what that means
to coders. Or maybe you’ll be bombarded by a list of acronyms like ORM, MVC, or
DRY. While these are all cool features of Ruby on Rails, the real reason Rails rocks is
not about vocabulary; it’s about the philosophy of efficient design.
There’s that word: design. It’s a slippery concept, yet unmistakable. Good design is
like pornography: You know it when you see it. Rails is like very, very good pornography.
What makes the design of Rails so good is difficult to pin down, but we’ll do our best.
We think the heart of it is free productivity.
1.1.1 Productivity wants to be free
The essence of free productivity is the eerie ability Rails has to anticipate your needs as
a web programmer. That’s not very concrete, though, so let’s get a flavor for the free
productivity Rails provides by looking at a list of examples. Not all of these will make
sense to you right now, but they should help you develop an intuition for the kinds of
things Rails is good at.
• Ruby: Rails applications, as well as Rails itself, are written in Ruby, a dynamic,
object-oriented programming language. Ruby comes out of the Perl tradition, and
Yukihiro “Matz” Matsumoto, the creator of Ruby, calls it “a better Perl than Perl.”
In our experience, most programmers with exposure to both languages agree. We’d
add that, for web programming, embedded Ruby (ERb) is a better PHP than PHP.
Being able to tap into the power and elegance of Ruby is a major advantage of Rails.
• Mapping of database tables to Ruby objects: There are no messy SQL1 calls in
most Rails applications—instead you’ll find Ruby objects and methods. Rails does
the dirty database work behind the scenes. (If, by some chance, you do want to
execute some raw SQL, Rails lets you do that, too.)
• Automatic conversion of data models into HTML and back: There is seamless
integration between the code for modeling objects in your system and the code
to display them to the user. This is nowhere clearer than in data validations; for
example, if your data model requires users to put in an email address, but the user
submits a form without one, Rails can automatically catch the error and put it in a
variable for display back to the user.
• Built-in support for automated testing of data models and web pages: Rails
makes it easy to write test suites that verify the integrity of your data model and the
correctness of the pages on your site, allowing you to be confident that changes to
your code will not break your application.
• Database-independent creation and alteration of database tables: Rails migrations
make it easy to create your data models, make changes to them, and roll them
back if necessary, all in a way that makes it possible to use the same model for
different databases.
1.1.2 This productivity ain’t free
Now that we’ve given a sense of what free productivity is, it’s important to explain what
free productivity is not. Many frameworks come with lots of built-in functionality: a
slick administrative interface, fancy role-based authentication and authorization, or the
ability to write certain applications with virtually no code. Even Rails has this capability;
a feature called scaffolding renders certain kinds of form-database interactions trivial—
leading, among other things, to the infamous “15-minute blog engine.”2
That’s not free productivity. Built-in authentication or 15-minute blog engines
might make for great marketing, but between us friends we can probably agree that
any serious software application is going to take more than 15 minutes, no matter how
brilliant the framework. Don’t get us wrong; if, by some chance, the application you
have in mind is rendered trivial by some existing framework, by all means use it. But
if, as is more likely, you want to build something new, you need a framework that will
help you when you need it and will get out of your way when you don’t. You need a
tool that will help you realize your vision; a tool flexible enough to change as your vision
changes; a tool that, in the end, mutates into a new framework—one that feels as if it
were custom-made to write your exact application.
This is where Rails excels. Rails is great at making custom applications—Rails helps
you make the application you want. You don’t switch to Rails because of the cool message
board or blog software available. You switch because the process of creating your own
custom apps is so much easier using Rails.
1.2 Why this book?
There’s a tension in any educational book between the two extremes of pure tutorial and
pure reference. We land firmly on the tutorial side of this spectrum, which has many
advantages. Because our application is real, you get to learn Rails as it is actually used.
You’ll see for yourself all the great ways that Rails makes writing web applications easier,
from its elegant model-view-controller architecture to the brilliance of embedded Ruby.
You also get the advantage of artful repetition: You see the most important ideas the
most. The result is learning by osmosis.
In the process of building RailsSpace, we’ll also see how Rails makes it easy to write
automated tests to make sure our application does what we wanted it to do. Because of
our tutorial approach, we will be able to develop the tests incrementally in parallel with
the application, just as you do (or should do) in real life. As our application evolves,
you’ll see how great having a test suite is: Whenever we modify a feature or add a new
one, we can run the tests to make sure that we haven’t broken some other part of the application.
Of course, while good for learning, a tutorial approach is not as good for looking
things up. As a counterbalance to RailsSpace, we recommend AgileWeb Development with
Rails by Dave Thomas and David Heinemeier Hansson (from Pragmatic Programmers),
the original introduction to Rails. AWDwR contains a wealth of reference material
documenting most aspects of the Rails framework. For learning Ruby we recommend
Programming Ruby, Second Edition, by Dave Thomas et al. (Pragmatic Programmers),
and The Ruby Way, Second Edition, by Hal Fulton (Addison-Wesley).
1.3 Who should read this book?
Since you’re reading this, the chances are good that you should read this book. RailsSpace
is an introductory book, so we don’t assume any knowledge of the Ruby programming
language or of the Rails framework. Of course, any previous exposure to either will be
beneficial. Even if you have some prior experience with Rails, we hope you can learn
something from our book, especially since we use several features (such as migrations,
form generators, REST, and Ajax support) introduced in the more recent releases of Rails.
To get the most out of RailsSpace, you should probably know at least one programming
language, and familiarity with object-oriented programming would be especially
helpful. Java is probably sufficient background,3 and it’s even better if you are familiar
with a dynamically typed language such as Perl, Python, or PHP (or Ruby!). It would be
especially good if you’ve used one of these languages to make dynamic websites. Some
exposure to JavaScript and Cascading Style Sheets (CSS) would also be good. At the
very least, you should be familiar with making static websites using HTML. Finally, we
assume a certain level of computer sophistication, so that when we say “Go download
MySQL and install it” you’re confident that (given enough time and coffee) you can
probably do it, even if you’ve never installed a database before.
All of these supposed prerequisites are really beside the point, though. By far the
most important factor is your enthusiasm for learning how to make web applications
with Rails. If you’re excited about putting something cool on the web, this book is for
you, no matter what your background is.
1.3.1 How to read this book
RailsSpace is designed to be read from start to finish, and you’ll get the most out of it if
you code along with us, but we’ve designed the book to be instructive and interesting
even if you’re not sitting in front of the computer. Also, it’s important to be patient. If
you don’t get something right away, keep pressing forward, and give the ideas a little time
to sink in. You’ll be amazed at how something that confuses you at the beginning of a
chapter will seem trivial by the end. You’re training your personal neural network how to
recognize Ruby on Rails patterns, and there’s no substitute for practice and perseverance.
1.3.2 How to watch this book
The accompanying website for this book can be found at
There you’ll find errata, sample data, and downloadable source code for RailsSpace.
You’ll also find links to screencasts for each chapter—narrated movies of the book’s source code
coming to life.
Product details
Price
|
|
---|---|
File Size
| 10,050 KB |
Pages
|
564 p |
File Type
|
PDF format |
ISBN-13
ISBN-10 | 978-0-321-48079-8 0-321-48079-1 |
Copyright
| 2008 Pearson Education |
Contents
List of figures xvii
Acknowledgments xxi
Chapter 1 Introduction
1.1 Why Rails? 1
1.1.1 Productivity wants to be free 2
1.1.2 This productivity ain’t free 3
1.2 Why this book? 3
1.3 Who should read this book? 4
1.3.1 How to read this book 5
1.3.2 How to watch this book 5
1.4 A couple of Rails stories 5
1.4.1 Aure 5
1.4.2 Michael 7
PART I Foundations
Chapter 2 Getting Started
2.1 Preliminaries 13
2.1.1 Setting up your development environment 15
2.1.2 Running with rails 15
2.1.3 Development server 18
2.2 Our first pages 20
2.2.1 Generating a controller 20
2.2.2 The Site controller 22
2.2.3 Rails URLs 24
2.2.4 Changing the route 25
2.3 Rails views 26
2.3.1 Embedded Ruby 27
2.4 Layouts 28
2.4.1 ERb, actions, and instance variables 30
2.4.2 Recap: slicing up a page 32
2.4.3 Adding navigation 32
2.4.4 Hashes 34
2.4.5 Symbols 35
2.4.6 Polishing up link_to 35
2.4.7 Some matters of style 36
2.4.8 Polishing navigation 37
2.4.9 Finding things for yourself 37
2.5 Developing with style 38
Chapter3 Modeling users
3.1 Creating the User model 43
3.1.1 Setting up the database 43
3.1.2 Migrations and the User model 46
3.1.3 The first user migration 47
3.1.4 Raking the migration 49
3.2 User model validations 51
3.2.1 The console 52
3.2.2 A simple validation 54
3.2.3 Validations in action 56
3.2.4 Improving validations 57
3.2.5 Full-strength validations 59
3.2.6 Magic columns 61
3.3 Further steps to ensure data integrity (?) 63
Chapter4 Registering users
4.1 A User controller 65
4.2 User registration: The view 66
4.2.1 The registration view: Appearance 66
4.2.2 Understanding the registration view 70
4.2.3 Registration form refinements 72
4.2.4 Fun with forms—and debug 75
4.3 User registration: The action 77
4.3.1 Form error messages 82
4.3.2 Flash 85
4.3.3 The finished register function 88
4.3.4 A hub stub 89
4.4 Linking in Registration 90
4.4.1 Helper files 92
4.5 An example user 95
Chapter 5 Getting started with testing
5.1 Our testing philosophy 98
5.2 Test database configuration 98
5.3 Site controller testing 99
5.3.1 A nontrivial test 100
5.3.2 Test overkill? 103
5.4 Registration testing 103
5.4.1 Running functional tests 104
5.4.2 Basic registration tests 104
5.4.3 Testing successful registration 107
5.4.4 Testing unsuccessful registration 108
5.4.5 Running the tests 110
5.4.6 More registration tests? 111
5.5 Basic User model testing 111
5.5.1 Basic validation testing 113
5.6 Detailed User model testing 115
5.6.1 Testing uniqueness 115
5.6.2 Testing screen name length 117
5.6.3 Detour: “Use the Console, Luke.” 118
5.6.4 Testing password length 120
5.6.5 Testing regexps 122
5.6.6 Running all tests 128
Chapter 6 Logging in and out
6.1 Maintaining state with sessions 131
6.1.1 Setting up database sessions 132
6.2 Logging in 134
6.2.1 Tracking login status 134
6.2.2 Registration login 134
6.2.3 Debugging with the session variable 135
6.2.4 Login view and action 138
6.2.5 Testing valid login 142
6.2.6 Testing invalid login 145
6.3 Logging out 146
6.3.1 Testing logout 147
6.3.2 Testing navigation 148
6.4 Protecting pages 150
6.4.1 Protecting pages the stupid way 151
6.4.2 Protecting pages the smart way 152
6.4.3 Testing protection 155
6.5 Friendly URL forwarding 156
6.5.1 The request variable 156
6.5.2 Friendly login forwarding 158
6.5.3 Friendly register forwarding 161
6.5.4 Friendly testing 162
6.6 Refactoring basic login 164
6.6.1 Logged in? 164
6.6.2 Log in! 168
6.6.3 Log out! 172
6.6.4 Clear password! 173
6.6.5 Unduplicated form handling 176
6.6.6 Unduplicated friendly forwarding 177
6.6.7 Sanity check 179
Chapter7 Advanced login
7.1 So you say you want to be remembered? 181
7.1.1 A “remember me” box 182
7.1.2 A “remember me” attribute 184
7.1.3 The “remember me” cookie 186
7.2 Actually remembering the user 192
7.2.1 An authorization cookie 193
7.2.2 Remembering that we remembered 195
7.2.3 Updating logout 197
7.2.4 A more secure cookie 199
7.2.5 The finished (?) functions 201
7.3 “Remember me” tests 203
7.3.1 Updated login tests 203
7.3.2 Updated logout test 209
7.4 Advanced tests: Integration testing 209
7.4.1 Testing cookie remembering: The first cut 210
7.4.2 Testing the test: A cautionary tale 212
7.4.3 Some reflections on Rails testing 214
7.5 Refactoring redux 215
7.5.1 Refactoring remember 216
7.5.2 Refactoring forget 218
7.5.3 Just two more bits of polish 219
7.5.4 The fully refactored login function 222
7.5.5 Some parting thoughts 223
Chapter8 Updating user information
8.1 A non-stub hub 226
8.2 Updating the email address 226
8.3 Updating password 229
8.3.1 Handling password submissions 233
8.4 Testing user edits 237
8.4.1 Test helpers 237
8.4.2 Testing the edit page 242
8.4.3 An advanced test 243
8.5 Partials 245
8.5.1 Two simple partials 246
8.5.2 A more advanced partial 247
8.5.3 A wrinkle, then done 249
8.5.4 Updating login and register 250
PART II Building a social network
Chapter9 Personal profiles
9.1 A user profile stub 256
9.1.1 Profile URLs 256
9.1.2 Profile controller and actions 258
9.2 User specs 260
9.2.1 Generating the Spec model 260
9.2.2 The Spec model 262
9.2.3 Tying models together 264
9.3 Editing the user specs 266
9.3.1 Spec controller 266
9.3.2 An HTML utility 268
9.3.3 The spec edit view 270
9.3.4 Protecting specs 272
9.3.5 Testing specs 273
9.4 Updating the user hub 277
9.4.1 The new hub view 277
9.4.2 A Spec box 280
9.4.3 Named routes and the profile URL 282
9.4.4 The hub main content 285
9.5 Personal FAQ: Interests and personality 288
9.5.1 The FAQ model 288
9.5.2 The FAQ controller 292
9.5.3 Editing the FAQ 293
9.5.4 Adding the FAQ to the hub 294
9.5.5 FAQ tests 298
9.6 Public-facing profile 299
Chapter 10 Community
10.1 Building a community (controller) 303
10.2 Setting up sample users 304
10.2.1 Collecting the data 305
10.2.2 Loading the data 305
10.3 The community index 308
10.3.1 find’s new trick 309
10.3.2 The index action 311
10.3.3 The alphabetical index 313
10.3.4 Displaying index results 316
10.4 Polishing results 320
10.4.1 Adding pagination 321
10.4.2 A results summary 323
Chapter 11 Searching and browsing
11.1 Searching 327
11.1.1 Search views 328
11.1.2 Ferret 330
11.1.3 Searching with find_by_contents 332
11.1.4 Adding pagination to search 336
11.1.5 An exception to the rule 339
11.2 Testing search 341
11.3 Beginning browsing 343
11.3.1 The browse page 343
11.3.2 Find by A/S/L (hold the L) 345
11.4 Location, location, location 350
11.4.1 A local database of geographical data 351
11.4.2 Using GeoData for location search 352
11.4.3 Location names 355
11.4.4 Adding browse validation 358
11.4.5 The final community home page 363
Chapter 12 Avatars
12.1 Preparing for avatar upload 365
12.1.1 Adapting a model 366
12.1.2 Avatar upload page 368
12.1.3 An avatar partial 371
12.2 Manipulating avatars 373
12.2.1 ImageMagick and convert 374
12.2.2 The save method 377
12.2.3 Adding validations 378
12.2.4 Deleting avatars 382
12.2.5 Testing Avatars 385
Chapter 13 Email
13.1 Action Mailer 389
13.1.1 Configuration 390
13.1.2 Password reminder 391
13.1.3 Linking and delivering the reminder 392
13.1.4 Testing the reminder 396
13.2 Double-blind email system 399
13.2.1 Email link 399
13.2.2 correspond and the email form 401
13.2.3 Email message 403
13.2.4 Testing double-blind email 406
Chapter 14 Friendships
14.1 Modeling friendships 411
14.1.1 Friendships in the abstract 412
14.1.2 Friendship model 413
14.1.3 Creating pending friendships 415
14.1.4 Friendship request 416
14.1.5 Completing the Friendship model 417
14.1.6 Testing the Friendship model 419
14.2 Friendship requests 420
14.2.1 Friendship request link 420
14.2.2 Controlling the request 423
14.3 Managing friendships 426
14.3.1 has_many :through 426
14.3.2 Hub friendships 428
14.3.3 Friendship actions 431
14.3.4 Testing friendship requests 433
Chapter 15 RESTful blogs
15.1 We deserve a REST today 438
15.1.1 REST and CRUD 439
15.1.2 URL modifiers 441
15.1.3 An elephant;in the room 442
15.1.4 Responding to formats and a free API 444
15.2 Scaffolds for a RESTful blog 445
15.2.1 The first RESTful resource 445
15.2.2 Blog posts 447
15.2.3 The Posts controller 450
15.3 Building the real blog 454
15.3.1 Connecting the models 454
15.3.2 Blog and post routing 455
15.3.3 Posts controller, for real 456
15.3.4 Blog management 459
15.3.5 Creating posts 461
15.3.6 Showing posts 463
15.3.7 Editing posts 467
15.3.8 Publishing posts 468
15.3.9 One final niggling detail 471
15.4 RESTful testing 473
15.4.1 Default REST functional tests 474
15.4.2 Two custom tests 476
Chapter 16 Blog comments with Ajax
16.1 RESTful comments 479
16.1.1 Comments resource 480
16.1.2 Comment model and associations 481
16.1.3 The Comments controller and a preemptive partial 482
16.1.4 Routing comments 484
16.2 Beginning Ajax 485
16.2.1 New comments 486
16.2.2 Comment creation 490
16.2.3 Destroying comments 492
16.3 Visual effects 495
16.3.1 RJS files and the first effect 495
16.3.2 Two more effects 497
16.3.3 A cancel button 499
16.3.4 Degrading gracefully 499
16.4 Debugging and testing 501
16.4.1 Another look at new 502
16.4.2 Testing Ajax with xhr 502
Chapter 17 What next?
17.1 Deployment considerations 505
17.1.1 Software and hardware options 506
17.1.2 Running in production mode 506
17.1.3 A minimal production server 508
17.1.4 Scaling 509
17.1.5 Administration basics 511
17.2 More Ruby and Rails 515
Index 517
Figures
2.1 Top-level directory structure. . . . . . . . . . . . . . . . . . . . . . . 16
2.2 Rails, as a pie chart. (Mmm. . . pie.) . . . . . . . . . . . . . . . . . . . 17
2.3 Here’s proof that your server is running. . . . . . . . . . . . . . . . . 19
2.4 Information about the Ruby on Rails environment. . . . . . . . . . . 19
2.5 Simple representation of the MVC architecture. . . . . . . . . . . . . 21
2.6 Tree view of the files we created with generate. . . . . . . . . . . . . 22
2.7 The default view for the Site controller. . . . . . . . . . . . . . . . . . 24
2.8 Our front page. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.9 About Us page. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.10 Help page. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.11 A navigation bar now appears at the top of each page. . . . . . . . . . 33
2.12 The effect of using the link_to_unless_current. . . . . . . . . . . 38
2.13 The main page with CSS defined. . . . . . . . . . . . . . . . . . . . 41
2.14 The about page with CSS defined. . . . . . . . . . . . . . . . . . . . 41
2.15 The help page with CSS defined. . . . . . . . . . . . . . . . . . . . . 41
3.1 Users schema as displayed by CocoaMySQL. . . . . . . . . . . . . . . 50
4.1 Registration page. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.2 A more stylish registration page. . . . . . . . . . . . . . . . . . . . . 71
4.3 Registration page after submission with foo, bar, and baz
in the three fields. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.4 The development log (with user params foo, bar, baz). . . . . . . . 79
4.5 Exception created by raise params[:user].inspect. . . . . . . . 80
4.6 Errors reported by a blank submission. . . . . . . . . . . . . . . . . . 84
4.7 Pretty error reporting. . . . . . . . . . . . . . . . . . . . . . . . . . . 85
4.8 A different set of errors found. . . . . . . . . . . . . . . . . . . . . . 86
4.9 Flash notice announcing successful user creation. . . . . . . . . . . . . 90
4.10 Rails application documentation. . . . . . . . . . . . . . . . . . . . . 94
6.1 A typical multiple webserver, single database server setup. . . . . . . . 133
6.2 Session information when registering a new user. . . . . . . . . . . . . 136
6.3 Debug links appear at the bottom, separated from the content. . . . . . 138
6.4 Debug information display when both params
and session are clicked. . . . . . . . . . . . . . . . . . . . . . . . . . 139
6.5 Login page. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
6.6 Login-dependent navigation bar. . . . . . . . . . . . . . . . . . . . . 147
6.7 The full request dump with REQUEST_URI highlighted. . . . . . . . . . 158
6.8 The full request dump with env_table highlighted. . . . . . . . . . . 159
6.9 The environment hash displayed separate from the request. . . . . . . . 160
7.1 The “remember me?” checkbox. . . . . . . . . . . . . . . . . . . . . 182
7.2 The error Rails displays when trying to log in foobar. . . . . . . . . . 184
7.3 Debug information shown when blank form is submitted with
checkbox unchecked. . . . . . . . . . . . . . . . . . . . . . . . . . . 186
7.4 Debug information shown when a blank form is submitted
with the checkbox checked. . . . . . . . . . . . . . . . . . . . . . . . 187
7.5 Viewing cookies on the Safari browser. . . . . . . . . . . . . . . . . . 202
8.1 The logged-in user experience begins to take form. . . . . . . . . . . . 227
8.2 Editing the email address (with error). . . . . . . . . . . . . . . . . . 228
8.3 Changing the email address to bazquux@example.com. . . . . . . . . 229
8.4 The edit page with password added. . . . . . . . . . . . . . . . . . . 232
8.5 The final edit form. . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
8.6 Errors generated manually and using the valid? function. . . . . . . . 238
8.7 The registration page now handles password confirmation
automatically. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
9.1 The basic stub for Foo Bar’s RailsSpace profile. . . . . . . . . . . . . . 259
9.2 The result of visiting http://localhost:3000/profile/foobar2
for nonexistent user “foobar2.” . . . . . . . . . . . . . . . . . . . . . 260
9.3 Editing Foo Bar’s spec. . . . . . . . . . . . . . . . . . . . . . . . . . 271
9.4 Exposing the date select implementation by way of posting an invalid
date (a date that was in the future when this chapter was written). . . . 272
9.5 The updated user hub with user info moved into a sidebar. . . . . . . . 280
9.6 The updated user hub with spec sidebar. . . . . . . . . . . . . . . . . 283
9.7 The user hub with the right column starting to fill in. . . . . . . . . . 288
9.8 The FAQ edit page. Feel free to change the rows/cols to suit
your style! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
9.9 The user hub updated with the FAQ. . . . . . . . . . . . . . . . . . . 297
9.10 A public-facing profile. . . . . . . . . . . . . . . . . . . . . . . . . . 302
10.1 The finished community index (shown for the letter H). . . . . . . . . 308
10.2 The RailsSpace community page with a nicely styled
alphabetical index. . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
10.3 The final form of the community index. . . . . . . . . . . . . . . . . 319
10.4 Page after adding style to the results table. . . . . . . . . . . . . . . . 321
10.5 Paginated alphabetical listing. . . . . . . . . . . . . . . . . . . . . . . 324
11.1 The evolving community index page now includes a search form. . . . 330
11.2 Search results for q=*, returning unpaginated results for all users. . . . . 335
11.3 Search results for q=*, returning paginated results for all users. . . . . . 338
11.4 Ferret throws an exception when given an invalid search string. . . . . . 340
11.5 The ferret query parse exception caught and handled. . . . . . . . . . 341
11.6 The final browse form. . . . . . . . . . . . . . . . . . . . . . . . . . 346
11.7 The browse form with some values submitted. . . . . . . . . . . . . . 346
11.8 Browsing our database by restricting spec parameters. . . . . . . . . . 350
11.9 The geographical data in the database. . . . . . . . . . . . . . . . . . 353
11.10 All female RailsSpace users between the ages of 50 and 65
within 250 miles of Caltech. . . . . . . . . . . . . . . . . . . . . . . 356
11.11 All male RailsSpace users between the ages of 65
and 90 within 250 miles of Caltech. . . . . . . . . . . . . . . . . . . 357
11.12 Browse results with city and state lookup. . . . . . . . . . . . . . . . . 359
11.13 Browsing for “foo” instead of integers. . . . . . . . . . . . . . . . . . 360
11.14 Browse form with a nice description of the errors. . . . . . . . . . . . 363
11.15 The final community page with index, browse, and search. . . . . . . . 364
12.1 The initial avatar upload page. . . . . . . . . . . . . . . . . . . . . . 371
12.2 The user hub with a link to avatar upload. . . . . . . . . . . . . . . . 373
12.3 Browsing for an avatar image. . . . . . . . . . . . . . . . . . . . . . . 379
12.4 The user hub after a successful avatar upload. . . . . . . . . . . . . . . 380
12.5 The error message for an invalid image type. . . . . . . . . . . . . . . 383
12.6 Confirming avatar deletion with JavaScript. . . . . . . . . . . . . . . 384
13.1 The login page with screen name/password reminder. . . . . . . . . . 393
13.2 The email reminder form. . . . . . . . . . . . . . . . . . . . . . . . 395
13.3 The email correspondence page with errors. . . . . . . . . . . . . . . 403
13.4 A RailsSpace email sent by Foo Bar (in development mode). . . . . . . 406
14.1 A sketch of the database tables needed to model friendships. . . . . . . 412
14.2 The two rows for a (potential) friendship between Foo (1)
and Baz (2). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
14.3 A user’s profile with friendship request link and confirmation
message. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
14.4 A user’s profile immediately after a friendship request. . . . . . . . . . 425
14.5 A friendship request email. . . . . . . . . . . . . . . . . . . . . . . . 425
14.6 The user hub with requested and pending friends. . . . . . . . . . . . 431
14.7 A user profile with friend listing. . . . . . . . . . . . . . . . . . . . . 434
15.1 A hypothetical RESTful Specs resource. . . . . . . . . . . . . . . . . 440
15.2 Nested resources for RESTful blog posts. . . . . . . . . . . . . . . . . 448
15.3 Blog post management using the posts index page. . . . . . . . . . . . 461
15.4 The blog post creation page. . . . . . . . . . . . . . . . . . . . . . . 463
15.5 The default show page. . . . . . . . . . . . . . . . . . . . . . . . . . 464
15.6 Styled show page. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466
15.7 Index page with a couple of blog entries added. . . . . . . . . . . . . . 466
15.8 The post edit form (with a funky semicolon URL). . . . . . . . . . . . 468
15.9 The blog on the profile page. . . . . . . . . . . . . . . . . . . . . . . 471
16.1 Nested resources for RESTful blog comments. . . . . . . . . . . . . . 485
16.2 The “Add a comment” link becomes the comment form through
the magic of Ajax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
16.3 The form goes away and the comment shows up. . . . . . . . . . . . . 492
16.4 Blog post comment with a “delete” link. . . . . . . . . . . . . . . . . 494
16.5 With the puff effect, a deleted comment grows in size as it
fades away. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
17.1 The RailsSpace homepage in a production environment,
with no debug links. . . . . . . . . . . . . . . . . . . . . . . . . . . 508
17.2 The local error page for an invalid request. . . . . . . . . . . . . . . . 514
17.3 The public error page for file not found errors (404). . . . . . . . . . . 514
17.4 The application error page (500) (in this case, a syntax error
in the routes.rb file). . . . . . . . . . . . . . . . . . . . . . . . . . 514
●▬▬▬▬▬❂❂❂▬▬▬▬▬●
●▬▬❂❂▬▬●
●▬❂▬●
●❂●