Introducing Registerable module, allowing users to sign up.

This commit is contained in:
Carlos Antonio da Silva
2010-01-23 22:26:06 -02:00
parent 4de1e43b7a
commit 6b837cb285
17 changed files with 173 additions and 22 deletions

View File

@@ -7,11 +7,12 @@ Devise is a flexible authentication solution for Rails based on Warden. It:
* Allows you to have multiple roles (or models/scopes) signed in at the same time;
* Is based on a modularity concept: use just what you really need.
Right now it's composed of nine modules:
Right now it's composed of ten modules:
* Authenticatable: responsible for encrypting password and validating authenticity of a user while signing in.
* Confirmable: responsible for verifying whether an account is already confirmed to sign in, and to send emails with confirmation instructions.
* Recoverable: takes care of reseting the user password and send reset instructions.
* Registerable: handles signing up users through a registration process.
* Rememberable: manages generating and clearing token for remember the user from a saved cookie.
* Trackable: tracks sign in count, timestamps and ip.
* Validatable: creates all needed validations for email and password. It's totally optional, so you're able to to customize validations by yourself.
@@ -170,7 +171,7 @@ Since devise is an engine, it has all default views inside the gem. They are goo
ruby script/generate devise_views
By default Devise will use the same views for all roles you have. But what if you need so different views to each of them? Devise also has an easy way to accomplish it: just setup config.scoped_views to true inside "config/initializers/devise.rb".
By default Devise will use the same views for all roles you have. But what if you need so different views to each of them? Devise also has an easy way to accomplish it: just setup config.scoped_views to true inside "config/initializers/devise.rb".
After doing so you will be able to have views based on the scope like 'sessions/users/new' and 'sessions/admin/new'. If no view is found within the scope, Devise will fallback to the default view.

View File

@@ -0,0 +1,22 @@
class RegistrationsController < ApplicationController
include Devise::Controllers::InternalHelpers
include Devise::Controllers::Common
# POST /resource/registration
def create
self.resource = resource_class.new(params[resource_name])
if resource.save
# Attempt to sign the resource in. When there is no other thing blocking
# the resource (ie confirmations), then the resource will be signed in,
# otherwise the specific message is shown and the resource will be
# redirected to the sign in page.
sign_in(resource_name, resource)
# At this time the resource has signed in and no hook has signed it out.
set_flash_message :notice, :signed_up
sign_in_and_redirect(resource_name, resource, true)
else
render_with_scope :new
end
end
end

View File

@@ -0,0 +1,19 @@
<h2>Sign up</h2>
<%- if devise_mapping.registerable? %>
<% form_for resource_name, resource, :url => registration_path(resource_name) do |f| -%>
<%= f.error_messages %>
<p><%= f.label :email %></p>
<p><%= f.text_field :email %></p>
<p><%= f.label :password %></p>
<p><%= f.password_field :password %></p>
<p><%= f.label :password_confirmation %></p>
<p><%= f.password_field :password_confirmation %></p>
<p><%= f.submit "Register" %></p>
<% end -%>
<% end%>
<%= render :partial => "shared/devise_links" %>

View File

@@ -2,6 +2,10 @@
<%= link_to t('devise.sessions.link'), new_session_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to t('devise.registrations.link'), new_registration_path(resource_name) %><br />
<% end -%>
<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
<%= link_to t('devise.passwords.link'), new_password_path(resource_name) %><br />
<% end -%>
@@ -12,4 +16,4 @@
<%- if devise_mapping.lockable? && controller_name != 'unlocks' %>
<%= link_to t('devise.unlocks.link'), new_unlock_path(resource_name) %><br />
<% end -%>
<% end -%>

View File

@@ -26,14 +26,16 @@ module Devise
autoload :MongoMapper, 'devise/orm/mongo_mapper'
end
ALL = [:authenticatable, :activatable, :confirmable, :recoverable,
:rememberable, :validatable, :trackable, :timeoutable, :lockable, :token_authenticatable]
ALL = [:authenticatable, :activatable, :confirmable, :lockable, :recoverable,
:registerable, :rememberable, :timeoutable, :token_authenticatable,
:trackable, :validatable]
# Maps controller names to devise modules
CONTROLLERS = {
:sessions => [:authenticatable, :token_authenticatable],
:passwords => [:recoverable],
:confirmations => [:confirmable],
:registrations => [:registerable],
:unlocks => [:lockable]
}
@@ -231,4 +233,4 @@ rescue
end
require 'devise/mapping'
require 'devise/rails'
require 'devise/rails'

View File

