RailsApps Tutorials

Devise with RSpec and Cucumber

Introduction

Ruby on Rails tutorial showing how to create a Rails 3.2 application using Devise with RSpec and Cucumber.

  • Devise provides ready-made authentication and user management.
  • RSpec is a popular framework for unit testing.
  • Cucumber is used for integration testing and behavior driven development.

The combination of Devise, RSpec, and Cucumber is the foundation for many real-world Rails applications. Devise provides features for signing up a new user and creating an account, as well as requiring a visitor to log in before allowing access to the application. Unit tests built with RSpec confirm that small, discrete portions of the application continue working as developers add features and refactor code. Integration tests built with Cucumber determine whether the application’s features work as expected, testing the application from the point of view of the user.

Is It for You?

This tutorial is for experienced Rails developers as well as startup founders or hobbyist coders who are new to Rails.

Experienced developers will find the complete application on GitHub; this tutorial provides the detail and background to understand the implementation in depth.

For Rails beginners, this tutorial describes each step that you must follow to create the application. Every step is documented concisely, so you can create this application without any additional knowledge. However, the tutorial assumes you’ve already been introduced to Rails, so if you are a beginner, you may be overwhelmed unless you’ve been introduced to Rails elsewhere. If you’re new to Rails, see recommendations for a Rails tutorial and resources for getting started with Rails.

This is one in a series of Rails example apps and tutorials from the RailsApps Project. See a list of similar Rails examples, tutorials, and starter apps.

This example application uses ActiveRecord and a SQLite database. You can use the Mongoid ORM with the MongoDB datastore instead, for faster development without schemas or migrations. The rails3-mongoid-devise example app and tutorial shows how to set up Devise and Mongoid with RSpec and Cucumber.

For more complex applications that use Devise, CanCan, and Twitter Bootstrap, see:

How to Support the Project

If you haven’t subscribed to the RailsApps Tutorials Pro Plan, please consider purchasing a monthly subscription to support the project.

The code for the example applications is open source and unrestricted; your purchase of a subscription plan supports maintenance of the project. Rails applications need frequent updates as Rails and its gems change. Subscription sales support the project so we can keep the applications current so you’ll always have an up-to-date reference implementation.

Before You Start

Most of the tutorials from the RailsApps project take about an hour to complete.

If you follow this tutorial closely, you’ll have a working application that closely matches the example app in this GitHub repository. The example app in the rails3-devise-rspec-cucumber repository is your reference implementation. If you find problems with the app you build from this tutorial, download the example app (in Git speak, clone it) and use a file compare tool to identify differences that may be causing errors. On a Mac, good file compare tools are FileMerge, DiffMerge, Kaleidoscope, or Ian Baird’s Changes.

If you find problems or wish to suggest improvements, please create a GitHub issue. It’s best to clone and check the example application from the GitHub repository before you report an issue, just to make sure the error isn’t a result of your own mistake.

The online edition of this tutorial contains a comments section at the end of the tutorial. I encourage you to offer feedback to improve this tutorial.

Assumptions

Before beginning this tutorial, you need to install

  • The Ruby language (version 1.9.3)
  • Rails 3.2

Check that appropriate versions of Ruby and Rails are installed in your development environment:

$ ruby -v
$ rails -v

Be sure to read Installing Rails to make sure your development environment is set up properly.

I recommend using rvm, the Ruby Version Manager to manage your Rails versions and create a dedicated gemset for each application you build.

Create the Application

You have several options for getting the code. You can copy from the tutorial, fork, clone, or generate.

Copy from the Tutorial

To create the application, you can cut and paste the code from the tutorial into your own files. It’s a bit tedious and error-prone but you’ll have a good opportunity to examine the code closely.

Other Options

Fork

If you’d like to add features (or bug fixes) to improve the example application, you can fork the GitHub repo and make pull requests. Your code contributions are welcome!

Clone

If you want to copy and customize the app with changes that are only useful for your own project, you can download or clone the GitHub repo. You’ll need to search-and-replace the project name throughout the application. You probably should generate the app instead (see below). To clone:

$ git clone git://github.com/RailsApps/rails3-devise-rspec-cucumber.git

You’ll need git on your machine. See Rails and Git.

Generate

If you wish to skip the tutorial and build the application immediately, use the Rails Composer tool to generate the complete example app. You’ll be able to give it your own project name when you generate the app. Generating the application gives you additional options.

To build the complete example application immediately, see the instructions in the README for the rails3-devise-rspec-cucumber example application.

Building from Scratch

Beginning here, we show how to create the application from scratch.

Your tools are a terminal application and text editor.

It’s a good idea to create a new gemset using rvm, the Ruby Version Manager, as described in the article Installing Rails. If you are using rvm to manage gemsets, create a new gemset and install Rails:

$ rvm use ruby-2.0.0@rails3-devise-rspec-cucumber --create
$ gem install rails

The first step will create and make active a new gemset named rails3-devise-rspec-cucumber. You can give the gemset any name but it’ll be easier to recognize if you give it the same name as the application you are going to build. The second step installs the newest stable Rails version.

Next, create a new Rails project. Navigate to a folder where you have rights to create files, and type:

$ rails new rails3-devise-rspec-cucumber -T

Use the -T flag to skip Test::Unit files (since you will be using RSpec).

You may give the app a different name if you are building it for your own use. For this tutorial, we’ll assume the name is “rails3-devise-rspec-cucumber.”

This will create a Rails application that uses a SQLite database for data storage.

