Ruby on Rails Tutorial

Learn Rails by Example

Michael Hartl

Contents

  1. Chapter 1 From zero to deploy
    1. 1.1 Introduction
      1. 1.1.1 Comments for various readers
      2. 1.1.2 “Scaling” Rails
      3. 1.1.3 Conventions in this book
    2. 1.2 Up and running
      1. 1.2.1 Development environments
        1. IDEs
        2. Text editors and command lines
        3. Browsers
        4. A note about tools
      2. 1.2.2 Ruby, RubyGems, Rails, and Git
        1. Rails Installer (Windows)
        2. Install Git
        3. Install Ruby
        4. Install RubyGems
        5. Install Rails
      3. 1.2.3 The first application
      4. 1.2.4 Bundler
      5. 1.2.5 rails server
      6. 1.2.6 Model-view-controller (MVC)
    3. 1.3 Version control with Git
      1. 1.3.1 Installation and setup
        1. First-time system setup
        2. First-time repository setup
      2. 1.3.2 Adding and committing
      3. 1.3.3 What good does Git do you?
      4. 1.3.4 GitHub
      5. 1.3.5 Branch, edit, commit, merge
        1. Branch
        2. Edit
        3. Commit
        4. Merge
        5. Push
    4. 1.4 Deploying
      1. 1.4.1 Heroku setup
      2. 1.4.2 Heroku deployment, step one
      3. 1.4.3 Heroku deployment, step two
      4. 1.4.4 Heroku commands
    5. 1.5 Conclusion
  2. Chapter 2 A demo app
    1. 2.1 Planning the application
      1. 2.1.1 Modeling users
      2. 2.1.2 Modeling microposts
    2. 2.2 The Users resource
      1. 2.2.1 A user tour
      2. 2.2.2 MVC in action
      3. 2.2.3 Weaknesses of this Users resource
    3. 2.3 The Microposts resource
      1. 2.3.1 A micropost microtour
      2. 2.3.2 Putting the micro in microposts
      3. 2.3.3 A user has_many microposts
      4. 2.3.4 Inheritance hierarchies
      5. 2.3.5 Deploying the demo app
    4. 2.4 Conclusion
  3. Chapter 3 Mostly static pages
    1. 3.1 Static pages
      1. 3.1.1 Truly static pages
      2. 3.1.2 Static pages with Rails
    2. 3.2 Our first tests
      1. 3.2.1 Testing tools
        1. Autotest
      2. 3.2.2 TDD: Red, Green, Refactor
        1. Spork
        2. Red
        3. Green
        4. Refactor
    3. 3.3 Slightly dynamic pages
      1. 3.3.1 Testing a title change
      2. 3.3.2 Passing title tests
      3. 3.3.3 Instance variables and Embedded Ruby
      4. 3.3.4 Eliminating duplication with layouts
    4. 3.4 Conclusion
    5. 3.5 Exercises
  4. Chapter 4 Rails-flavored Ruby
    1. 4.1 Motivation
      1. 4.1.1 A title helper
      2. 4.1.2 Cascading Style Sheets
    2. 4.2 Strings and methods
      1. 4.2.1 Comments
      2. 4.2.2 Strings
        1. Printing
        2. Single-quoted strings
      3. 4.2.3 Objects and message passing
      4. 4.2.4 Method definitions
      5. 4.2.5 Back to the title helper
    3. 4.3 Other data structures
      1. 4.3.1 Arrays and ranges
      2. 4.3.2 Blocks
      3. 4.3.3 Hashes and symbols
      4. 4.3.4 CSS revisited
    4. 4.4 Ruby classes
      1. 4.4.1 Constructors
      2. 4.4.2 Class inheritance
      3. 4.4.3 Modifying built-in classes
      4. 4.4.4 A controller class
      5. 4.4.5 A user class
    5. 4.5 Exercises
  5. Chapter 5 Filling in the layout
    1. 5.1 Adding some structure
      1. 5.1.1 Site navigation
      2. 5.1.2 Custom CSS
      3. 5.1.3 Partials
    2. 5.2 Layout links
      1. 5.2.1 Integration tests
      2. 5.2.2 Rails routes
      3. 5.2.3 Named routes
    3. 5.3 User signup: A first step
      1. 5.3.1 Users controller
      2. 5.3.2 Signup URL
    4. 5.4 Conclusion
    5. 5.5 Exercises
  6. Chapter 6 Modeling and viewing users, part I
    1. 6.1 User model
      1. 6.1.1 Database migrations
      2. 6.1.2 The model file
        1. Model annotation
        2. Accessible attributes
      3. 6.1.3 Creating user objects
      4. 6.1.4 Finding user objects
      5. 6.1.5 Updating user objects
    2. 6.2 User validations
      1. 6.2.1 Validating presence
      2. 6.2.2 Length validation
      3. 6.2.3 Format validation
      4. 6.2.4 Uniqueness validation
        1. The uniqueness caveat
    3. 6.3 Viewing users
      1. 6.3.1 Debug and Rails environments
      2. 6.3.2 User model, view, controller
      3. 6.3.3 A Users resource
        1. params in debug
    4. 6.4 Conclusion
    5. 6.5 Exercises
  7. Chapter 7 Modeling and viewing users, part II
    1. 7.1 Insecure passwords
      1. 7.1.1 Password validations
      2. 7.1.2 A password migration
      3. 7.1.3 An Active Record callback
    2. 7.2 Secure passwords
      1. 7.2.1 A secure password test
      2. 7.2.2 Some secure password theory
      3. 7.2.3 Implementing has_password?
      4. 7.2.4 An authenticate method
    3. 7.3 Better user views
      1. 7.3.1 Testing the user show page (with factories)
      2. 7.3.2 A name and a Gravatar
        1. A Gravatar helper
      3. 7.3.3 A user sidebar
    4. 7.4 Conclusion
      1. 7.4.1 Git commit
      2. 7.4.2 Heroku deploy
    5. 7.5 Exercises
  8. Chapter 8 Sign up
    1. 8.1 Signup form
      1. 8.1.1 Using form_for
      2. 8.1.2 The form HTML
    2. 8.2 Signup failure
      1. 8.2.1 Testing failure
      2. 8.2.2 A working form
      3. 8.2.3 Signup error messages
      4. 8.2.4 Filtering parameter logging
    3. 8.3 Signup success
      1. 8.3.1 Testing success
      2. 8.3.2 The finished signup form
      3. 8.3.3 The flash
      4. 8.3.4 The first signup
    4. 8.4 RSpec integration tests
      1. 8.4.1 Integration tests with style
      2. 8.4.2 Users signup failure should not make a new user
      3. 8.4.3 Users signup success should make a new user
    5. 8.5 Conclusion
    6. 8.6 Exercises
  9. Chapter 9 Sign in, sign out
    1. 9.1 Sessions
      1. 9.1.1 Sessions controller
      2. 9.1.2 Signin form
    2. 9.2 Signin failure
      1. 9.2.1 Reviewing form submission
      2. 9.2.2 Failed signin (test and code)
    3. 9.3 Signin success
      1. 9.3.1 The completed create action
      2. 9.3.2 Remember me
      3. 9.3.3 Current user
    4. 9.4 Signing out
      1. 9.4.1 Destroying sessions
      2. 9.4.2 Signin upon signup
      3. 9.4.3 Changing the layout links
      4. 9.4.4 Signin/out integration tests
    5. 9.5 Conclusion
    6. 9.6 Exercises
  10. Chapter 10 Updating, showing, and deleting users
    1. 10.1 Updating users
      1. 10.1.1 Edit form
      2. 10.1.2 Enabling edits
    2. 10.2 Protecting pages
      1. 10.2.1 Requiring signed-in users
      2. 10.2.2 Requiring the right user
      3. 10.2.3 Friendly forwarding
    3. 10.3 Showing users
      1. 10.3.1 User index
      2. 10.3.2 Sample users
      3. 10.3.3 Pagination
        1. Testing pagination
      4. 10.3.4 Partial refactoring
    4. 10.4 Destroying users
      1. 10.4.1 Administrative users
        1. Revisiting attr_accessible
      2. 10.4.2 The destroy action
    5. 10.5 Conclusion
    6. 10.6 Exercises
  11. Chapter 11 User microposts
    1. 11.1 A Micropost model
      1. 11.1.1 The basic model
        1. Accessible attribute
      2. 11.1.2 User/Micropost associations
      3. 11.1.3 Micropost refinements
        1. Default scope
        2. Dependent: destroy
      4. 11.1.4 Micropost validations
    2. 11.2 Showing microposts
      1. 11.2.1 Augmenting the user show page
      2. 11.2.2 Sample microposts
    3. 11.3 Manipulating microposts
      1. 11.3.1 Access control
      2. 11.3.2 Creating microposts
      3. 11.3.3 A proto-feed
      4. 11.3.4 Destroying microposts
      5. 11.3.5 Testing the new home page
    4. 11.4 Conclusion
    5. 11.5 Exercises
  12. Chapter 12 Following users
    1. 12.1 The Relationship model
      1. 12.1.1 A problem with the data model (and a solution)
      2. 12.1.2 User/relationship associations
      3. 12.1.3 Validations
      4. 12.1.4 Following
      5. 12.1.5 Followers
    2. 12.2 A web interface for following and followers
      1. 12.2.1 Sample following data
      2. 12.2.2 Stats and a follow form
      3. 12.2.3 Following and followers pages
      4. 12.2.4 A working follow button the standard way
      5. 12.2.5 A working follow button with Ajax
    3. 12.3 The status feed
      1. 12.3.1 Motivation and strategy
      2. 12.3.2 A first feed implementation
      3. 12.3.3 Scopes, subselects, and a lambda
      4. 12.3.4 The new status feed
    4. 12.4 Conclusion
      1. 12.4.1 Extensions to the sample application
        1. Replies
        2. Messaging
        3. Follower notifications
        4. Password reminders
        5. Signup confirmation
        6. RSS feed
        7. REST API
        8. Search
      2. 12.4.2 Guide to further resources
    5. 12.5 Exercises
  13. Chapter 13 Rails 3.1
    1. 13.1 Upgrading the sample app
      1. 13.1.1 Installing and configuring Rails 3.1
      2. 13.1.2 Getting to Red
      3. 13.1.3 Minor issues
        1. will_paginate
        2. Pagination spec
      4. 13.1.4 Major differences
        1. Asset directories
        2. Prototype to jQuery
    2. 13.2 New features in Rails 3.1
      1. 13.2.1 Asset pipeline
      2. 13.2.2 Reversible migrations
      3. 13.2.3 Sass and CoffeeScript
    3. 13.3 Exercises

