-
Notifications
You must be signed in to change notification settings - Fork 0
tutorial rails bootstrap devise cancan
Last updated 19 April 2012
Ruby on Rails tutorial showing how to create a Rails 3.2 application using Devise with CanCan and Twitter Bootstrap.
- Devise gives you ready-made authentication and user management.
- CanCan provides authorization which restricts what resources a given user is allowed to access.
- Twitter Bootstrap is a front-end framework for CSS styling.
You can create the example app from this tutorial or clone the rails3-bootstrap-devise-cancan repository for the complete application. If you want to use the project as a starter app for your own customized application, see the rails3-bootstrap-devise-cancan README to use an application template to generate a your own version of the example app. Generating the application gives you many options, such as using Haml for views, additional Devise modules, and other popular gems such as will-paginate.
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 is based on the rails3-devise-rspec-cucumber example and tutorial and adds CanCan and Twitter Bootstrap. View the rails3-devise-rspec-cucumber example and tutorial for the basics of setting up an application with RSpec and Cucumber.
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.
To see a more complex application that uses Devise, CanCan, and Twitter Bootstrap, see the rails-prelaunch-signup example and tutorial from the RailsApps project.
Follow the project on Twitter: rails_apps. Tweet some praise if you like what you’ve found.
This tutorial documents each step that you must follow to create this application. Every step is documented concisely, so a complete beginner can create this application without any additional knowledge. However, no explanation is offered for any of the steps, so if you are a beginner, you’re advised to look for an introduction to Rails elsewhere. See resources for getting started with Rails.
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-bootstrap-devise-cancan 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 clone and install the example app and find problems or wish to suggest improvements, please create a GitHub issue.
To improve this tutorial, please edit this wiki page or leave comments below.
Follow this 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.
Use the ready-made application template to generate the code.
You can use an application template to generate a new Rails app with code that closely matches the tutorial. You’ll find an application template for this tutorial in the Rails Application Templates repository.
You’ll be able to give it your own project name when you generate the app. Generating the application (described below) gives you many options, such as using Haml for views, additional Devise modules, and other popular gems such as will-paginate.
Use the command:
$ rails new rails3-bootstrap-devise-cancan -m https://raw.github.com/RailsApps/rails3-application-templates/master/rails3-bootstrap-devise-cancan-template.rb -T
Use the -T flag to skip Test::Unit files.
The $ character indicates a shell prompt; don’t include it when you run the command.
This creates a new Rails app named rails3-bootstrap-devise-cancan on your computer. You can use a different name if you wish.
The application generator template will ask you for your preferences.
To produce an application exactly like the tutorial, make the following selections:
- Would you like to use Haml instead of ERB? no
- Would you like to use RSpec instead of TestUnit? yes
- Would you like to use factory_girl for test fixtures with RSpec? yes
- Would you like to use machinist for test fixtures with RSpec? no
- Would you like to use Cucumber for your BDD? yes
- Would you like to use Guard to automate your workflow? no
- Would you like the app to use a Gmail account to send email? yes
- Would you like to use Devise for authentication? #2
- No
- Devise with default modules
- Devise with Confirmable module
- Devise with Confirmable and Invitable modules
- Would you like to manage authorization with CanCan & Rolify? yes
- Which front-end framework would you like for HTML5 and CSS3? #4
- None
- Zurb Foundation
- Twitter Bootstrap (less)
- Twitter Bootstrap (sass)
- Skeleton
- Normalize CSS for consistent styling
- Which form gem would you like? #1
- None
- simple form
- simple form (bootstrap)
- Would you like to use rails-footnotes during development? no
- Would you like to set a robots.txt file to ban spiders? yes
- Would you like to add ‘will_paginate’ for pagination? no
Be sure to choose the CanCan & Rolify option as well as the Twitter Bootstrap (sass) option to create the example application.
You can choose other selections if you don’t care about matching the example application exactly.
You can choose the Devise with Confirmable module option if you want new users to confirm their email address before gaining access to your site. If you don’t wish to require email confirmation, choose the option Devise with default modules.
Use the rails_apps_composer gem to create a reusuable application template.
This is optimal if you are creating a “starter app” based on this example app but wish to customize the code for your own preferences.
Each step in this tutorial has a corresponding application template recipe from the Rails Apps Composer recipes repository. You can create your own application template using the template recipes. To do so, clone the Rails Apps Composer project, customize recipes as needed, and follow the instructions to create a reusable application template file.
Before beginning this tutorial, you need to install
- The Ruby language (version 1.9.3 or newer)
- 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 for detailed instructions and advice.
Beginning here, we show how to create the application from scratch.
Open a terminal, navigate to a folder where you have rights to create files, and type:
$ rails new rails3-bootstrap-devise-cancan -T
Use the -T flag to skip Test::Unit files (since you are 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-bootstrap-devise-cancan.”
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 that application:
$ cd rails3-bootstrap-devise-cancan
If you’re open sourcing the app on GitHub, please edit the README file to add a description of the app and your contact info. Changing the README is important if you’re using a clone of the example app. I’ve been mistaken (and contacted) as the author of apps that are copied from my example.
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 .
$ git commit -m 'Initial commit'
See detailed instructions for Using Git with Rails.
The application uses the following gems:
- rails
- rspec-rails
- database_cleaner
- factory_girl_rails
- email_spec
- cucumber-rails
- capybara
- devise
- cancan
- rolify
- bootstrap-sass
It’s a good idea to create a new gemset using rvm, the Ruby Version Manager, as described in the article Installing Rails.
See Example Gemfiles for Rails 3.2.
Open your Gemfile and replace the contents with the following:
Gemfile
source 'https://rubygems.org' gem 'rails', '3.2.3' 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.9.0.rc2", :group => [:development, :test] gem "factory_girl_rails", ">= 3.1.0", :group => [:development, :test] gem "email_spec", ">= 1.2.1", :group => :test gem "cucumber-rails", ">= 1.3.0", :group => :test gem "capybara", ">= 1.1.2", :group => :test gem "database_cleaner", ">= 0.7.2", :group => :test gem "launchy", ">= 2.1.0", :group => :test gem "devise", ">= 2.1.0.rc" gem "cancan", ">= 1.6.7" gem "rolify", ">= 3.1.0" gem "bootstrap-sass", ">= 2.0.1"
Check for the current version of Rails and replace gem 'rails', '3.2.3' 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.
Install the required gems on your computer:
$ bundle install
You can check which gems are installed on your computer with:
$ gem list --local
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.
In this tutorial, we’ll use the default “ERB” Rails template engine. Optionally, you can use another template engine, such as Haml. See instructions for adding Haml to Rails.
The rails3-devise-rspec-cucumber tutorial shows how to set up RSpec and provides example specs for use with Devise.
The rails3-devise-rspec-cucumber tutorial shows how to set up Cucumber and provides example scenarios for use with Devise.
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.
The “Configure Email” step in the rails3-devise-rspec-cucumber tutorial is identical.
You must configure the app for your email account if you want your application to send email messages, for example, if you’ve generated the application with the option to install the Devise :confirmable module.
Remove the following from the config/environments/development.rb file:
# Don't care if the mailer can't send config.action_mailer.raise_delivery_errors = false
Add the following to the config/environments/development.rb file:
# ActionMailer Config
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
config.action_mailer.delivery_method = :smtp
# change to false to prevent email from being sent during development
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default :charset => "utf-8"
Add the following to the config/environments/production.rb file:
config.action_mailer.default_url_options = { :host => 'example.com' }
# ActionMailer Config
# Setup for production - deliveries, no errors raised
config.action_mailer.delivery_method = :smtp
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = false
config.action_mailer.default :charset => "utf-8"
Add the following to the config/environments/test.rb file:
# ActionMailer Config
config.action_mailer.default_url_options = { :host => 'example.com' }
This will set the example application to deliver email in both development and production. It will raise delivery errors in development but not production.
In development, config.action_mailer.default_url_options is set for a host at localhost:3000 which will enable links in Devise confirmation email messages to work properly during development.
For testing, config.action_mailer.default_url_options is set for a host at example.com. Any value allows tests to run.
For production, you’ll need to change the config.action_mailer.default_url_options host option from example.com to your own domain.
If you want to use a Gmail account to send email, you’ll need to modify the files config/environments/development.rb and config/environments/production.rb:
config.action_mailer.smtp_settings = {
address: "smtp.gmail.com",
port: 587,
domain: "example.com",
authentication: "plain",
enable_starttls_auto: true,
user_name: ENV["GMAIL_USERNAME"],
password: ENV["GMAIL_PASSWORD"]
}
You can replace ENV["GMAIL_USERNAME"] and ENV["GMAIL_PASSWORD"] with your Gmail username and password. However, committing the file to a public GitHub repository will expose your secret password.
If you’re familiar with setting Unix environment variables, it’s advisable to leave config.action_mailer.smtp_settings unchanged and set your environment variables in the file that is read when starting an interactive shell (the ~/.bashrc file for the bash shell). This will keep the password out of your repository.
Are you using a bash shell? Use echo $SHELL to find out. For a bash shell, edit the ~/.bashrc file and add:
export GMAIL_USERNAME="myname@gmail.com" export GMAIL_PASSWORD="secret*"
Open a new shell or restart your terminal application to continue.
The “Set Up Authentication” step in the rails3-devise-rspec-cucumber tutorial is identical.
This app uses Devise for user management and authentication.
You should have the following gem in your Gemfile file:
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.
Complete your email configuration by modifying
config/initializers/devise.rb
and setting the config.mailer_sender option for the return email address for messages that Devise sends from the application.
Use Devise to generate models 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 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.
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.
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.
The rails3-devise-rspec-cucumber tutorial does not include a “Set Up Authorization” step.
Devise provides authentication, a system to securely identify users, making sure the user is really who he represents himself to be. We need to add a system for authorization to determine if an authenticated user should have access to secured resources. This app uses CanCan for authorization, to restrict access to pages that should only be viewed by an administrator. Cancan is by far the most popular gem used to implement authorization (see the Rails Authorization category on The Ruby Toolbox site) . The author of CanCan, Ryan Bates, offers a RailsCast on Authorization with CanCan to show how to use it.
There are many ways to implement authorization in a web application. CanCan offers an architecture that centralizes all authorization rules (permissions or “abilities”) in a single location, the CanCan Ability class. For a discussion of the benefits of using a single, consolidated location for the permissions, see the article Don’t Do Role-Based Authorization Checks; Do Activity-Based Checks.
CanCan provides a mechanism for limiting access at the level of controller and controller method and expects you to set permissions based on user attributes you define. CanCan doesn’t provide default user attributes such as user roles; you must implement this outside of CanCan. There are many ways to implement role-based authorization for use with CanCan. For this example, we use Florent Monbillard’s Rolify gem to create a Role model, add methods to a User model, and generate a migration for a roles table.
CanCan provides a Rails generator to create the CanCan Ability class. Run the generator to create the file app/models/ability.rb:
$ rails generate cancan:ability
Edit the file app/models/ability.rb to define a simple rule for granting permission to an administrator to access any page:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.has_role? :admin
can :manage, :all
end
end
end
Rules defined in the Ability class can become quite complex. See the CanCan wiki Defining Abilities for details.
Note that the user.has_role? method doesn’t yet exist. We’ll add the method when we set up Rolify.
If user authorization fails, a CanCan::AccessDenied exception will be raised. See the CanCan wiki Exception Handling for ways to handle authorization exceptions.
For this example, we’ll handle the CanCan::AccessDenied exception in the ApplicationController. We’ll set an error message and redirect to the home page. Modify the file app/controllers/application_controller.rb like this:
class ApplicationController < ActionController::Base
protect_from_forgery
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_path, :alert => exception.message
end
end
We’ll use the Rolify gem to implement user roles. Rolify provides a Rails generator to create a Role model, add methods to a User model, and generate a migration for a roles table. Run the command:
$ rails generate rolify:role
If you’re using Mongoid with the MongoDB datastore, add parameters to the command:
$ rails generate rolify:role Role User mongoid
The generator will insert a rolify method in app/models/users.rb and it will create several files:
- app/models/role.rb
- config/initializers/rolify.rb
- db/migrate/…rolify_create_roles.rb
The app/models/role.rb looks like this:
class Role < ActiveRecord::Base has_and_belongs_to_many :users, :join_table => :users_roles belongs_to :resource, :polymorphic => true end
These few steps with CanCan and Rolify implement role-based authorization in our application. We’ll use the authorization options provided by CanCan and Rolify when we add an administrative page with corresponding links.
The “Customize the Application” step in the rails3-devise-rspec-cucumber tutorial is identical.
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.
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:
$ bundle exec rake db:migrate $ bundle exec rake db:test:prepare
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.
You’ll also 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) and 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’ve chosen to create the application with the Devise Confirmable module, also add :confirmed_at:
attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :confirmed_at
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.
First, to copy all the default Devise views to your application, run
rails generate devise:views
This will generate a set of views in the directory app/views/devise/.
Next, modify the views to create and edit users.
Add the following code to each file:
app/views/devise/registrations/edit.html.erb
<p><%= f.label :name %><br /> <%= f.text_field :name %></p>
app/views/devise/registrations/new.html.erb
<p><%= f.label :name %><br /> <%= f.text_field :name %></p>
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, Devise does not generate views for Haml (it did before Devise 1.2; see Devise issue 878). See How To Create Haml and Slim Views from the Devise wiki.
If you are using Haml, you can generate the ERB files using rails generate devise:views and then convert them 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.
The “Create a Home Page” step in the rails3-devise-rspec-cucumber tutorial is identical.
Delete the default home page from your application:
$ rm public/index.html
Create the first page of the application. Use the Rails generate command to create a “home” controller and a “views/home/index” page. Specify --no-controller-specs to avoid overwriting the RSpec files you’ve already downloaded.
$ rails generate controller home index --no-controller-specs
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. Edit the file config/routes.rb and replace:
get "home/index"
with
authenticated :user do root :to => 'home#index' end root :to => "home#index"
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.
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.
The “Display Users on the Home Page” step in the rails3-devise-rspec-cucumber tutorial is identical.
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.
The “Set Up a Database Seed File” step in the rails3-devise-rspec-cucumber tutorial is very similar. Here we add code to make the first user an administrator.
You’ll want to set up a default user so you can test the app. Modify the file db/seeds.rb by adding:
puts 'SETTING UP DEFAULT USER LOGIN' user = User.create! :name => 'First User', :email => 'user@example.com', :password => 'please', :password_confirmation => 'please' puts 'New user created: ' << user.name user2 = User.create! :name => 'Second User', :email => 'user2@example.com', :password => 'please', :password_confirmation => 'please' puts 'New user created: ' << user2.name user.add_role :admin
If you’ve chosen to create the application with the Devise Confirmable module, add the field confirmed_at:
puts 'SETTING UP DEFAULT USER LOGIN' user = User.create! :name => 'First User', :email => 'user@example.com', :password => 'please', :password_confirmation => 'please', :confirmed_at => DateTime.now puts 'New user created: ' << user.name user2 = User.create! :name => 'Second User', :email => 'user2@example.com', :password => 'please', :password_confirmation => 'please', :confirmed_at => DateTime.now puts 'New user created: ' << user2.name user.add_role :admin
You can change the values for name, email, and password as you wish.
Add the default user to the database by running the command:
$ bundle exec rake db:seed
If you need to, you can run $ bundle exec rake db:reset to drop and then recreate the database using your seeds.rb file.
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
At this point, you may want to know if the default user has been saved to the database.
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.
The “Add Links to Users on the Home Page” step in the rails3-devise-rspec-cucumber tutorial is identical.
You’ve already modified the file app/controllers/home_controller.rb to include this:
def index @users = User.all end
Now 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.
The “Set Up the Users Controller, Views, and Routes” step in the rails3-devise-rspec-cucumber tutorial is very similar. Here we set up the UsersController to demonstrate use of authorization in a later step.
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
Note that “users” is plural when you create the controller.
The file config/routes.rb has already been modified to include:
get "users/index" get "users/show"
Remove that and change the routes to:
root :to => "home#index" devise_for :users resources :users, :only => [:show, :index]
Important note: The devise_for :users route must be placed above resources :users, :only => [:show, :index].
Modify the file app/views/users/show.html.erb and add:
<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.
For the purposes of our example, this page will be accessible only to administrators. It 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>
We want to restrict access to this page, the Users#index page at http://localhost:3000/users. In the next section, we will set up authorization so the page is accessible only to administrators.
The “Set Up a Demonstration of Devise” step in the rails3-devise-rspec-cucumber tutorial is very similar. Here we add code to limit access to the index method of the UsersController.
You’ll want to see how CanCan is used to limit access to only the administrator.
Modify the file app/controllers/users_controller.rb like this:
class UsersController < ApplicationController
before_filter :authenticate_user!
def index
authorize! :index, @user, :message => 'Not authorized as an administrator.'
@users = User.all
end
def show
@user = User.find(params[:id])
end
end
Only one line of code is needed to limit access to the Users#index page at
http://localhost:3000/users:
authorize! :index, @user, :message => 'Not authorized as an administrator.'
The CanCan authorize! method will check the CanCan Ability class definition to determine if the user has permission to execute the index method. We’ve previously set a rule in the Ability class definition that gives a user in an administrator role the ability to execute all methods:
if user.has_role? :admin can :manage, :all end
No rule is present for other users, so by default, other users are restricted from executing the index method.
This approach is the most obvious way to implement authorization using CanCan. However, CanCan offers convenience methods that can eliminate superfluous controller code in some applications. The next example shows how to use CanCan convenience methods to reduce the amount of code in your controllers.
CanCan provides a convenience method authorize_resource that applies the authorize! method to every action in the controller. Another convenience method load_resource queries the database and loads the resources required by each action (for example, users = User.all for the index action). A third convenience method combines the two as load_and_authorize_resource.
Using load_and_authorize_resource, you can set up the file app/controllers/users_controller.rb like this:
class UsersController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource :only => :index
def show
@user = User.find(params[:id])
end
end
The index action does not need to be declared because Rails provides it by default.
If a non-administrator tries to view the at http://localhost:3000/users he or she will be redirected to the home page (as specified by the rescue_from CanCan::AccessDenied method in the ApplicationController) and will see CanCan’s generic exception message, “You are not authorized to access this page.” You can customize the exception message in the ApplicationController, if you wish.
Some developers will like this approach; others will feel it dries up code at the expense of introducing layers of black magic. If you prefer a more explicit approach, the next example shows how to implement simple role-based authorization without using CanCan at all.
The purported benefit of using CanCan is the advantage of maintaining authorization rules in one location, the Ability class. This may be a matter of taste; you may prefer to confine authorization code to only the controllers that need it.
Here is an example of limiting access to the Users#index page using only methods provided by the Rolify gem. CanCan is not used.
class UsersController < ApplicationController
before_filter :authenticate_user!
before_filter :only_allow_admin, :only => [ :index ]
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
private
def only_allow_admin
redirect_to root_path, :alert => 'Not authorized as an administrator.' unless current_user.has_role? :admin
end
end
If multiple controllers will use the only_allow_admin method, it can be moved to the ApplicationController so all controllers will inherit the method.
In contrast to CanCan, all the authorization logic is defined in the controller. For a small application, this approach is simpler and less confusing. You may find this approach preferable to using CanCan, if your application only requires simple role-based authorization. However, for a large or complex application with multiple roles and many constrained activities, CanCan offers better separation of concerns.
Rails will use the layout defined in the file app/views/layouts/application.html.erb as a default for rendering any page. If you are using Haml, the file will be app/views/layouts/application.html.haml.
You’ll want to add navigation links, include flash messages for errors and notifications, and apply CSS styling.
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. And a user who is an administrator should see a link for Admin.
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 Bootstrap Devise Cancan", 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>
<% if current_user.has_role? :admin %>
<li>
<%= link_to('Admin', users_path) %>
</li>
<% end %>
<% else %>
<li>
<%= link_to('Sign up', new_user_registration_path) %>
</li>
<% end %>
</ul>
Notice the condition <% if current_user.has_role? :admin %> that uses a has_role? method provided by the Rolify gem. The Admin link will display only if the user is an administrator.
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| %>
<div class="alert alert-<%= name == :notice ? "success" : "error" %>">
<a class="close" data-dismiss="alert">×</a>
<%= content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) %>
</div>
<% end %>
Rails uses :notice and :alert as flash message keys. Twitter Bootstrap provides a base class .alert with additional classes .alert-success and .alert-error (see the Bootstrap documentation on alerts). A bit of parsing is required to get a Rails “notice” message to be styled with the Twitter Bootstrap “alert-success” style. Any other message, including a Rails “alert” message, will be styled with the Twitter Bootstrap “alert-error” style.
Twitter Bootstrap provides a jQuery plugin named bootstrap-alert that makes it easy to dismiss flash messages with a click. The data-dismiss property displays an “x” that enables the close functionality. Note that Twitter Bootstrap uses the HTML entity “×” instead of the keyboard letter “x”.
By default, Twitter Bootstrap applies a green background to .alert-success and a red background to .alert-error. Twitter Bootstrap provides a third class .alert-info with a blue background. With a little hacking, it’s possible to create a Rails flash message with a custom name, such as :info, that will display with the Bootstrap .alert-info class. However, it’s wise to stick with the Rails convention of using only “alert” and “notice.”
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.
Twitter Bootstrap and other CSS front-end frameworks (such as Zurb Foundation) are toolkits that provide the kind of structure and convention that make Rails popular for server-side (“back-end”) development. You can use Twitter Bootstrap to quickly add attractive CSS styling to your application. If you’ve generated your app from an application template in the RailsApps Application Templates repository, the script will offer to install Twitter Bootstrap or other CSS front-end frameworks and set up your default application layout accordingly.
Several options are available for installing Twitter Bootstrap in a Rails application. Twitter Bootstrap can be installed using either its native LESS CSS language or the SASS language that is the default for CSS files in Rails. See the article Twitter Bootstrap, Less, and Sass: Understanding Your Options for Rails 3.1. SASS is a default for CSS development in Rails so I recommend you install Thomas McDonald’s bootstrap-sass gem.
In your Gemfile, you’ve already added:
gem 'bootstrap-sass'
and previously run $ bundle install.
Include the Twitter Bootstrap Javascript files by modifying the file app/assets/javascripts/application.js:
//= require jquery //= require jquery_ujs //= require bootstrap //= require_tree .
Next, import the Twitter Bootstrap CSS files. You can modify the app/assets/stylesheets/application.css.scss file to import Bootstrap. However, I recommend adding a new file app/assets/stylesheets/bootstrap_and_overrides.css.scss file. You may wish to modify the Twitter Bootstrap CSS rules; you can do so in the application.css.scss file but placing changes to Twitter Bootstrap CSS rules in the bootstrap_and_overrides.css.scss file will keep your CSS better organized.
The file app/assets/stylesheets/bootstrap_and_overrides.css.scss is automatically included and compiled into your Rails application.css file by the *= require_tree . statement in the app/assets/stylesheets/application.css.scss file.
Add the file app/assets/stylesheets/bootstrap_and_overrides.css.scss:
// Set the correct sprite paths
$iconSpritePath: image-path('glyphicons-halflings.png');
$iconWhiteSpritePath: image-path('glyphicons-halflings-white.png');
@import "bootstrap";
body { padding-top: 60px; }
@import "bootstrap-responsive";
The file will import both basic Bootstrap CSS rules and the Bootstrap rules for responsive design (allowing the layout to resize for various devices and secreen sizes).
The CSS rule body { padding-top: 60px; } applies an additional CSS rule to accommodate the “Bootstrap black bar” heading created by the navbar-fixed-top class in the header tag in the layout below.
Finally, to provide an example of adding a CSS rule that will be used on every page of your application, the following code creates a nice gray box as a background to page content.
Add this to your app/assets/stylesheets/application.css.scss file for a gray background:
.content {
background-color: #eee;
padding: 20px;
margin: 0 -20px; /* negative indent the amount of the padding to maintain the grid system */
-webkit-border-radius: 0 0 6px 6px;
-moz-border-radius: 0 0 6px 6px;
border-radius: 0 0 6px 6px;
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.15);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.15);
box-shadow: 0 1px 2px rgba(0,0,0,.15);
}
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. Twitter Bootstrap provides additional elements for a more complex page layout.
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 charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= content_for?(:title) ? yield(:title) : "Myapp" %></title>
<meta name="description" content="">
<meta name="author" content="">
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
<%= yield(:head) %>
</head>
<body>
<header class="navbar navbar-fixed-top">
<nav class="navbar-inner">
<div class="container">
<%= render 'layouts/navigation' %>
</div>
</nav>
</header>
<div id="main" role="main">
<div class="container">
<div class="content">
<div class="row">
<div class="span12">
<%= render 'layouts/messages' %>
<%= yield %>
</div>
</div>
<footer>
</footer>
</div>
</div> <!--! end of .container -->
</div> <!--! end of #main -->
</body>
</html>
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 using Twitter Bootstrap.
There’s much more you can do. For an advanced example, see the RailsApp rails-prelaunch-signup example app. The rails-prelaunch-signup tutorial shows how you can use Twitter Bootstrap to add a modal window and AJAX for a “sign up” form for a “Web 2.0” application. When the visitor submits the form, the modal window changes to display a “thank you” message (or an error message) without a page refresh.
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.
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 (the administrator), (unless you’ve changed it) use
- email: user@example.com
- password: please
You’ll see a navigation link for Admin. Clicking the link will display a page with a list of users at
http://localhost:3000/users.
To sign in as the second user, (unless you’ve changed it) use
- email: user2@example.com
- password: please
The second user will not see the Admin navigation link and will not be able to access the page at
http://localhost:3000/users.
Stop the server with Control-C.
Heroku provides low cost, easily configured Rails application hosting. For your convenience, see Tutorial: Rails on Heroku.
This concludes the tutorial for creating a Ruby on Rails web application that uses Devise and adds CanCan and Twitter Bootstrap.
Daniel Kehoe implemented the application and wrote 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.


Tutorial