After you create the application, switch to its folder to continue work directly in the application root directory:

$ cd rails3-devise-rspec-cucumber

If you are using rvm to manage gemsets, create .ruby-version and .ruby-gemset files which will automatically select the correct Ruby version and gemset when you enter the project directory:

$ echo "ruby-2.0.0" > .ruby-version
$ echo "rails3-devise-rspec-cucumber" > .ruby-gemset

In the future, when you cd into the application root directory, rvm will activate the project-specific gemfile.

Replace the READMEs

Please edit the README files to add a description of the app and your contact info. Changing the README is important if your app will be publicly visible on GitHub. Otherwise, people will think I am the author of your app. If you like, add an acknowledgment and a link to the RailsApps project.

Set Up Source Control (Git)

If you’re creating an app for deployment into production, you’ll want to set up a source control repository at this point. If you are building a throw-away app for your own education, you may skip this step.

$ git init .
$ git add -A
$ git commit -m 'Initial commit'

See detailed instructions for Using Git with Rails.

If you want to store your application on GitHub, you should now set up your GitHub repository.

Set Up Gems

Open your Gemfile and replace the contents with the following:

source 'https://rubygems.org'
gem 'rails', '3.2.11'
gem 'sqlite3'
group :assets do
  gem 'sass-rails',   '~> 3.2.3'
  gem 'coffee-rails', '~> 3.2.1'
  gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
gem "rspec-rails", ">= 2.12.2", :group => [:development, :test]
gem "database_cleaner", ">= 0.9.1", :group => :test
gem "email_spec", ">= 1.4.0", :group => :test
gem "cucumber-rails", ">= 1.3.0", :group => :test, :require => false
gem "launchy", ">= 2.1.2", :group => :test
gem "capybara", ">= 2.0.2", :group => :test
gem "factory_girl_rails", ">= 4.2.0", :group => [:development, :test]
gem "devise", ">= 2.2.3"
gem "quiet_assets", ">= 1.0.1", :group => :development
gem "figaro", ">= 0.5.3"
gem "better_errors", ">= 0.3.2", :group => :development
gem "binding_of_caller", ">= 0.6.8", :group => :development

Check for the current version of Rails and replace gem 'rails', '3.2.11' accordingly.

Note: The RailsApps examples are generated with application templates created by the Rails Apps Composer Gem. For that reason, groups such as :development or :test are specified inline. You can reformat the Gemfiles to organize groups in an eye-pleasing block style. The functionality is the same.

If you want to use PostgreSQL instead of SQLite for your database, replace gem 'sqlite3' with gem 'pg'. SQLite is already running if you are using Mac OS X; if you want to use PostgreSQL, you’ll need to install and launch it.

For more information about the gems we are using in this Gemfile, see Gemfiles for Rails 3.2.

Install the Required Gems

Install the required gems on your computer:

$ bundle install

You can check which gems are installed on your computer with:

$ gem list

Keep in mind that you have installed these gems locally. When you deploy the app to another server, the same gems (and versions) must be available. If you deploy to Heroku, the Gemfile will manage that for you.

Test the App

Check that your app runs properly by entering the command:

$ rails server

You can use rails s as a shortcut.

To see your application in action, open a browser window and navigate to http://localhost:3000/. You should see the Rails default information page.

Stop the server with Control-C.

Test-Driven Development

This example application uses RSpec for unit testing and Cucumber for integration testing.

Testing is at the center of any robust software development process. Unit tests confirm that small, discrete portions of the application continue working as developers add features and refactor code. Integration tests determine whether the application’s features work as expected, testing the application from the point of view of the user.

This tutorial provides examples but does not teach the art of writing tests. This tutorial:

  • shows how to set up RSpec and provides example specs for use with Devise
  • shows how to set up Cucumber and provides example scenarios for use with Devise

To learn more about writing tests, see the books:

Use the gem rspec-rails to set up the app for RSpec.

You should have the following gems in your Gemfile:

gem 'rspec-rails', :group => [:development, :test]
gem "factory_girl_rails", :group => [:development, :test]
gem "database_cleaner", :group => :test
gem "email_spec", :group => :test

The gem rspec-rails needs to be in the :development group (as well as the :test group) to expose generators and rake tasks during development.

Install the required gems on your computer if you haven’t already done so:

$ bundle install

Generate RSpec

Use the rspec-rails generator to set up files needed for RSpec:

$ rails generate rspec:install

The rspec-rails generator creates the files:

  • .rspec
  • spec/spec_helper.rb

In you did not include the -T option when you ran rails new, you can remove the test folder (it is not needed for RSpec):

$ rm -rf test/

Database Cleaner for RSpec

To reset your application database to a pristine state during testing, you can use Database Cleaner with RSpec.

Modify the file spec/spec_helper.rb to add Database Cleaner before the final end:

.
.
.
RSpec.configure do |config|
.
.
.
  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
  end
  config.before(:each) do
    DatabaseCleaner.start
  end
  config.after(:each) do
    DatabaseCleaner.clean
  end
end

Don’t copy the dots. Leave the file intact and just add the Database Cleaner lines.

Add Factory Girl for Test Objects

The factory_girl gem is used to create default model objects for tests. For example, if a controller action requires finding a User object before displaying a “show” page, Factory Girl will create a user just for a test of the controller. You’ll need gem 'factory_girl_rails', :group => :test in your Gemfile.

We’ll create a User model later in the tutorial. For testing the User model, we’ll need a factory to create a User model object.