Foreword

My former company (CD Baby) was one of the first to loudly switch to Ruby on Rails, and then even more loudly switch back to PHP (Google me to read about the drama). This book by Michael Hartl came so highly recommended that I had to try it, and Ruby on Rails Tutorial is what I used to switch back to Rails again.

Though I’ve worked my way through many Rails books, this is the one that finally made me “get” it. Everything is done very much “the Rails way”—a way that felt very unnatural to me before, but now after doing this book finally feels natural. This is also the only Rails book that does test-driven development the entire time, an approach highly recommended by the experts but which has never been so clearly demonstrated before. Finally, by including Git, GitHub, and Heroku in the demo examples, the author really gives you a feel for what it’s like to do a real-world project. The tutorial’s code examples are not in isolation.

The linear narrative is such a great format. Personally, I powered through Rails Tutorial in three long days, doing all the examples and challenges at the end of each chapter. Do it from start to finish, without jumping around, and you’ll get the ultimate benefit.

Enjoy!

Derek Sivers (sivers.org)
Formerly: Founder, CD Baby
Currently: Founder, Thoughts Ltd.

Acknowledgments

Ruby on Rails Tutorial owes a lot to my previous Rails book, RailsSpace, and hence to my coauthor Aurelius Prochazka. I’d like to thank Aure both for the work he did on that book and for his support of this one. I’d also like to thank Debra Williams Cauley, my editor on both RailsSpace and Rails Tutorial; as long as she keeps taking me to baseball games, I’ll keep writing books for her.

I’d like to acknowledge a long list of Rubyists who have taught and inspired me over the years: David Heinemeier Hansson, Yehuda Katz, Carl Lerche, Jeremy Kemper, Xavier Noria, Ryan Bates, Geoffrey Grosenbach, Peter Cooper, Matt Aimonetti, Gregg Pollack, Wayne E. Seguin, Amy Hoy, Dave Chelimsky, Pat Maddox, Tom Preston-Werner, Chris Wanstrath, Chad Fowler, Josh Susser, Obie Fernandez, Ian McFarland, Steven Bristol, Wolfram Arnold, Alex Chaffee, Giles Bowkett, Evan Dorn, Long Nguyen, James Lindenbaum, Adam Wiggins, Tikhon Bernstam, Ron Evans, Wyatt Greene, Miles Forrest, the good people at Pivotal Labs, the Heroku gang, the thoughtbot guys, and the GitHub crew. Finally, many, many readers—far too many to list—have contributed a huge number of bug reports and suggestions during the writing of this book, and I gratefully acknowledge their help in making it as good as it can be.

About the author

Michael Hartl is a programmer, educator, and entrepreneur. Michael was coauthor of RailsSpace, a best-selling Rails tutorial book published in 2007, and was cofounder and lead developer of Insoshi, a popular social networking platform in Ruby on Rails. Previously, he taught theoretical and computational physics at the California Institute of Technology (Caltech), where he received the Lifetime Achievement Award for Excellence in Teaching. Michael is a graduate of Harvard College, has a Ph.D. in Physics from Caltech, and is an alumnus of the Y Combinator entrepreneur program.

Copyright and license

Ruby on Rails Tutorial: Learn Rails by Example. Copyright © 2010 by Michael Hartl. All source code in Ruby on Rails Tutorial is available under the MIT License and the Beerware License.

   Copyright (c) 2010 Michael Hartl

   Permission is hereby granted, free of charge, to any person
   obtaining a copy of this software and associated documentation
   files (the "Software"), to deal in the Software without
   restriction, including without limitation the rights to use,
   copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following
   conditions:

   The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   OTHER DEALINGS IN THE SOFTWARE.
/*
 * ------------------------------------------------------------
 * "THE BEERWARE LICENSE" (Revision 42):
 * Michael Hartl wrote this code. As long as you retain this 
 * notice, you can do whatever you want with this stuff. If we
 * meet someday, and you think this stuff is worth it, you can
 * buy me a beer in return.
 * ------------------------------------------------------------
 */

Chapter 7 Modeling and viewing users, part II

In Chapter 6, we created the first iteration of a User model to represent users of our application, but the job is only half-done. Virtually any website with users, including ours, needs authentication as well, but currently any user signing up for the site would only have a name and email address, with no way to verify their identity. In this chapter, we’ll add the password attribute needed for an initial user signup (Chapter 8) and for signing in with an email/password combination (Chapter 9). In the process, we’ll re-use several of the ideas from Chapter 6, including migrations and validations, and also introduce some new ideas such as virtual attributes, private methods, and Active Record callbacks.

Once we have a working password attribute, we’ll make a working action and view for showing user profiles (Section 7.3). By the end of the chapter, our user profiles will display names and profile photos (as indicated by the mockup in Figure 7.1), and they will be nicely tested with user factories.