@@ -19,7 +19,7 @@ module Devise
# Those helpers are added to your ApplicationController.
module UrlHelpers
[:session, :password, :confirmation, :unlock].each do |module_name|
[:session, :password, :confirmation, :registration, :unlock].each do |module_name|
[:path, :url].each do |path_or_url|
actions = [ nil, :new_ ]
actions << :edit_ if module_name == :password

View File

@@ -19,6 +19,9 @@ en:
link: "Didn't receive confirmation instructions?"
send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
confirmed: 'Your account was successfully confirmed. You are now signed in.'
registrations:
link: 'Sign up'
signed_up: 'You have signed up successfully.'
unlocks:
link: "Didn't receive unlock instructions?"
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
@@ -27,3 +30,4 @@ en:
confirmation_instructions: 'Confirmation instructions'
reset_password_instructions: 'Reset password instructions'
unlock_instructions: 'Unlock Instructions'

View File

@@ -129,7 +129,7 @@ module Devise
# Configure default path names, allowing the user overwrite defaults by
# passing a hash in :path_names.
def setup_path_names
[:sign_in, :sign_out, :password, :confirmation].each do |path_name|
[:sign_in, :sign_out, :password, :confirmation, :registration, :unlock].each do |path_name|
@path_names[path_name] ||= path_name.to_s
end
end

View File

@@ -0,0 +1,8 @@
module Devise
module Models
# Registerable is responsible for everything related to registering a new
# resource (ie account or sign up).
module Registerable
end
end
end

View File

@@ -105,10 +105,6 @@ module ActionController::Routing
end
end
def recoverable(routes, mapping)
routes.resource :password, :only => [:new, :create, :edit, :update], :as => mapping.path_names[:password]
end
def confirmable(routes, mapping)
routes.resource :confirmation, :only => [:new, :create, :show], :as => mapping.path_names[:confirmation]
end
@@ -117,6 +113,13 @@ module ActionController::Routing
routes.resource :unlock, :only => [:new, :create, :show], :as => mapping.path_names[:unlock]
end
def recoverable(routes, mapping)
routes.resource :password, :only => [:new, :create, :edit, :update], :as => mapping.path_names[:password]
end
def registerable(routes, mapping)
routes.resource :registration, :only => [:new, :create], :as => mapping.path_names[:registration]
end
end
end
end

View File

@@ -0,0 +1,58 @@
require 'test/test_helper'
class RegistrationTest < ActionController::IntegrationTest
test 'a guest admin should be able to sign in successfully' do
visit new_admin_session_path
click_link 'Sign up'
assert_template 'registrations/new'
fill_in 'email', :with => 'new_user@test.com'
fill_in 'password', :with => 'new_user123'
fill_in 'password confirmation', :with => 'new_user123'
click_button 'Register'
assert_contain 'You have signed up successfully.'
assert warden.authenticated?(:admin)
admin = Admin.last
assert_equal admin.email, 'new_user@test.com'
end
test 'a guest user should be able to sign up successfully and be blocked by confirmation' do
visit new_user_session_path
click_link 'Sign up'
assert_template 'registrations/new'
fill_in 'email', :with => 'new_user@test.com'
fill_in 'password', :with => 'new_user123'
fill_in 'password confirmation', :with => 'new_user123'
click_button 'Register'
follow_redirect!
assert_contain 'You have to confirm your account before continuing.'
assert_not warden.authenticated?(:user)
user = User.last
assert_equal user.email, 'new_user@test.com'
end
test 'a guest user cannot sign up with invalid information' do
visit new_user_session_path
click_link 'Sign up'
fill_in 'email', :with => 'invalid_email'
fill_in 'password', :with => 'new_user123'
fill_in 'password confirmation', :with => 'new_user321'
click_button 'Register'
assert_template 'registrations/new'
assert_have_selector '#errorExplanation'
assert_contain "Email is invalid"
assert_contain "Password doesn't match confirmation"
assert_nil User.first
end
end

View File