Create a file spec/factories/users.rb:

FactoryGirl.define do
  factory :user do
    name 'Test User'
    email 'example@example.com'
    password 'changeme'
    password_confirmation 'changeme'
    # required if the Devise Confirmable module is used
    # confirmed_at Time.now
  end
end

This tutorial uses Devise for user authentication. Devise offers an optional Confirmable module which sends an email to a new user to request confirmation of an email address when creating a new account. If you choose to add the Devise Confirmable module, remove the commenting to enable confirmed_at Time.now.

Add Devise Test Helpers

Using Devise, your controllers will often include before_filter :authenticate_user! to limit access to signed-in users. Your tests will fail unless a default user is created and logs in before each test run. Devise provides test helpers to make it simple to create and log in a default user.

Create a file spec/support/devise.rb:

RSpec.configure do |config|
  config.include Devise::TestHelpers, :type => :controller
end

Now you can write controller specs that set up a signed-in user before tests are run.

Email Spec

The email_spec gem provides a collection of RSpec matchers for testing email.

Modify the file spec/spec_helper.rb to enable the email_spec library:

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'email_spec'
require 'rspec/autorun'
.
.
.
RSpec.configure do |config|
  config.include(EmailSpec::Helpers)
  config.include(EmailSpec::Matchers)
  # ## Mock Framework
.
.
.
end

Don’t copy the dots. Leave the file intact and just add the email_spec lines.

Configure the Rails Generator

By default, using a Rails generator to create a controller will create placeholder files for Test::Unit controller tests with additional placeholder files for testing helpers and views.

We’ll modify the config/application.rb configuration settings to indicate we are using RSpec.

We’re using Cucumber scenarios (integration tests) so unit tests of helpers and views are redundant. We’ll tell the Rails generator to skip unit tests for helpers and views.

Rails also likes to create controller-specific helper classes, JavaScript files and stylesheet stubs. If you are like most developers, you will create these files manually as you need them, so this is a good time to tell the generator to skip the clutter.

Modify the file config/application.rb to include:

.
.
.
module Rails3DeviseRspecCucumber
  class Application < Rails::Application

  # don't generate RSpec tests for views and helpers
  config.generators do |g|
    g.test_framework :rspec, fixture: true
    g.fixture_replacement :factory_girl, dir: 'spec/factories'
    g.view_specs false
    g.helper_specs false
    g.stylesheets = false
    g.javascripts = false
    g.helper = false
  end
.
.
.

Again, don’t copy the dots.

Run RSpec

You can run rake -T as a quick “smoke test” to check that rake tasks for RSpec are available.

$ rake -T

You’ll see rake spec as well as numerous other RSpec rake tasks.

Prepare the database for testing by running the commands:

$ rake db:migrate
$ rake db:test:prepare

If you’re not using rvm, you should preface each rake command with bundle exec. You don’t need to use bundle exec if you are using rvm version 1.11.0 or newer.

This will create a db/schema.rb file so rake spec can run successfully.

You should be able to run rake spec to run all specs.

$ rake spec

If you haven’t written any specs, you’ll see the message “No examples matching … could be found”.

You can copy the files from the example spec directory to use our ready-made specs.

$ cd spec
$ mkdir controllers
$ cd controllers
$ curl -o home_controller_spec.rb https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/spec/controllers/home_controller_spec.rb
$ curl -o users_controller_spec.rb https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/spec/controllers/users_controller_spec.rb
$ cd ../
$ mkdir models
$ cd models
$ curl -o user_spec.rb https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/spec/models/user_spec.rb
$ cd ../../

Our ready-made specs provide tests for a User model, User controller, and Home controller. If you run rake spec now, you’ll see an error such as Uninitialized constant ... (NameError) because you haven’t created a User model, User controller, or Home controller.

You’ll have to complete the tutorial before rake spec will run successfully.

Add Cucumber for Behavior Driven Development

This example application uses Cucumber for integration testing.

Not all developers use Cucumber. Some developers create integration tests using Capybara in combination with RSpec as described in Ryan Bates’s How I Test Railscast.

In a broad sense, Cucumber is a tool for managing software development. If you are planning development and specifying application functionality as “user stories,” Cucumber is a good tool for turning user stories into specifications for automated acceptance testing. Cucumber is particularly appropriate when a team includes nonprogrammers who are involved in defining product requirements or there is a need for a specification and acceptance tests to be maintained independently of implementation (for example, when implementation is outsourced).

This tutorial shows how to set up Cucumber and provides example scenarios for use with Devise.

Cucumber Gems

Use the gem cucumber-rails to set up the app for Cucumber.

You should have the following gems in your Gemfile:

gem "cucumber-rails", :group => :test, :require => false
gem "capybara", :group => :test
gem "database_cleaner", :group => :test
gem "email_spec", :group => :test

The database_cleaner and email_spec gems are also used by RSpec. There’s no need to add them again if you previously added them for use with RSpec.

Install the required gems on your computer (if you haven’t already done so):

$ bundle install

Use the cucumber-rails generator to set up files needed for Cucumber:

$ rails generate cucumber:install --capybara --rspec

The -–capybara option specifies Capybara instead of the default Webrat for acceptance testing. The -–rspec option enables RSpec matchers for your step definitions.

The cucumber-rails generator creates the directories:

  • features/step_definitions
  • features/support

The cucumber-rails generator creates the files:

  • config/cucumber.yml
  • features/support/env.rb
  • lib/tasks/cucumber.rake
  • script/cucumber