Before moving on, let’s reset the database with rake db:reset, which will clear out any old sample users from previous sessions:

$ bundle exec rake db:reset
profile_mockup_profile_name
Figure 7.1: A mockup of the user profile made in Section 7.3(full size)

Note: Roll-your-own authentication is substantially simplified in Rails 3.1. The framework developed in this chapter works well, but a future edition of this book will refactor it to use the new has_secure_password function provided by Rails 3.1. See also the exercise in Section 13.3.

7.1 Insecure passwords

Making industrial-strength passwords requires a lot of machinery, so we’ll break the process into two main steps. In this section, we’ll make a password attribute and add validations. The resulting User model will be functionally complete but badly insecure, with the passwords stored as plain text in the database. In Section 7.2, we’ll fix this problem by encrypting the passwords before saving them, thereby protecting our site against potential attackers.

7.1.1 Password validations

Even though we have yet even to add a column for passwords to our database, we’re already going to start writing tests for them. Our initial plan is to have tests to validate the presence, length, and confirmation of passwords. This is our biggest single block of tests so far, so see if you can read it all in one go. If you get stuck, it might help to review the analogous validations from Section 6.2 or skip ahead to the application code in Listing 7.2.

In order to minimize typos in passwords, when making a user signup page in Chapter 8 we’ll adopt the common convention of requiring that users confirm their passwords. To get started, let’s review the user attributes hash last seen in Listing 6.20:

describe User do

  before(:each) do
    @attr = { :name => "Example User", :email => "user@example.com" }
  end
  .
  .
  .
end

To write tests for passwords, we’ll need to add two new attributes to the @attr hash, password and password_confirmation. As you can probably guess, the password_confirmation attribute will be used for the password confirmation step.

Let’s write tests for the presence of the password and its confirmation, together with tests confirming that the password is a valid length (restricted somewhat arbitrarily to be between 6 and 40 characters long). The results appear in Listing 7.1.

Listing 7.1. Tests for password validations.
spec/models/user_spec.rb
require 'spec_helper'

describe User do

  before(:each) do
    @attr = {
      :name => "Example User",
      :email => "user@example.com",
      :password => "foobar",
      :password_confirmation => "foobar"
    }
  end

  it "should create a new instance given valid attributes" do
    User.create!(@attr)
  end
  .
  .
  .
  describe "password validations" do

    it "should require a password" do
      User.new(@attr.merge(:password => "", :password_confirmation => "")).
        should_not be_valid
    end

    it "should require a matching password confirmation" do
      User.new(@attr.merge(:password_confirmation => "invalid")).
        should_not be_valid
    end

    it "should reject short passwords" do
      short = "a" * 5
      hash = @attr.merge(:password => short, :password_confirmation => short)
      User.new(hash).should_not be_valid
    end

    it "should reject long passwords" do
      long = "a" * 41
      hash = @attr.merge(:password => long, :password_confirmation => long)
      User.new(hash).should_not be_valid
    end
  end
end

Note in Listing 7.1 how we first collect a set of valid user attributes in @attr. If for some reason those attributes aren’t valid—as would be the case, for example, if we didn’t implement password confirmations properly—then the first test

  it "should create a new instance given valid attributes" do
    User.create!(@attr)
  end

would catch the error. The subsequent tests then check each validation in turn, using the same @attr.merge technique first introduced in Listing 6.11.

Now for the application code, which contains a trick. Actually, it contains two tricks. First, you might expect at this point that we would run a migration to add a password attribute to the User model, as we did with the name and email attributes in Listing 6.1. But this is not the case: we will store only an encrypted password in the database; for the password, we will introduce a virtual attribute (that is, an attribute not corresponding to a column in the database) using the attr_accessor method, much as we did with the original name and email attributes for the example user in Section 4.4.5. The password attribute will not ever be written to the database, but will exist only in memory for use in performing the password confirmation step (implemented below) and the encryption step (implemented in Section 7.1.2 and Section 7.2).

The second trick is that we will not introduce a password_confirmation attribute, not even a virtual one. Instead, we will use the special validation

validates :password, :confirmation => true

which will automatically create a virtual attribute called password_confirmation, while confirming that it matches the password attribute at the same time.

Thus prepared to understand the implementation, let’s take a look at the code itself (Listing 7.2).

Listing 7.2. Validations for the password attribute.
app/models/user.rb
class User < ActiveRecord::Base
  attr_accessor :password
  attr_accessible :name, :email, :password, :password_confirmation
  .
  .
  .
  # Automatically create the virtual attribute 'password_confirmation'.
  validates :password, :presence     => true,
                       :confirmation => true,
                       :length       => { :within => 6..40 }
end

As promised, we use attr_accessor :password to create a virtual password attribute (as in Section 4.4.5). Then, since we’ll be accepting passwords and password confirmations as part of the signup process in Chapter 8, we need to add the password and its confirmation to the list of accessible attributes (first mentioned in Section 6.1.2.2), which we’ve done in the line

attr_accessible :name, :email, :password, :password_confirmation

Next come the password validations. They require the presence of a :password (as in, e.g., Listing 6.7) and include :confirmation => true to reject users whose password and password confirmations don’t match. We also have a second application of length validation; in Listing 6.15 we constrained the name attribute to be 50 characters or less using the :maximum option:

validates :name,  :presence => true,
                  :length   => { :maximum => 50 }

For the password length validation, instead we’ve used the :within option, passing it the range1 6..40 to enforce the desired length constraints.

7.1.2 A password migration

At this point, you may be concerned that we’re not storing user passwords anywhere; since we’ve elected to use a virtual password, rather than storing it in the database, it exists only in memory. How can we use this password for authentication? The solution is to create a separate attribute dedicated to password storage, and our strategy will be to use the virtual password as raw material for an encrypted password, which we will store in the database upon user signup (Chapter 8) and retrieve later for use in user authentication (Chapter 9).

Let’s plan to store the encrypted password using an encrypted_password attribute in our User model. We’ll discuss the implementation details in Section 7.2, but we can get started with our encrypted password tests by noting that the encrypted password should at the least exist. We can test this using the Ruby method respond_to?, which accepts a symbol and returns true if the object responds to the given method or attribute and false otherwise:

$ rails console --sandbox
>> user = User.new
>> user.respond_to?(:password)
=> true
>> user.respond_to?(:encrypted_password)
=> false

We can test the existence of an encrypted_password attribute with the code in Listing 7.3, which uses RSpec’s respond_to helper method.

Listing 7.3. Testing for the existence of an encrypted_password attribute.
spec/models/user_spec.rb
describe User do
  .
  .
  .
  describe "password encryption" do

    before(:each) do
      @user = User.create!(@attr)
    end

    it "should have an encrypted password attribute" do
      @user.should respond_to(:encrypted_password)
    end
  end
end

Note that in the before(:each) block we create a user, rather than just calling User.new. We could actually get this test to pass using User.new, but (as we’ll see momentarily) setting the encrypted password will require that the user be saved to the database. Using create! in this first case does no harm, and putting it in before(:each) will allow us to keep all the encrypted password tests in one describe block.

To get this test to pass, we’ll need a migration to add the encrypted_password attribute to the users table:

$ rails generate migration add_password_to_users encrypted_password:string

Here the first argument is the migration name, and we’ve also supplied a second argument with the name and type of attribute we want to create. (Compare this to the original generation of the users table in Listing 6.1.) We can choose any migration name we want, but it’s convenient to end the name with _to_users, since in this case Rails can automatically construct a migration to add columns to the users table. Moreover, by including the second argument, we’ve given Rails enough information to construct the entire migration for us, as seen in Listing 7.4.

