Clearance
Rails authentication for developers who write tests.
Integration with Suspenders
Clearance is based on the same conventions and tools as Suspenders Thus if you use it, you may already have some configuration mentioned below in place.
Gem installation (Rails 2.1+)
In config/environment.rb:
config.gem ‘mocha’ config.gem ‘thoughtbot-shoulda’, :lib => ‘shoulda’, :source => “http://gems.github.com”, :version => ‘>= 2.0.6’ config.gem ‘thoughtbot-factory_girl’, :lib => ‘factory_girl’, :source => “http://gems.github.com”, :version => ‘>= 1.1.5’ config.gem “thoughtbot-clearance”, :lib => ‘clearance’, :source => ‘http://gems.github.com’, :version => ‘>= 0.3.9’Then:
rake gems:install rake gems:unpackThe generator
Make sure the development database exists and run the generator:
script/generate clearanceA number of files will be created and instructions will be printed.
You may already have some of these files. Don’t worry. You’ll be asked if you want to overwrite them.
Modules
Clearance works by mixing behavior into tests, controllers, and models. For any file that you do not want to overwrite, include the corresponding Clearance module. They are namespaced exactly like the directory structure of a Rails app.
Application controller example:
class ApplicationController < ActionController::Base include Clearance::App::Controllers::ApplicationController endUser model example:
class User < ActiveRecord::Base include Clearance::App::Models::User endUser test example:
class UserTest < Test::Unit::TestCase include Clearance::Test::Unit::UserTest endThe migration
The generator will also create a migration to add a “users” table and run it. If the table already exists in the database, the migration will just add fields and indexes that are missing and required by Clearance. If the migration fails, the generator will revert all changes back.
Routes
Clearance will add these routes to your routes.rb:
map.resources :users, :has_one => [:password, :confirmation] map.resource :session map.resources :passwordsPlease note that Clearance depends on root_url, so please make sure that it is defined to something in your config/routes.rb:
map.root :controller => ‘home’Environments
You need to define HOST constant in your environments files. In config/environments/test.rb and config/environments/development.rb it can be:
HOST = “localhost”While in config/environments/production.rb it must be the actual host your application is deployed to because the constant is used by mailers to generate URLs in emails.
In config/environment.rb:
DO_NOT_REPLY = “[email protected]”Tests
The tests use Shoulda >= 2.0.6 and Factory Girl >= 1.1.5. There needs to be a Clearance module in your test/test_helper.rb:
class Test::Unit::TestCase self.use_transactional_fixtures = true self.use_instantiated_fixtures = false include Clearance::Test::TestHelper endThe generator will create a user factory in test/factories/clearance.rb unless you have it defined somewhere else.
Usage: basic workflow
Rails authentication with Clearance uses the standard approach thoughtbot and our clients have agreed upon.
Users register (UsersController) using an email address and a password (User model). They get an email (ClearanceMailer) with a confirmation link to confirm their registration (ConfirmationController).
Registered users can sign in and out (SessionsController). If they forget their password, they request an email (ClearanceMailer) containing a link to change it (PasswordsController).
Usage: actions which require an authenticated user
To protect your controllers with authentication add:
class ProtectedController < ApplicationController before_filter :authenticateThe filter will ensure that only authenticated users can access the controller. If someone who’s not signed in tries to access a protected action:
- the URL is stored in the session,
- the user is redirected to sign in page, and
- after successful authentication will be be redirected back to that URL.
Usage: signed_in?, current_user
Clearance provides two methods that can be used in controllers, helpers, and views to check if current user is authenticated and get the actual user:
- signed_in?
- current_user
Usage: mass assignment
Please note that all User attributes except email, password and password_confirmation are protected from mass assignment by default. Use attr_accessible to enable it for your custom attributes.
class User < ActiveRecord::Base include Clearance::App::Models::User attr_accessible :first_name, :last_name endHooks: return_to parameter
To specify where to redirect a user (say you want to have a sign in form on every page and redirect the user to the same page) after he/she signs in, you can add a “return_to” parameter to the request (thanks to Phillippe for the tip):
<% form_for :session, :url => session_path(:return_to => request.request_uri) do |form| %>Hooks: url_after_create, url_after_update, url_after_destroy
Actions that redirect (create, update, and destroy) in Clearance controllers are customizable. If you want to redirect a user to a specific route after signing in, overwrite the “url_after_create” method in the SessionsController:
class SessionsController < ApplicationController include Clearance::App::Controllers::SessionsController private def url_after_create new_blog_post_path end endThere are similar methods in other controllers as well:
UsersController#url_after_create (register) SessionsController#url_after_create (sign in) SessionsController#url_after_destroy (sign out) PasswordsController#url_after_create (password request) PasswordsController#url_after_update (password) ConfirmationsController#url_after_create (confirmation)Hooks: sign_user_in
Say you want to add a last_signed_in_at attribute to your User model. You would want to update it when the User signs in.
Clearance has a method named sign_user_in that you can overwrite with that logic. Be sure to call sign_in(user) at the end (and write tests!).
class ApplicationController < ActionController::Base include Clearance::App::Controllers::ApplicationController private def sign_user_in(user)- store current time to display “last signed in at” message user.update_attribute(:last_signed_in_at, Time.now) sign_in(user) end end
Authors
- thoughtbot, inc.
- Dan Croak
- Mike Burns
- Jason Morrison
- Eugene Bolshakov
- Josh Nichols
- Mike Breen