Database Cleaner for Cucumber

To reset your application database to a pristine state during testing, Cucumber makes use of Database Cleaner. The file features/support/env.rb is already set up to use Database Cleaner:

begin
  DatabaseCleaner.strategy = :transaction
rescue NameError
  raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
end

Email Spec

The email_spec gem provides a collection of Cucumber steps for testing email.

Add a file features/support/email_spec.rb to enable the email_spec library:

require 'email_spec/cucumber'

Create a set of Cucumber step definitions for testing email:

$ rails generate email_spec:steps

This creates a file features/step_definitions/email_steps.rb.

Run Single Features Easily

You can run a single Cucumber feature with a command such as:

$ cucumber features/visitors/request_invitation.feature --require features

To save some typing, you can eliminate the need for --require features by changing the config/cucumber.yml file:

std_opts = "-r features/support/ -r features/step_definitions --format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"

Run Cucumber

Run rake -T to check that rake tasks for Cucumber are available.

You should be able to run rake cucumber (or more simply, cucumber) to run Cucumber scenarios and steps. If you haven’t written any Cucumber scenarios and steps, you’ll see the message “0 scenarios, 0 steps”.

You can copy the files from the example features directory to use our ready-made Cucumber feature files.

$ cd features
$ cd support
$ curl -o paths.rb https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/features/support/paths.rb
$ cd ../
$ cd step_definitions
$ curl -o user_steps.rb https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/features/step_definitions/user_steps.rb
$ cd ../
$ mkdir users
$ cd users
$ curl -o sign_in.feature https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/features/users/sign_in.feature
$ curl -o sign_out.feature https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/features/users/sign_out.feature
$ curl -o sign_up.feature https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/features/users/sign_up.feature
$ curl -o user_edit.feature https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/features/users/user_edit.feature
$ curl -o user_show.feature https://raw.github.com/RailsApps/rails3-devise-rspec-cucumber/master/features/users/user_show.feature
$ cd ../../

If you’ve chosen to create the application with the Devise Confirmable module, you should change one step in the features/step_definitions/user_steps.rb file:

Then /^I should see a successful sign up message$/ do
  page.should have_content "Welcome! You have signed up successfully."
end

should be changed so all Cucumber scenarios pass when the Devise Confirmable module is used:

Then /^I should see a successful sign up message$/ do
  page.should have_content "A message with a confirmation link has been sent to your email address."
end

You can run rake cucumber to see your failing Cucumber integraton tests.

You’ll have to complete the tutorial before all Cucumber scenarios will run successfully.

Write Cucumber Scenarios

To learn more about using Cucumber, refer to The Cucumber Book or the free introduction to Cucumber, The Secret Ninja Cucumber Scrolls.

There are two approaches to writing Cucumber scenarios. The newest (and recommended) approach uses Capybara to write the code (“steps”) that turn Cucumber scenarios into executable specifications. Older versions of Cucumber provided a web_steps.rb file that implemented common features. See the The Training Wheels Came Off by Aslak Hellesøy to understand why the web_steps.rb approach is no longer recommended.

Test the App

You can check that your app runs properly by entering the command

$ rails server

To see your application in action, open a browser window and navigate to http://localhost:3000/. You should see the Rails default information page.

Stop the server with Control-C.

Configuration

Configuration File

The application uses the figaro gem to set environment variables. See the article Rails Environment Variables for more information.

The figaro gem uses a generator to set up the necessary files. Run:

$ rails generate figaro:install

This generates a config/application.yml file and lists it in your .gitignore file.

Credentials for a user’s account are set in the config/application.yml file. The .gitignore file prevents the config/application.yml file from being saved in the git repository so your credentials are kept private.

Modify the file config/application.yml:

# Add account credentials and API keys here.
# See http://railsapps.github.io/rails-environment-variables.html
# This file should be listed in .gitignore to keep your settings secret!
# Each entry sets a local environment variable and overrides ENV variables in the Unix shell.
# For example, setting:
# GMAIL_USERNAME: Your_Gmail_Username
# makes 'Your_Gmail_Username' available as ENV["GMAIL_USERNAME"]
# Add application configuration variables here, as shown below.
#
GMAIL_USERNAME: Your_Username
GMAIL_PASSWORD: Your_Password
ADMIN_NAME: First User
ADMIN_EMAIL: user@example.com
ADMIN_PASSWORD: changeme

You can use the GMAIL_USERNAME and GMAIL_PASSWORD variables if you want to set up the application to send email.

The variables ADMIN_NAME, ADMIN_EMAIL, and ADMIN_PASSWORD are used in the db/seeds.rb file to intialize the application database. They set the name, email address, and password for the first user’s account. You can use the default to sign in to the application and edit the account after deployment. It is always a good idea to change the user’s password after the application is deployed.

All configuration values in the config/application.yml file are available anywhere in the application as environment variables. For example, ENV["GMAIL_USERNAME"] will return the string “Your_Username”.

If you prefer, you can delete the config/application.yml file and set each value as an environment variable in the Unix shell.

Configure Email

This example application doesn’t send email messages. However, if you want your application to send email messages (for example, if you plan to install the Devise :confirmable module) you must configure the application for your email account. See the article Send Email with Rails.

Layout and Stylesheets

Rails will use the layout defined in the file app/views/layouts/application.html.erb as a default for rendering any page.

You’ll want to add navigation links, include flash messages for errors and notifications, and apply CSS styling.