Listing 7.4. The migration to add an encrypted_password column to the users table.
db/migrate/<timestamp>_add_password_to_users.rb
class AddPasswordToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :encrypted_password, :string
  end

  def self.down
    remove_column :users, :encrypted_password
  end
end

This code uses the add_column method to add an encrypted_password column to the users table (and the complementary remove_column method to remove it when migrating down). The result is the data model shown in Figure 7.2.

user_model_password
Figure 7.2: The User model with an added (encrypted) password attribute.

Now if we run the migration and prepare the test database, the test should pass, since the User model will respond to the encrypted_password attribute. (Be sure to close any Rails consoles started in a sandbox; the sandbox locks the database and prevents the migration from going through.)

$ bundle exec rake db:migrate
$ bundle exec rake db:test:prepare

Of course, we can run the full test suite with rspec spec/, but sometimes it’s convenient to run just one RSpec example, which we can do with the -e (“example”) flag:

$ bundle exec rspec spec/models/user_spec.rb \
> -e "should have an encrypted password attribute"
.

1 example, 0 failures

7.1.3 An Active Record callback

Now that our User model has an attribute for storing the password, we need to arrange to generate and save the encrypted password when Active Record saves the user to the database. We’ll do this with a technique called a callback, which is a method that gets invoked at a particular point in the lifetime of an Active Record object. In the present case, we’ll use a before_save callback to create encrypted_password just before the user is saved.2

We start with a test for the encrypted password attribute. Since we’re deferring the implementation details—and, in particular, the method of encryption—to Section 7.2, in this section we’ll just make sure that a saved user’s encrypted_password attribute is not blank. We do this by combining the blank? method on strings (Section 4.4.2) with the RSpec convention for boolean methods (first seen in the context of valid?/be_valid in Listing 6.11), yielding the test in Listing 7.5.

Listing 7.5. Testing that the encrypted_password attribute is nonempty.
spec/models/user_spec.rb
describe User do
  .
  .
  .
  describe "password encryption" do

    before(:each) do
      @user = User.create!(@attr)
    end
    .
    .
    .
    it "should set the encrypted password" do
      @user.encrypted_password.should_not be_blank
    end
  end
end

This code verifies that encrypted_password.blank? is not true using the construction should_not be_blank.

To get this test to pass, we register a callback called encrypt_password by passing a symbol of that name to the before_save method, and then define an encrypt_password method to perform the encryption. With the before_save in place, Active Record will automatically call the corresponding method before saving the record. The result appears in Listing 7.6.

Listing 7.6. A before_save callback to create the encrypted_password attribute.
app/models/user.rb
class User < ActiveRecord::Base
  .
  .
  .
  validates :password, :presence     => true,
                       :confirmation => true,
                       :length       => { :within => 6..40 }

  before_save :encrypt_password

  private

    def encrypt_password
      self.encrypted_password = encrypt(password)
    end

    def encrypt(string)
      string # Only a temporary implementation!
    end
end

Here the encrypt_password callback delegates the actual encryption to an encrypt method; as noted in the comment, this is only a temporary implementation—as currently constructed, Listing 7.6 simply sets the encrypted to the unencrypted password, which kind of defeats the purpose. But it’s enough to get our test to pass, and we’ll make the encrypt method do some actual encryption in Section 7.2.

Before trying to understand the implementation, first note that the encryption methods appear after the private keyword; inside a Ruby class, all methods defined after private are used internally by the object and are not intended for public use.3 For an example, we can look at a User object in the console:

>> user = User.new
>> user.encrypt_password
NoMethodError: Attempt to call private method

Here Ruby raises a NoMethodError exception and issues a warning that the encrypt_password method is private.

In the present context, making the encrypt_password and encrypt methods private isn’t strictly necessary, but it’s a good practice to make them private unless they are needed for the public interface.4

Now that we understand the private keyword, let’s take another look at the encrypt_password method:

def encrypt_password
  self.encrypted_password = encrypt(password)
end

This is a one-line method (the best kind!), but it contains not one but two subtleties. First, the left-hand side of the statement explicitly assigns the encrypted_password attribute using the self keyword. (Recall from Section 4.4.2 that inside the class self refers to the object itself, which for the User model is just the user.) The use of self is required in this context; if we omitted self and wrote

def encrypt_password
  encrypted_password = encrypt(password)
end

Ruby would create a local variable called encrypted_password, which isn’t what we want at all.

Second, the right-hand side of the assignment calls encrypt on password, but there is no password in sight. In the console, we would access the password attribute through a user object

>> user = User.new(:password => "foobar")
>> user.password
=> "foobar"

Inside the User class, the user object is just self, and we could write

def encrypt_password
  self.encrypted_password = encrypt(self.password)
end

in analogy with the console example, just replacing user with self. But the self is optional, so for brevity we can write simply

def encrypt_password
  self.encrypted_password = encrypt(password)
end

as in Listing 7.6 above. (Of course, as we’ve noted, the self is not optional when assigning to an attribute, so we have to write self.encrypted_password in this case.)

7.2 Secure passwords

With the code from Section 7.1, in principle we are done: although the “encrypted” password is the same as the unencrypted password, as long as we are willing to store unencrypted passwords in the database we have the necessary foundation for user login and authentication.5 Our standards in the Rails Tutorial are much loftier, though: any web developer worth his salt should know how to implement a password system with secure one-way hashing. In this section, we will build on the material from Section 7.1 to implement just such an industrial-strength password system.

7.2.1 A secure password test

As hinted at in Section 7.1.3, all of the machinery for password encryption will be tucked away in the private regions of the User model, which presents a challenge for testing it. What we need is some sort of public interface that we can expose to the rest of the application. One useful aspect of test-driven development is that, by acting as a client for our application code, the tests motivate us to design a useful interface right from the start.

Authenticating users involves comparing the encrypted version of a submitted password to the (encrypted) password of a given user. This means we need to define some method to perform the comparison, which we’ll call has_password?; this will be our public interface to the encryption machinery.6 The has_password? method will test whether a user has the same password as one submitted on a sign-in form (to be written in Chapter 9); a skeleton method for has_password? appears in Listing 7.7.

Listing 7.7. A has_password? method for users.
app/models/user.rb
class User < ActiveRecord::Base
  .
  .
  .
  before_save :encrypt_password

  # Return true if the user's password matches the submitted password.
  def has_password?(submitted_password)
    # Compare encrypted_password with the encrypted version of
    # submitted_password.
  end

  private
  .
  .
  .
end

With this method, we can write tests as in Listing 7.8, which uses the RSpec methods be_true and be_false to test that has_password? returns true or false in the proper cases.

Listing 7.8. Tests for the has_password? method.
spec/models/user_spec.rb
describe User do
  .
  .
  .
  describe "password encryption" do

    before(:each) do
      @user = User.create!(@attr)
    end
    .
    .
    .
    describe "has_password? method" do

      it "should be true if the passwords match" do
        @user.has_password?(@attr[:password]).should be_true
      end    

      it "should be false if the passwords don't match" do
        @user.has_password?("invalid").should be_false
      end 
    end
  end
end

In Section 7.2.3, we’ll complete the implementation of has_password? (and get the test to pass in the process). But first we need to learn a little more about secure passwords.

7.2.2 Some secure password theory

The basic idea of encrypted passwords is simple: rather than storing a raw password in the database (known as “cleartext”), we store a string generated using a cryptographic hash function, which is essentially irreversible, so that even an attacker in possession of the hashed password will be unable to infer the original. To verify that a submitted password matches the user’s password, we first encrypt the submitted string and then compare the hashes. Let’s drop into a console session to see how this works:

$ rails console
>> require 'digest'
>> def secure_hash(string)
>>   Digest::SHA2.hexdigest(string)
>> end
=> nil
>> password = "secret"
=> "secret"
>> encrypted_password = secure_hash(password)
=> "2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b"
>> submitted_password = "secret"
=> "secret"
>> encrypted_password == secure_hash(submitted_password)
=> true