@@ -67,6 +67,8 @@ class MappingTest < ActiveSupport::TestCase
assert_equal 'sign_out', mapping.path_names[:sign_out]
assert_equal 'password', mapping.path_names[:password]
assert_equal 'confirmation', mapping.path_names[:confirmation]
assert_equal 'registration', mapping.path_names[:registration]
assert_equal 'unlock', mapping.path_names[:unlock]
end
test 'allow custom path names to be given' do
@@ -75,6 +77,8 @@ class MappingTest < ActiveSupport::TestCase
assert_equal 'logout', mapping.path_names[:sign_out]
assert_equal 'secret', mapping.path_names[:password]
assert_equal 'verification', mapping.path_names[:confirmation]
assert_equal 'sign_up', mapping.path_names[:registration]
assert_equal 'unblock', mapping.path_names[:unlock]
end
test 'has an empty path as default path prefix' do
@@ -86,7 +90,7 @@ class MappingTest < ActiveSupport::TestCase
mapping = Devise.mappings[:manager]
assert_equal '/:locale/', mapping.path_prefix
end
test 'retrieve as from the proper position' do
assert_equal 1, Devise.mappings[:user].as_position
assert_equal 2, Devise.mappings[:manager].as_position
@@ -96,13 +100,13 @@ class MappingTest < ActiveSupport::TestCase
assert_equal '/users', Devise.mappings[:user].raw_path
assert_equal '/:locale/accounts', Devise.mappings[:manager].raw_path
end
test 'raw path ignores the relative_url_root' do
swap ActionController::Base, :relative_url_root => "/abc" do
assert_equal '/users', Devise.mappings[:user].raw_path
end
end
test 'parsed path is returned' do
begin
Devise.default_url_options {{ :locale => I18n.locale }}
@@ -112,7 +116,7 @@ class MappingTest < ActiveSupport::TestCase
Devise.default_url_options {{ }}
end
end
test 'parsed path adds in the relative_url_root' do
swap ActionController::Base, :relative_url_root => '/abc' do
assert_equal '/abc/users', Devise.mappings[:user].parsed_path

View File

@@ -23,7 +23,7 @@ class ActiveRecordTest < ActiveSupport::TestCase
end
test 'add modules cherry pick' do
assert_include_modules Admin, :authenticatable, :timeoutable
assert_include_modules Admin, :authenticatable, :registerable, :timeoutable
end
test 'set a default value for stretches' do

View File

@@ -1,5 +1,5 @@
class Admin < ActiveRecord::Base
devise :authenticatable, :timeoutable
devise :authenticatable, :registerable, :timeoutable
def self.find_for_authentication(conditions)
last(:conditions => conditions)

View File

@@ -1,5 +1,7 @@
class User < ActiveRecord::Base
devise :authenticatable, :confirmable, :recoverable, :rememberable, :trackable,
:validatable, :timeoutable, :lockable, :token_authenticatable
devise :authenticatable, :confirmable, :lockable, :recoverable,
:registerable, :rememberable, :timeoutable, :token_authenticatable,
:trackable, :validatable
attr_accessible :username, :email, :password, :password_confirmation
end

View File

@@ -3,8 +3,9 @@ ActionController::Routing::Routes.draw do |map|
map.devise_for :admin, :as => 'admin_area'
map.devise_for :accounts, :scope => 'manager', :path_prefix => ':locale',
:class_name => "User", :requirements => { :extra => 'value' }, :path_names => {
:sign_in => 'login', :sign_out => 'logout', :password => 'secret',
:confirmation => 'verification', :unlock => 'unblock'
:sign_in => 'login', :sign_out => 'logout',
:password => 'secret', :confirmation => 'verification',
:unlock => 'unblock', :registration => 'sign_up'
}
map.resources :users, :only => [:index], :member => { :expire => :get }

View File

@@ -42,6 +42,26 @@ class MapRoutingTest < ActionController::TestCase
assert_recognizes({:controller => 'passwords', :action => 'update'}, {:path => 'users/password', :method => :put})
end
test 'map new user unlock' do
assert_recognizes({:controller => 'unlocks', :action => 'new'}, 'users/unlock/new')
end
test 'map create user unlock' do
assert_recognizes({:controller => 'unlocks', :action => 'create'}, {:path => 'users/unlock', :method => :post})
end
test 'map show user unlock' do
assert_recognizes({:controller => 'unlocks', :action => 'show'}, {:path => 'users/unlock', :method => :get})
end
test 'map new user registration' do
assert_recognizes({:controller => 'registrations', :action => 'new'}, 'users/registration/new')
end
test 'map create user registration' do
assert_recognizes({:controller => 'registrations', :action => 'create'}, {:path => 'users/registration', :method => :post})
end
test 'map admin session with :as option' do
assert_recognizes({:controller => 'sessions', :action => 'new'}, {:path => 'admin_area/sign_in', :method => :get})
end
@@ -72,4 +92,7 @@ class MapRoutingTest < ActionController::TestCase
assert_recognizes({:controller => 'unlocks', :action => 'new', :locale => 'en', :extra => 'value'}, '/en/accounts/unblock/new')
end
test 'map account with custom path name for registration' do
assert_recognizes({:controller => 'registrations', :action => 'new', :locale => 'en', :extra => 'value'}, '/en/accounts/sign_up/new')
end
end