Twitter Bootstrap and other CSS front-end frameworks (such as Zurb Foundation) are toolkits that provide the kind of structure and convention for the front end that make Rails popular for server-side (“back-end”) development. If you want to use Twitter Bootstrap to quickly add attractive CSS styling to your application, see the article Twitter Bootstrap and Rails.

This tutorial shows code using ERB, the default Rails templating language. If you prefer to use Haml, see the detailed guide Rails Default Application Layout for HTML5.

You’ll likely need navigation links on every page of your web application. You’ll want a link for Home. You’ll want links for Login, Logout, and Sign Up.

You can add navigation links directly to your application layout file but many developers prefer to create a partial template – a “partial” – to better organize the default application layout.

Create the file app/views/layouts/_navigation.html.erb for the navigation links:

<%= link_to "Rails3 Devise Rspec Cucumber", root_path, :class => 'brand' %>
<ul class="nav">
  <% if user_signed_in? %>
    <li>
    <%= link_to 'Logout', destroy_user_session_path, :method=>'delete' %>
    </li>
  <% else %>
    <li>
    <%= link_to 'Login', new_user_session_path %>
    </li>
  <% end %>
  <% if user_signed_in? %>
    <li>
    <%= link_to 'Edit account', edit_user_registration_path %>
    </li>
  <% else %>
    <li>
    <%= link_to 'Sign up', new_user_registration_path %>
    </li>
  <% end %>
</ul>

Flash Messages

Rails provides a standard convention to display alerts (including error messages) and other notices (including success messages), called Rails “flash messages” (as in “flash memory”, not to be confused with the “Adobe Flash” proprietary web development platform).

You can include code to display flash messages directly in your application layout file or you can create a partial.

Create a partial for flash messages in app/views/layouts/_messages.html.erb like this:

<% flash.each do |name, msg| %>
  <% if msg.is_a?(String) %>
    <%= content_tag :div, msg, :id => "flash_#{name}" %>
  <% end %>
<% end %>

Rails uses :notice and :alert as flash message keys. This partial will display flash messages. You can add a CSS style to display either a red or green message determined by the element ID.

CSS Styling with SASS

It’s a good idea to rename the app/assets/stylesheets/application.css file as app/assets/stylesheets/application.css.scss.

This will allow you to use the advantages of the SASS syntax and features for your application stylesheet. For more on the advantages of SASS and how to use it, see the SASS Basics RailsCast from Ryan Bates.

You can apply simple CSS styling for your navigation links and flash messages with the following code.

Add stylesheet rules to the app/assets/stylesheets/application.css.scss file:

.brand {
  float: left;
  padding-right: 8px;
}
ul.nav {
  list-style: none;
  margin: 0 0 2em;
  padding: 0;
}
ul.nav li {
  display: inline;
}
#flash_notice, #flash_alert {
  padding: 5px 8px;
  margin: 10px 0;
}
#flash_notice {
  background-color: #CFC;
  border: solid 1px #6C6;
}
#flash_alert {
  background-color: #FCC;
  border: solid 1px #C66;
}

The CSS style rules display the navigation links on a single line at the top of the page.

“Alert” messages are displayed in red and “notice” messages are displayed in green.

Default Application Layout

Generating a new Rails application with the rails new command will create a default application layout in the file app/views/layouts/application.html.erb. Modify the file to add navigation links, include flash messages, and apply CSS styling.

Use the code below to incorporate recommendations from the article HTML5 Boilerplate for Rails Developers.

Replace the contents of the file app/views/layouts/application.html.erb with this:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%= content_for?(:title) ? yield(:title) : "Rails3 Devise Rspec Cucumber" %></title>
    <meta name="description" content="<%= content_for?(:description) ? yield(:description) : "Rails3 Devise Rspec Cucumber" %>">
    <%= stylesheet_link_tag "application", :media => "all" %>
    <%= javascript_include_tag "application" %>
    <%= csrf_meta_tags %>
    <%= yield(:head) %>
  </head>
  <body class="<%= controller_name %> <%= action_name %>">
    <div id="container" class="container">
      <header>
        <%= render 'layouts/navigation' %>
        <%= render 'layouts/messages' %>
      </header>
      <div id="main" role="main">
        <%= yield %>
      </div>
      <footer>
      </footer>
    </div> <!--! end of #container -->
  </body>
</html>

See the article Rails Default Application Layout for an explanation of each of the elements in the application layout.

Your default application layout defines the look-and-feel of your application. You now have the basics with navigation links, messages for alerts and notices, and CSS styling.

Authentication

This app uses Devise for user management and authentication.

Set Up Configuration for Devise

You should have the following gem in your Gemfile:

gem 'devise'

If you haven’t already, run:

$ bundle install

Run the generator to install Devise:

$ rails generate devise:install

which installs a configuration file config/initializers/devise.rb and a localization file config/locales/devise.en.yml.

Configure Devise for Email

If you will be using the Devise Confirmable module to send confirmation emails, complete your email configuration by modifying the config/initializers/devise.rb file and setting the config.mailer_sender option for the return email address for messages that Devise sends from the application.

Generate a Model and Routes for Users

Use Devise to generate a model and routes for a User.

$ rails generate devise User

Devise will create a database migration and a User model.

Devise will try to create a spec file for the User model. If you’ve already downloaded the example app spec files, don’t let the Devise generator overwrite the spec/models/user_spec.rb file.

Devise will try to create factories for testing the User model. If you’ve already downloaded the spec/factories/users.rb file, don’t let the Devise generator overwrite the spec/factories/users.rb file (enter “n” to prevent overwriting).