Here we’ve defined a function called secure_hash that uses a cryptographic hash function called SHA2, part of the SHA family of hash functions, which we include into Ruby through the digest library.7 It’s not important to know exactly how these hash functions work; for our purposes what’s important is that they are one-way: there is no computationally tractable way to discover that

2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b

is the SHA2 hash of the string "secret".

If you think about it, though, we still have a problem: if an attacker ever got hold of the hashed passwords, he would still have a chance at discovering the originals. For example, he could guess that we used SHA2, and so write a program to compare a given hash to the hashed values of potential passwords:

>> hash = "2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b"
>> secure_hash("secede") == hash
=> false
>> secure_hash("second") == hash
=> false
>> secure_hash("secret") == hash
=> true

So our attacker has a match—bad news for any users with password "secret".

To make such a password-guessing attack more difficult, we can use a salt, which is a different unique string for each user. One common way to (nearly) ensure uniqueness is to hash the current time (in UTC to be time zone–independent) along with the password, so that two users will have the same salt only if they are created at exactly the same time and have the same password. Let’s see how this works using the secure_hash function defined in the console above:

>> Time.now.utc
=> Fri Jan 29 18:11:27 UTC 2010
>> password = "secret"
=> "secret"
>> salt = secure_hash("#{Time.now.utc}--#{password}")
=> "d1a3eb8c9aab32ec19cfda810d2ab351873b5dca4e16e7f57b3c1932113314c8"
>> encrypted_password = secure_hash("#{salt}--#{password}")
=> "69a98a49b7fd103058639be84fb88c19c998c8ad3639cfc5deb458018561c847"

In the last line, we’ve hashed the salt with the password, yielding an encrypted password that is virtually impossible to crack. (For clarity, arguments to hashing functions are often separated with --.)

7.2.3 Implementing has_password?

Having finished with the theory, we’re now ready for the implementation. Let’s look ahead a little to see where we’re going. Each user object knows its own encrypted password, so to check for a match with a submitted password we can define has_password? as follows:

def has_password?(submitted_password)
  encrypted_password == encrypt(submitted_password)
end

As long as we encrypt the submitted password using the same salt used to encrypt the original password, this function will be true if and only if the submitted password matches.

Since comparing a user password with a submitted password will involve encrypting the submitted password with the salt, we need to store the salt somewhere, so the first step is to add a salt column to the users table:

$ rails generate migration add_salt_to_users salt:string

As with the encrypted_password migration (Section 7.1.2), this migration has a name that ends in _to_users and passes a second argument containing the attribute name and type, so Rails automatically constructs the right migration (Listing 7.9).

Listing 7.9. The migration to add a salt column to the users table.
db/migrate/<timestamp>_add_salt_to_users.rb
class AddSaltToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :salt, :string
  end

  def self.down
    remove_column :users, :salt
  end
end

Then we migrate the database and prepare the test database as usual:

$ bundle exec rake db:migrate
$ bundle exec rake db:test:prepare

The result is a database with the data model shown in Figure 7.3.

user_model_salt
Figure 7.3: The User model with an added salt.

Finally, we’re ready for the full implementation. When last we saw the encrypt function (Listing 7.6), it did nothing, simply returning the string in its argument. With the ideas from Section 7.2.2, we’re now in a position to use a secure hash instead (Listing 7.10).8

Listing 7.10. The has_password? method with secure encryption.
app/models/user.rb
require 'digest'
class User < ActiveRecord::Base
  .
  .
  .
  before_save :encrypt_password

  def has_password?(submitted_password)
    encrypted_password == encrypt(submitted_password)
  end

  private

    def encrypt_password
      self.salt = make_salt unless has_password?(password)
      self.encrypted_password = encrypt(password)
    end

    def encrypt(string)
      secure_hash("#{salt}--#{string}")
    end

    def make_salt
      secure_hash("#{Time.now.utc}--#{password}")
    end

    def secure_hash(string)
      Digest::SHA2.hexdigest(string)
    end
end

This code contains the same two subtleties mentioned in Section 7.1.3, namely, the assignment to an Active Record attribute in the line

self.salt = make_salt unless has_password?(password)

and the omission of the self keyword in the encrypt method:

def encrypt(string)
  secure_hash("#{salt}--#{string}")
end

Since we’re inside the User class, Ruby knows that salt refers to the user’s salt attribute.

It’s also important to note the use of the has_password? boolean. This ensures that the salt gets reset whenever the user changes his password. (This subtlety doesn’t matter now, but it will when we implement a “remember me” signin feature in Section 9.3.2.)

At this point, the tests from Listing 7.8 should pass:

$ bundle exec rspec spec/models/user_spec.rb \
> -e "should be true if the passwords match"
.

1 example, 0 failures

$ bundle exec rspec spec/models/user_spec.rb \
> -e "should be false if the passwords don't match"
.

1 example, 0 failures

We can also run all the examples in a particular describe block, but we do have to be careful to escape any special regular expression characters—in this case, the question mark ? in "has_password? method":

$ bundle exec rspec spec/models/user_spec.rb -e "has_password\? method"
Run filtered using {:full_description=>/(?-mix:has_password\? method)/}
..

2 examples, 0 failures

The backslash before the question mark ensures that RSpec’s regular expression matcher interprets the string correctly, thereby running the tests associated with the given describe block.

7.2.4 An authenticate method

Having a has_password? method for each user is nice, but by itself it isn’t very useful. We’ll end our discussion of passwords by using has_password? to write a method to authenticate a user based on an email/password combination. In Chapter 9, we’ll use this authenticate method when signing users in to our site.

We can get a hint of how this will work by using the console. First, we’ll create a user, and then retrieve that user by email address to verify that it has a given password:9

$ rails console --sandbox
>> User.create!(:name => "Michael Hartl", :email => "mhartl@example.com",
?>              :password => "foobar", :password_confirmation => "foobar")
>> user = User.find_by_email("mhartl@example.com")
>> user.has_password?("foobar")
=> true

Using these ideas, let’s write a method that will return an authenticated user on password match, and nil otherwise. We should be able to use the resulting authenticate class method as follows:

User.authenticate(email, submitted_password)

We start with the tests, which we’ll use to specify the behavior we expect from User.authenticate. There are three cases to check: authenticate (1) should return nil when the email/password combination is invalid or (2) when no user exists with the given email address, and (3) should return the user object itself on success. With this information, we can write the tests for authenticate as in Listing 7.11.

Listing 7.11. Tests for the User.authenticate method.
spec/models/user_spec.rb
describe User do
  .
  .
  .
  describe "password encryption" do
    .
    .
    .
    describe "authenticate method" do

      it "should return nil on email/password mismatch" do
        wrong_password_user = User.authenticate(@attr[:email], "wrongpass")
        wrong_password_user.should be_nil
      end

      it "should return nil for an email address with no user" do
        nonexistent_user = User.authenticate("bar@foo.com", @attr[:password])
        nonexistent_user.should be_nil
      end

      it "should return the user on email/password match" do
        matching_user = User.authenticate(@attr[:email], @attr[:password])
        matching_user.should == @user
      end
    end
  end
end

Now we’re ready for the implementation, which will get our tests to pass and show how to define a class method as a bonus. We’ve mentioned class methods several times before, most recently in Section 6.1.1; a class method is simply a method attached to a class, rather than an instance of that class. For example, new, find, and find_by_email are all class methods on the User class. Outside of the class, they are invoked using the class name, as in User.find, but inside the class we can omit the class name.