Devise will modify the config/routes.rb file to add:

devise_for :users

which provides a complete set of routes for user signup and login. If you run rake routes you can see the routes that this line of code creates.

Accommodate Cucumber Testing for “Sign Out”

By default, Devise uses an http DELETE request for sign out requests (destroy_user_session_path). Rails uses Javascript to implement http DELETE requests. Prior to Devise 1.4.1 (27 June 2011), Devise used an http GET request for sign out requests. Jose Valim explained the change: “GET requests should not change the state of the server. When sign out is a GET request, CSRF can be used to sign you out automatically and things that preload links can eventually sign you out by mistake as well.”

However, Cucumber wants to test GET requests not DELETE requests. If you intend to use Cucumber with Devise, you must change the Devise default from DELETE to GET in /config/initializers/devise.rb for the Rails test environment. You may see a suggestion elsewhere to tweak the routes.rb file or change the log_out link to make the fix. It isn’t necessary if you change the /config/initializers/devise.rb file.

# The default HTTP method used to sign out a resource. Default is :delete.
config.sign_out_via = Rails.env.test? ? :get : :delete

Since you only use Cucumber during testing, switching the default is only needed for testing.

If you’re not going to use Cucumber, leave Devise’s default (DELETE) in place.

Prevent Logging of Passwords

We don’t want passwords written to our log file.

Modify the file config/application.rb to include:

config.filter_parameters += [:password, :password_confirmation]

Note that filter_parameters is an array.

Authorization

Devise provides authentication, a system to securely identify users, making sure the user is really who he represents himself to be. Devise also provides user management, implementing features that let a user sign up to create or edit an account.

Devise does not provide features for authorization. A system for authorization determines if an authenticated user should have access to secured resources. For example, you might wish to limit access so only users in an administrator role can see an administrative dashboard.

CanCan is by far the most popular gem used to implement authorization (see the Rails Authorization category on The Ruby Toolbox site). See the Rails3-Bootstrap-Devise-Cancan example application and tutorial if you want to implement role-based authorization in your application.

User Management

By default, Devise uses an email address to identify users. We’ll add a “name” attribute as well. Your application may not require a user to provide a name. But showing you how to add a name will help you see what you need to do if you decide to make changes to the default Devise user model.

Add a Migration

Devise created a migration file to establish the schema for the SQLite database with a migration file named something like db/migrate/xxxxxxx_devise_create_users.rb. We won’t modify the migration file. Instead we’ll add an additional migration that adds the “name” field to the User record.

$ rails generate migration AddNameToUsers name:string

Run the migration and prepare the test database to pick up the “name” field:

$ rake db:migrate
$ rake db:test:prepare

Modify the User Model

You’ll want to prevent malicious hackers from creating fake web forms that would allow changing of passwords through the mass-assignment operations of update_attributes(attrs) or new(attrs). Devise already added this to the models/user.rb file:

attr_accessible :email, :password, :password_confirmation, :remember_me

but you’ll need to add the “name” attribute:

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

If you wish, you can modify the user model to validate the presence and uniqueness of the “name” attribute. Modify the file app/models/user.rb and add:

validates_presence_of :name
validates_uniqueness_of :name, :email, :case_sensitive => false

This will allow users to be created (or edited) with a name attribute. When a user is created, a name and email address must be present and must be unique (not used before). Note that Devise (by default) will check that the email address and password are not blank and that the email address is unique.

Create Custom Views for User Registration

Devise provides a controller and views for registering users. It is called the “registerable” module. The controller and views are hidden in the Devise gem so we don’t need to create anything. However, because we want our users to provide a name when registering, we will create custom views for creating and editing a user. Our custom views will override the Devise gem defaults.

Create a new app/views/devise/registrations/new.html.erb file:

<h2>Sign up</h2>

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
  <%= devise_error_messages! %>
<p><%= f.label :name %><br />
<%= f.text_field :name %></p>

  <div><%= f.label :email %><br />
  <%= f.email_field :email %></div>

  <div><%= f.label :password %><br />
  <%= f.password_field :password %></div>

  <div><%= f.label :password_confirmation %><br />
  <%= f.password_field :password_confirmation %></div>

  <div><%= f.submit "Sign up" %></div>
<% end %>

<%= render "devise/shared/links" %>

Create a new app/views/devise/registrations/edit.html.erb file:

<h2>Edit <%= resource_name.to_s.humanize %></h2>

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
  <%= devise_error_messages! %>
<p><%= f.label :name %><br />
<%= f.text_field :name %></p>

  <div><%= f.label :email %><br />
  <%= f.email_field :email %></div>

  <div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
  <%= f.password_field :password, :autocomplete => "off" %></div>

  <div><%= f.label :password_confirmation %><br />
  <%= f.password_field :password_confirmation %></div>

  <div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
  <%= f.password_field :current_password %></div>

  <div><%= f.submit "Update" %></div>
<% end %>

<h3>Cancel my account</h3>

<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %>.</p>

<%= link_to "Back", :back %>

We do not need to add a controller with methods to create a new user or edit or delete a user. We use the existing “registerable” module from Devise which provides a controller with methods to create, edit or delete a user.

Note that Devise’s default behaviour allows any logged-in user to edit or delete his or her own record (but no one else’s). When you access the edit page you are editing just your info, and not info of other users.

If you are using Haml, you can convert the ERB files using the online tool Html2Haml. You’ll need to remove the .erb files and replace them with app/views/devise/registrations/edit.html.haml and app/views/devise/registrations/new.html.haml.

Home Page

Remove the Default Home Page

Delete the default home page from your application:

$ rm public/index.html

Create a Home Controller and View

Create the first page of the application. Use the Rails generate command to create a “home” controller and a “views/home/index” page:

$ rails generate controller home index --no-controller-specs --skip-stylesheets --skip-javascripts

Specify --no-controller-specs to avoid overwriting any RSpec files you’ve already downloaded. We’ll use --skip-stylesheets --skip-javascripts to avoid cluttering our application with stylesheet and JavaScript files we don’t need.

If you’re using the default template engine, you’ll find an ERB file with placeholder content: app/views/home/index.html.erb.

Next, set a route to your home page.

Modify the file config/routes.rb so it looks like this:

Rails3DeviseRspecCucumber::Application.routes.draw do
  authenticated :user do
    root :to => 'home#index'
  end
  root :to => "home#index"
  devise_for :users
end

Be careful. If you have changed the name of the application to something different from “rails3-devise-rspec-cucumber,” the application object should not be named Rails3DeviseRspecCucumber.

If you examine this code, you’ll see that authenticated users (those who have an account and are logged in) will see the home/index page as the application root (or home) page. And all other users (those who don’t have an account or who are not logged in) will see the same home page. The redundancy serves a didactic purpose: If you decide you want users to see a different page when they log in, you now know exactly where to change it.

By default, Devise will redirect to the root_path after successful sign in or sign out. It is easy to change the root_path as shown in the config/routes.rb file. Alternatively, you can override the Devise methods after_sign_in_path_for and after_sign_out_path_for as described in the Devise wiki article How To Redirect to a Specific Page.

Test the App

You can check that your app runs properly by entering the command

$ rails server

To see your application in action, open a browser window and navigate to http://localhost:3000/. You should see your new home page.

Stop the server with Control-C.

Display Users on the Home Page

Modify the file app/controllers/home_controller.rb and add:

def index
  @users = User.all
end

Modify the file app/views/home/index.html.erb and add:

<h3>Home</h3>
<% @users.each do |user| %>
  <p>User: <%= user.name %> </p>
<% end %>

This code is not appropriate for deployment in a real application. You likely will not want to display a list of users on the home page. However, it is convenient for our example.

Initial Data

Set Up a Database Seed File

You’ll want to set up default users so you can test the application.

Replace the file db/seeds.rb with:

puts 'DEFAULT USERS'
user = User.find_or_create_by_email :name => ENV['ADMIN_NAME'].dup, :email => ENV['ADMIN_EMAIL'].dup, :password => ENV['ADMIN_PASSWORD'].dup, :password_confirmation => ENV['ADMIN_PASSWORD'].dup
puts 'user: ' << user.name

The db/seeds.rb file initializes the database with default values. To keep some data private, and consolidate configuration settings in a single location, we use the config/application.yml file to set environment variables and then use the environment variables in the db/seeds.rb file.

You can change the administrator name, email, and password in this file but it is better to make the changes in the config/application.yml file to keep the credentials private. If you decide to include your private password in the db/seeds.rb file, be sure to add the filename to your .gitignore file so that your password doesn’t become available in your public GitHub repository.

Note that it’s not necessary to personalize the db/seeds.rb file before you deploy your app. You can deploy the app with an example user and then use the application’s “Edit Account” feature to change name, email address, and password after you log in.

If you wish, you can add a second example user:

user2 = User.find_or_create_by_email :name => 'Second User', :email => 'user2@example.com', :password => 'changeme', :password_confirmation => 'changeme'
puts 'user: ' << user2.name

If you’ve chosen to create the application with the Devise Confirmable module, add the field confirmed_at:

puts 'DEFAULT USERS'
user = User.find_or_create_by_email :name => ENV['ADMIN_NAME'].dup, :email => ENV['ADMIN_EMAIL'].dup, :password => ENV['ADMIN_PASSWORD'].dup, :password_confirmation => ENV['ADMIN_PASSWORD'].dup, :confirmed_at => DateTime.now
puts 'user: ' << user.name

Seed the Database

Initialize the database by running:

$ rake db:migrate
$ rake db:seed

You can run rake db:reset whenever you need to recreate the database:

$ rake db:reset

You should set up the database for the test environment:

$ rake db:test:prepare

If you’re not using rvm, you should preface each rake command with bundle exec. You don’t need to use bundle exec if you are using rvm version 1.11.0 or newer.

If the task fails with “Validation failed: Name can’t be blank” you should check that the file models/user.rb allows the “name” attribute to be mass updated:

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

Test the App

You can check that your app runs properly by entering the command

$ rails server

To see your application in action, open a browser window and navigate to http://localhost:3000/. You should see your new home page.

Stop the server with Control-C.

If you test the app by starting the web server and then leave the server running while you install new gems, you’ll have to restart the server to see any changes. The same is true for changes to configuration files in the config folder. This can be confusing to new Rails developers because you can change files in the app folders without restarting the server. Stop the server each time after testing and you will avoid this issue.

User Profiles

We can add user profile pages to demonstrate how Devise manages authentication.

You’ve already modified the file app/controllers/home_controller.rb to include this:

def index
  @users = User.all
end

Now we’ll add links to each user’s profile.

Modify the file app/views/home/index.html.erb to look like this:

<h3>Home</h3>
<% @users.each do |user| %>
  <p>User: <%=link_to user.name, user %></p>