The way to define a class method is to use the self keyword in the method definition. (This self is not the same as the self shown in Listing 7.10; see Box 7.1.) Listing 7.12 shows this construction in the context of the authenticate method. Note the call to find_by_email, in which we omit the explicit User class name since this method is already inside the User class.

Listing 7.12. The User.authenticate method.
app/models/user.rb
class User < ActiveRecord::Base
  .
  .
  .
  def has_password?(submitted_password)
    encrypted_password == encrypt(submitted_password)
  end

  def self.authenticate(email, submitted_password)
    user = find_by_email(email)
    return nil  if user.nil?
    return user if user.has_password?(submitted_password)
  end

  private
  .
  .
  .
end

There are several equivalent ways to write the authenticate method, but I find the implementation above the clearest. It handles two cases (invalid email and a successful match) with explicit return keywords, and handles the third case (password mismatch) implicitly, since in that case we reach the end of the method, which automatically returns nil. See Section 7.5 for some of the other possible ways to implement this method.

7.3 Better user views

Now that User model is effectively complete,10 we are in a position to add a sample user to the development database and make a show page to show some of that user’s information. Along the way, we’ll add some tests to the Users controller spec started in Section 5.3.1.

Before continuing, it’s helpful to see where we left off by recalling what the Users controller spec looks like right now (Listing 7.13). Our tests for the user show page will follow this example, but we’ll find that, unlike the tests for the new action, the tests for the show action will require the use of an instance of the User model. We’ll meet this challenge using a technique called factories.

Listing 7.13. The current Users controller spec.
spec/controllers/users_controller_spec.rb
require 'spec_helper'

describe UsersController do
  render_views

  describe "GET 'new'" do

    it "should be successful" do
      get 'new'
      response.should be_success
    end

    it "should have the right title" do
      get 'new'
      response.should have_selector("title", :content => "Sign up")
    end
  end
end

7.3.1 Testing the user show page (with factories)

Tests for the Users controller will need instances of User model objects, preferably with pre-defined values. For example, as seen in Listing 7.14, the Users controller show action needs an instance of the User class, so the tests for this action will require that we create an @user variable somehow. We’ll accomplish this goal with a user factory, which is a convenient way to define a user object and insert it into our test database.11

Listing 7.14. The user show action from Listing 6.25.
app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end
  .
  .
  .
end

We’ll be using the factories generated by Factory Girl,12 a Ruby gem produced by the good people at thoughtbot. As with other Ruby gems, we can install it by adding a line to the Gemfile used by Bundler (Listing 7.15). (Since Factory Girl is only needed in the tests, we’ve included it in the :test group.)

Listing 7.15. Adding Factory Girl to the Gemfile.
source 'http://rubygems.org'
.
.
.
group :test do
  .
  .
  .
  gem 'factory_girl_rails', '1.0'
end

Then install as usual:

$ bundle install

Now we’re ready to create the file spec/factories.rb and define a User factory, as shown in Listing 7.16. By putting the factories.rb file in the spec/ directory, we arrange for RSpec to load our factories automatically whenever the tests run.

Listing 7.16. A factory to simulate User model objects.
spec/factories.rb
# By using the symbol ':user', we get Factory Girl to simulate the User model.
Factory.define :user do |user|
  user.name                  "Michael Hartl"
  user.email                 "mhartl@example.com"
  user.password              "foobar"
  user.password_confirmation "foobar"
end

With the definition in Listing 7.16, we can create a User factory in the tests like this:

@user = Factory(:user)

As noted in the comment in the first line of Listing 7.16, by using the symbol :user we ensure that Factory Girl will guess that we want to use the User model, so in this case @user will simulate an instance of User.

To use our new User factory in the Users controller spec, we’ll create an @user variable in a before(:each) block and then get the show page and verify success (just as we did with the new page in Listing 7.13), while also verifying that the show action pulls the correct user out of the database. The result appears in Listing 7.17. (If you’re using Spork, you might have to restart it to get these tests to pass.)

Listing 7.17. A test for getting the user show page, with a user factory.
spec/controllers/users_controller_spec.rb
require 'spec_helper'

describe UsersController do
  render_views

  describe "GET 'show'" do

    before(:each) do
      @user = Factory(:user)
    end

    it "should be successful" do
      get :show, :id => @user
      response.should be_success
    end

    it "should find the right user" do
      get :show, :id => @user
      assigns(:user).should == @user
    end
  end
  .
  .
  .
end

Apart from the first use of a factory, the real novelty here is the use of a assigns(:user), which is a facility provided by RSpec (via the underlying Test::Unit library). The assigns method takes in a symbol argument and returns the value of the corresponding instance variable in the controller action. In other words, in Listing 7.17 the code

assigns(:user)

returns the value of the instance variable

@user

in the show action of the Users controller. The test

assigns(:user).should == @user

then verifies that the variable retrieved from the database in the action corresponds to the @user instance created by Factory Girl. It’s worth noting that not all Rails programmers use assigns in this context, preferring instead to use a technique called stubbing (Box 7.2).

There are two other details in Listing 7.17 worth noting. First, in the call to get, the test uses the symbol :show instead of the string ’show’, which is different from the convention in the other tests (for example, in Listing 3.11 we wrote get ’home’). Both

get :show

and

get 'show'

do the same thing, but when testing the canonical REST actions (Table 6.2) I prefer to use symbols, which for some reason feel more natural in this context.13 Second, note that the value of the hash key :id, instead of being the user’s id attribute @user.id, is the user object itself:

get :show, :id => @user

We could use the code

get :show, :id => @user.id

to accomplish the same thing, but in this context Rails automatically converts the user object to the corresponding id.14 Using the more succinct construction

get :show, :id => @user

is a very common Rails idiom.

Because of the code we added in Listing 6.25, the test in this section already passes. If you’re feeling paranoid, you can comment out the line

@user = User.find(params[:id])

and verify that the test fails, then uncomment it to get it to pass. (We went through this same process once before, in Section 6.2.1.)

7.3.2 A name and a Gravatar

In this section, we’ll improve the look of the user show page by adding a heading with the user’s name and profile image. This is one of those situations where I can go either way on test-driven development, and often when making views I’ll experiment with the HTML before bothering with tests. Let’s stick with the TDD theme for now, and test for a top-level heading (h1 tag) containing the user’s name and an img tag of class gravatar. (We’ll talk momentarily about what this second part means.)

To view a working user show page in a browser, we’ll need to create a sample user in the development database. To do this, start the console (not in a sandbox this time) and create the user:

$ bundle exec rake db:reset
$ rails console
>> User.create!(:name => "Example User", :email => "user@example.com",
?>              :password => "foobar", :password_confirmation => "foobar")

The tests in this section are similar to the tests for the new page seen in Listing 5.26. In particular, we use the have_selector method to check the title and the content of the h1 tag, as seen in Listing 7.18.

Listing 7.18. Tests for the user show page.
spec/controllers/users_controller_spec.rb
require 'spec_helper'

describe UsersController do
  render_views

  describe "GET 'show'" do
    .
    .
    .
    it "should have the right title" do
      get :show, :id => @user
      response.should have_selector("title", :content => @user.name)
    end

    it "should include the user's name" do
      get :show, :id => @user
      response.should have_selector("h1", :content => @user.name)
    end

    it "should have a profile image" do
      get :show, :id => @user
      response.should have_selector("h1>img", :class => "gravatar")
    end
  end
  .
  .
  .
end

Here RSpec’s have_selector method verifies the presence of a title and h1 tags containing the user’s name. The third example introduces a new element through the code h1>img, which makes sure that the img tag is inside the h1 tag.15 In addition, we see that have_selector can take a :class option to test the CSS class of the element in question.

We can get the first test to pass by setting the @title variable for use in the title helper (Section 4.1.1), in this case setting it to the user’s name (Listing 7.19).

Listing 7.19. A title for the user show page.
app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
    @title = @user.name
  end
  .
  .
  .
end

This code introduces a potential problem: a user could enter a name with malicious code—called a cross-site scripting attack—which would be injected into our application by the title helper defined in Listing 4.2. Before Rails 3, the solution was to escape potentially problematic code using the h method (short for html_escape), but as of Rails 3.0 all Embedded Ruby text is escaped by default.16 For example, if a user tried to inject a malicious JavaScript program by using <script> in his name, the automatic HTML escaping would convert it to &lt;script&gt;, rendering it completely harmless.

Now for the other tests. Creating an h1 with the (auto-escaped) user name is easy (Listing 7.20).

Listing 7.20. The user show view with the user’s name.
app/views/users/show.html.erb
<h1>
  <%= @user.name %>
</h1>

Getting the img test to pass is trickier. The first step is to install the gravatar_image_tag gem to handle each user’s Gravatar,17 which is a “globally recognized avatar”.18 As usual, we will include the gem dependency in the Gemfile (Listing 7.21).

Listing 7.21. Adding a Gravatar gem to the Gemfile.
source 'http://rubygems.org'

gem 'rails', '3.0.12'
gem 'sqlite3', '1.3.3'
gem 'gravatar_image_tag', '1.0.0.pre2'
.
.
.

The install it with bundle:

$ bundle install

You should also restart your web server at this point to load the new Gravatar gem properly.

Gravatars are a convenient way to include user profile images without going through the trouble of managing image upload, cropping, and storage.19 Each Gravatar is associated with an email address, so the Gravatar gem comes with a helper method called gravatar_image_tag that takes an email address as an argument:

<%= gravatar_image_tag 'example@railstutorial.org' %>

For the moment, we’ll use this directly in our user show view, as seen in Listing 7.22. (We’ll make a helper method for it in a moment.) The result appears in Figure 7.4, which shows our example user with the default Gravatar image.

Listing 7.22. The user show view with name and Gravatar.
app/views/users/show.html.erb
<h1>
  <%= gravatar_image_tag @user.email %>
  <%= @user.name %>
</h1>
user_show_default_gravatar_rails_3
Figure 7.4: The initial user show page /users/1 with the default Gravatar. (full size)

This Gravatar business might seem like magic, so let’s fire up the console to get a little more insight into what’s going on:

$ rails console
>> user = User.first
>> user.update_attributes(:email => "example@railstutorial.org",
?>                        :password => "foobar",
?>                        :password_confirmation => "foobar")
=> true

Note that we can pull out the first (and, at this point, only) user in the database with the handy User.first method. In the update_attributes step we’ve reassigned the user’s email address, changing it to example@railstutorial.org. As you can see from Figure 7.5, this change results in a new Gravatar being displayed: the Rails Tutorial logo. What’s going on is that Gravatar works by associating images with email addresses; since user@example.com is an invalid email address (the example.com domain is reserved for examples), there is no Gravatar for that email address. But at my Gravatar account I’ve associated the address example@railstutorial.org with the Rails Tutorial logo, so when updating the example user with that email address the Gravatar changes automatically.

user_show_railstutorial_gravatar_rails_3
Figure 7.5: The user show page /users/1 with the Rails Tutorial Gravatar. (full size)

A Gravatar helper

At this point, the Gravatar displays properly, but the final example from Listing 7.18 still doesn’t pass. This is because the "gravatar" class, which we want for styling the Gravatar with CSS, isn’t yet present in the Gravatar’s img tag. We could arrange for the test to pass by including an option to the gravatar_image_tag method:

<%= gravatar_image_tag @user.email, :class => "gravatar" %>

On the other hand, since we expect the Gravatars to appear in multiple places in our application, it would be repetitive to put the class in everywhere by hand. It would be better to make a helper method to eliminate this duplication preemptively.

This situation may remind you of the repetition in the site’s base title (“Ruby on Rails Tutorial Sample App”), which we solved with a title helper in the Application helper (Listing 4.2). The solution here is similar; since Gravatars are naturally associated with users, we’ll define a gravatar_for method in the Users helper. (The choice to use the Users helpers instead of the Application helper is just for conceptual convenience; Rails makes all helpers available in all views.) The result will be concise view code like

<%= gravatar_for @user %>

The gravatar_for helper should take in a user object and then pass some default options to the gravatar_image_tag helper. The implementation appears in Listing 7.23.

Listing 7.23. Defining a gravatar_for helper method.
app/helpers/users_helper.rb
module UsersHelper

  def gravatar_for(user, options = { :size => 50 })
    gravatar_image_tag(user.email.downcase, :alt => h(user.name),
                                            :class => 'gravatar',
                                            :gravatar => options)
  end
end

The first argument in the call to gravatar_image_tag passes in the lower-case version of the user’s email address (using the downcase method).20 Then the first option to gravatar_image_tag uses the h method to assign an HTML escaped version of the user’s name to the img tag’s alt attribute, which gets displayed in case the image can’t be rendered. (Note that, although inserted text is escaped by default in Rails 3, this doesn’t apply to alt attributes, which must be escaped explicitly.) The second option sets the CSS class of the resulting Gravatar. The third option passes the options hash using the :gravatar key, which (according to the gravatar_image_tag gem documentation) is how to set the options for gravatar_image_tag. Note that the function definition sets a default option21 for the size of the Gravatar22 using

option = { :size => 50 }

This sets the default Gravatar size to 50x50, while also allowing us to override the default size using code like

<%= gravatar_for @user, :size => 30 %>

If we now update the user show template with the code in Listing 7.24, the user show page appears as in Figure 7.6. And since the gravatar_for helper assigns the img tag the class "gravatar", the tests from Listing 7.18 should now pass.

Listing 7.24. Updating the user show template to use gravatar_for.
app/views/users/show.html.erb
<h1>
  <%= gravatar_for @user %>
  <%= @user.name %>
</h1>
user_show_gravatar_for
Figure 7.6: The user show page with gravatar_for(full size)

7.3.3 A user sidebar

Even though our tests are now passing, and the user show page is much-improved, it’s still nice to polish it up just a bit more. In Listing 7.25, we have a table tag with one table row (tr) and two table data cells (td).23

Listing 7.25. Adding a sidebar to the user show view.
app/views/users/show.html.erb
<table class="profile" summary="Profile information">
  <tr>
    <td class="main">
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
    </td>
    <td class="sidebar round">
      <strong>Name</strong> <%= @user.name %><br />
      <strong>URL</strong>  <%= link_to user_path(@user), @user %>
    </td>
  </tr>
</table>

Here we’ve used an HTML break tag <br /> to put a break between the user’s name and URL. Also note the use of user_path to make a clickable link so that users can easily share their profile URLs. This is only the first of many named routes (Section 5.2.2) associated with the User resource (Listing 6.26); we’ll see many more in the next few chapters. The code

user_path(@user)

returns the path to the user, in this case /users/1. The related code

user_url(@user)

just returns the entire URL, http://localhost:3000/users/1. (Compare to the routes created in Section 5.2.2.) Both are examples of the named routes created by the users resource in Listing 6.26; a list of all the named routes appears in Table 7.1.

Named routePath
users_path/users
user_path(@user)/users/1
new_user_path/users/new
edit_user_path(@user)/users/1/edit
users_urlhttp://localhost:3000/users
user_url(@user)http://localhost:3000/users/1
new_user_urlhttp://localhost:3000/users/new
edit_user_url(@user)http://localhost:3000/users/1/edit
Table 7.1: Named routes provided by the users resource in Listing 6.26.

Note that in

<%= link_to user_path(@user), @user %>