<% end %>

This code is not appropriate for deployment in a real application. You likely will not want to display a list of users on the home page. However, it is convenient for our example.

The links to the user’s profile page will not yet work; in the next section we’ll create a users controller, routes, and views.

Create a Users Controller

Use the Rails generate command to create a “users” controller and a “views/user/show” page. You can specify --no-controller-specs if you’ve already downloaded RSpec files for the example application.

$ rails generate controller users index show --no-controller-specs --skip-stylesheets --skip-javascripts

Note that “users” is plural when you create the controller.

Set Up the Users Routes

The generator command has changed the file config/routes.rb to include:

get "users/index"
get "users/show"

Remove that and change the file config/routes.rb to look like this:

Rails3DeviseRspecCucumber::Application.routes.draw do
  authenticated :user do
    root :to => 'home#index'
  end
  root :to => "home#index"
  devise_for :users
  resources :users
end

Important note: The devise_for :users route must be placed above resources :users.

Set Up the Users#Show Page

Modify the file app/views/users/show.html.erb and add:

<h3>User</h3>
<p>User: <%= @user.name %></p>
<p>Email: <%= @user.email if @user.email %></p>

In a typical application, this page might provide additional details about the user’s account.

Set Up the Users#Index Page

The Users#Index page will display a list of all users of the application.

Modify the file app/views/users/index.html.erb and add:

<ul class="users">
  <% @users.each do |user| %>
    <li>
      <%= link_to user.name, user %> signed up <%= user.created_at.to_date %>
    </li>
  <% end %>
</ul>

Like the home page, this page displays a list of all user accounts. If we add an authorization feature to limit access, we could use this page as the basis for an “Administrator’s Dashboard” and make it accessible only to administrators.

Right now, any visitor can see this page. In the next section, we will set up authentication so the page is accessible only to users who are registered and logged in.

Restrict Access

Let’s see how Devise is used to limit access to only users who are registered and logged in.

Modify the file app/controllers/users_controller.rb to look like this:

class UsersController < ApplicationController
  before_filter :authenticate_user!

  def index
    @users = User.all
  end

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

end

The before_filter :authenticate_user! statement uses Devise to make sure a user is logged in.

Your application is ready to customize and deploy!

Deploy

Cleanup

Several unneeded files are generated in the process of creating a new Rails application.

Additionally, you may want to prevent search engines from indexing your website if you’ve deployed it publicly while still in development.

See instructions for cleaning up unneeded files in Rails and banning spiders.

Test the App

You can check that your app runs properly by entering the command:

$ rails server

To see your application in action, open a browser window and navigate to http://localhost:3000/. You should see the default user listed on the home page. When you click on the user’s name, you should be required to log in before seeing the user’s detail page.

To sign in as the first user, (unless you’ve changed it) use:

  • email: user@example.com
  • password: changeme

Stop the server with Control-C.

Testing

If you’ve copied the RSpec unit tests and Cucumber integration tests from the rails3-devise-rspec-cucumber example application, and set up RSpec and Cucumber, you can run rake -T to check that rake tasks for RSpec and Cucumber are available.

Run rake spec to run RSpec tests.

Run rake cucumber (or more simply, cucumber) to run Cucumber scenarios.

Deploy to Heroku

For your convenience, here is a Tutorial for Rails on Heroku. Heroku provides low cost, easily configured Rails application hosting.

Be sure to set up SSL before you make your application available in production. See the Heroku documentation on SSL.

Add this configuration parameter to the config/application.rb file:

# Heroku requires this to be false
config.assets.initialize_on_precompile=false

Then precompile assets, commit to git, and push to Heroku:

$ rake assets:precompile
$ git add -A
$ git commit -m "assets compiled for Heroku"
$ git push heroku master

You’ll need to set the configuration values from the config/application.yml file as Heroku environment variables. See the article Rails Environment Variables for more information.

With the figaro gem, just run:

$ rake figaro:heroku

Alternatively, you can set Heroku environment variables directly.

Here’s how to set environment variables directly on Heroku with heroku config:add.

$ heroku config:add GMAIL_USERNAME='myname@gmail.com' GMAIL_PASSWORD='secret'
$ heroku config:add 'ROLES=[admin, user, VIP]'
$ heroku config:add ADMIN_NAME='First User' ADMIN_EMAIL='user@example.com' ADMIN_PASSWORD='changeme'

Complete Heroku deployment with:

$ heroku run rake db:migrate
$ heroku run rake db:seed

See the Tutorial for Rails on Heroku for details.

Additional Features

You’ve created a fully functional web application that allows visitors to register and sign in.

Here are other example applications that add features to this application:

Tutorial GitHub Repo Description
Devise with CanCan and Twitter Bootstrap rails3-bootstrap-devise-cancan adds CanCan for authorization, Twitter Bootstrap for CSS
Rails Membership Site with Stripe rails-stripe-membership-saas Site with subscription billing using Stripe
Rails Membership Site with Recurly rails-recurly-subscription-saas Site with subscription billing using Recurly

Feature Requests

If you have suggestions for additional features, please create an issue on GitHub.

Comments

Credits

Daniel Kehoe implemented the application and wrote the tutorial.

Did You Like the Tutorial?

Was this useful to you? Follow rails_apps on Twitter and tweet some praise. I’d love to know you were helped out by the tutorial.

Any issues? Please create an issue on GitHub. Reporting (and patching!) issues helps everyone.

Comments disabled.