user_path(@user) is the link text, while the address is just @user. In the context of a link_to, Rails converts @user to the appropriate URL; in other words, the code above is equivalent to the code

<%= link_to user_path(@user), user_path(@user) %>

Either way works fine, but, as in the :id => @user idiom from Listing 7.17, using just @user is a common Rails convention. In both cases, the Embedded Ruby produces the HTML

<a href="/users/1">/users/1</a>

With the HTML elements and CSS classes in place, we can style the show page with the CSS shown in Listing 7.26. The resulting page is shown in Figure 7.7.

Listing 7.26. CSS for styling the user show page, including the sidebar.
public/stylesheets/custom.css
.
.
.
/* User show page */

table.profile {
  width: 100%;
  margin-bottom: 0;
}

td.main {
  width: 70%;
  padding: 1em;
}

td.sidebar {
  width: 30%;
  padding: 1em;
  vertical-align: top;
  background: #ffc;
}

.profile img.gravatar {
  border: 1px solid #999;
  margin-bottom: -15px;
}
user_show_sidebar_css
Figure 7.7: The user show page /users/1 with a sidebar and CSS. (full size)

7.4 Conclusion

In this chapter, we’ve effectively finished the User model, so we’re now fully prepared to sign up new users and to let them sign in securely with an email/password combination. Moreover, we have a nice first cut of the user profile page, so after signing in users will have a place to go.

7.4.1 Git commit

Before moving on, we should close the Git loop opened in the introduction to Chapter 6 by making a final commit to the modeling-users branch and then merging into master.24 First, verify that we are on the modeling-users branch:

$ git branch
  master
* modeling-users

As noted in Section 1.3.5.1, the asterisk here identifies the present branch, so we are indeed ready to commit and merge:25

$ git add .
$ git commit -m "User model with passwords, and user show page"
$ git checkout master
$ git merge modeling-users

7.4.2 Heroku deploy

If you’ve deployed your sample application to Heroku, you can push it up at this point:

$ git push heroku

Then migrate the database on the remote server using the heroku command:

$ heroku rake db:migrate

Now if you want to create a sample user on Heroku, you can use the Heroku console:

$ heroku console
>> User.create!(:name => "Example User", :email => "user@example.com",
?>              :password => "foobar", :password_confirmation => "foobar")

7.5 Exercises

  1. Copy each of the variants of the authenticate method from Listing 7.27 through Listing 7.31 into your User model, and verify that they are correct by running your test suite.
  2. The final authenticate example (Listing 7.31) is particularly challenging. Experiment with the console to see if you can understand how it works.
  3. How could you get the Gravatar helper gravatar_for to work if our User model used email_address instead of email to represent email addresses?
Listing 7.27. The authenticate method with User in place of self.
  def User.authenticate(email, submitted_password)
    user = find_by_email(email)
    return nil  if user.nil?
    return user if user.has_password?(submitted_password)
  end
Listing 7.28. The authenticate method with an explicit third return.
  def self.authenticate(email, submitted_password)
    user = find_by_email(email)
    return nil  if user.nil?
    return user if user.has_password?(submitted_password)
    return nil
  end
Listing 7.29. The authenticate method using an if statement.
  def self.authenticate(email, submitted_password)
    user = find_by_email(email)
    if user.nil?
      nil
    elsif user.has_password?(submitted_password)
      user
    else
      nil
    end
  end
Listing 7.30. The authenticate method using an if statement and an implicit return.
  def self.authenticate(email, submitted_password)
    user = find_by_email(email)
    if user.nil?
      nil
    elsif user.has_password?(submitted_password)
      user
    end
  end
Listing 7.31. The authenticate method using the ternary operator.
  def self.authenticate(email, submitted_password)
    user = find_by_email(email)
    user && user.has_password?(submitted_password) ? user : nil
  end
  1. We saw ranges before in Section 4.3.1
  2. For more details on the kind of callbacks supported by Active Record, see the discussion of callbacks at the Rails Guides
  3. The extra level of indentation is a typographical reminder that we’re in a private section; otherwise, it’s easy to miss the private keyword and be confused when trying to access a private method that you think is public. I thought the extra indentation was a stupid convention until I burned an hour on just this problem a couple years back. Now I add the extra indentation… 
  4. Ruby has a closely related keyword called protected that differs subtly from private. As far as I can tell, the only reason to learn the difference is so that you can ace a job interview that asks you “In Ruby, what is the difference between private and protected?” But do you really want to work at a company that asks you such a lame interview question? At his keynote at RubyConf in 2008, Dave Thomas (author of Programming Ruby) suggested eliminating protected from future versions of Ruby, and I agree with the sentiment. Just use private and you’ll be fine. 
  5. I am ashamed to admit that this is how we implemented passwords in RailsSpace. Consider this section my penance. 
  6. The alert reader may notice that none of what we do in this section requires encryption, but, once we develop some of the theory of secure passwords and write a basic implementation (Section 7.2.2), the only way for the has_password? method to work properly is for all the encryption machinery to work properly as well. 
  7. In my setup, the require ’digest’ line is unnecessary, but several readers have reported getting a NameError exception if they don’t include it explicitly. It does no harm in any case, so I’ve included the explicit require just to be safe. 
  8. As noted in Section 7.2.2, the explicit require ’digest’ line is unnecessary on some systems, but it does no harm to include it. 
  9. Recall from Box 6.2 that the index on the email column ensures that this retrieval is efficient. 
  10. We’ll plan to add a couple more attributes (one to identify administrative users and one to allow a “remember me” feature), but they are not strictly necessary. All the essential user attributes have now been defined. 
  11. Many experienced Rails programmers find that this factory approach is much more flexible than fixtures, which Rails uses by default but can be brittle and difficult to maintain. 
  12. Presumably “Factory Girl” is a reference to the movie of the same name
  13. I used get ’new’ in Listing 5.24 and subsequent tests for the new action because at that point we had yet to encounter the idea of standard REST actions. I’ll switch to get :new in future tests. 
  14. It does this by calling the to_param method on the @user variable. 
  15. It’s not necessarily always a good idea to make HTML tests this specific, since we don’t always want to constrain the HTML layout this tightly. Feel free to experiment and find the right level of detail for your projects and tastes. 
  16. Instead, if you want unescaped text you have to use the raw method, as in <%= raw @title %>
  17. Gravatar was originally created by Tom Preston-Werner, cofounder of GitHub, and was acquired and scaled by Automattic (best known as the makers of WordPress). 
  18. In Hinduism, an avatar is the manifestation of a deity in human or animal form. By extension, the term avatar is commonly used to mean some kind of personal representation, especially in a virtual environment. But you’ve seen the movie by now, so you already knew this. 
  19. If your application does need to handle images or other file uploads, Paperclip is the way to go. Like Factory Girl, Paperclip is brought to you by thoughtbot. (Though I do know several people there, I have no vested interest in promoting thoughtbot; they just make good software.) 
  20. Thanks to the anonymous reader who noted that the Gravatar plugin is case-sensitive in this context. 
  21. There’s actually a way to reset the default size in a configuration file, but I find this way clearer. 
  22. Gravatars are square, so a single parameter determines their size uniquely. 
  23. If anyone gives you grief for using, horror of horrors, tables for layout, have them point their Firebug inspector at Twitter’s profile sidebar and tell you what they see. In fact, you’ll find that, while “semantic markup” using divs and spans is increasingly common, virtually all sites resort to tables for layout on occasion. In the present case, getting the vertical alignment just right is much easier with tables. 
  24. Ordinarily, I recommend making more frequent, smaller commits, but frequent Git commits throughout the tutorial would be hard to maintain and would break up the flow of the discussion. 
  25. If you’re not on the right branch, run git checkout modeling-users before proceeding. 
Michael Hartl is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to Amazon.com.