From 05fc5712e35407366c08b1adafad7f6547f1e381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 15 Nov 2009 12:19:16 -0200 Subject: [PATCH] Added a couple of helpers to help sign up and delete account tasks. --- CHANGELOG.rdoc | 4 +++ lib/devise/controllers/filters.rb | 50 +++++++++++++++++++++++++++---- lib/devise/controllers/helpers.rb | 13 +------- lib/devise/mapping.rb | 12 ++++++++ test/controllers/filters_test.rb | 49 +++++++++++++++++++++++++----- 5 files changed, 102 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index cd9ab3b5..bb4e6f75 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -1,3 +1,7 @@ +* enhancements + * [#28] Improved sign_in and sign_out helpers to accepts resources + * [#28] Added stored_location_for as a helper + == 0.5.1 * enhancements diff --git a/lib/devise/controllers/filters.rb b/lib/devise/controllers/filters.rb index e6787be3..16ffa436 100644 --- a/lib/devise/controllers/filters.rb +++ b/lib/devise/controllers/filters.rb @@ -49,17 +49,45 @@ module Devise warden.authenticated?(scope) end - # Set the warden user with the scope, signing in the resource automatically, - # without running hooks. - def sign_in(scope, resource) + # Sign in an user that already was authenticated. This helper is useful for logging + # users in after sign up. + # + # Examples: + # + # sign_in :user, @user # sign_in(scope, resource) + # sign_in @user # sign_in(resource) + # + def sign_in(resource_or_scope, resource=nil) + scope ||= find_devise_scope(resource_or_scope) + resource ||= resource_or_scope warden.set_user(resource, :scope => scope) end - # Sign out based on scope. - def sign_out(scope, *args) + # Sign out a given user or scope. This helper is useful for signing out an user + # after deleting accounts. + # + # Examples: + # + # sign_out :user # sign_out(scope) + # sign_out @user # sign_out(resource) + # + def sign_out(resource_or_scope) + scope = find_devise_scope(resource_or_scope) warden.user(scope) # Without loading user here, before_logout hook is not called warden.raw_session.inspect # Without this inspect here. The session does not clear. - warden.logout(scope, *args) + warden.logout(scope) + end + + # Returns and delete the url stored in the session for the given scope. Useful + # for giving redirect backs after sign up: + # + # Example: + # + # redirect_to stored_location_for(:user) || root_path + # + def stored_location_for(resource_or_scope) + scope = find_devise_scope(resource_or_scope) + session.delete(:"#{scope}.return_to") end # Define authentication filters and accessor helpers based on mappings. @@ -106,6 +134,16 @@ module Devise METHODS end + protected + + def find_devise_scope(resource_or_scope) + if resource_or_scope.is_a?(Symbol) + resource_or_scope + else + Devise::Mapping.find_by_class!(resource_or_scope.class).name + end + end + end end end diff --git a/lib/devise/controllers/helpers.rb b/lib/devise/controllers/helpers.rb index c28c8959..bda0211c 100644 --- a/lib/devise/controllers/helpers.rb +++ b/lib/devise/controllers/helpers.rb @@ -45,18 +45,7 @@ module Devise # Redirects to stored uri before signing in or the default path and clear # return to. def redirect_back_or_to(default) - redirect_to(return_to || default) - clear_return_to - end - - # Access to scoped stored uri - def return_to - session[:"#{resource_name}.return_to"] - end - - # Clear scoped stored uri - def clear_return_to - session[:"#{resource_name}.return_to"] = nil + redirect_to(stored_location_for(resource_name) || default) end # Checks for the existence of the resource root path. If it exists, diff --git a/lib/devise/mapping.rb b/lib/devise/mapping.rb index af5499c5..3e3d94dd 100644 --- a/lib/devise/mapping.rb +++ b/lib/devise/mapping.rb @@ -34,6 +34,18 @@ module Devise nil end + # Find a mapping by a given class. It takes into account single table inheritance as well. + def self.find_by_class(klass) + Devise.mappings.values.find { |m| return m if klass <= m.to } + end + + # Find by class but raising an error in case it can't be found. + def self.find_by_class!(klass) + mapping = find_by_class(klass) + raise "Could not find a valid mapping for #{klass}" unless mapping + mapping + end + # Default url options which can be used as prefix. def self.default_url_options {} diff --git a/test/controllers/filters_test.rb b/test/controllers/filters_test.rb index 0674da3d..8754566e 100644 --- a/test/controllers/filters_test.rb +++ b/test/controllers/filters_test.rb @@ -14,11 +14,13 @@ class MockController < ApplicationController end class ControllerAuthenticableTest < ActionController::TestCase + tests MockController def setup @controller = MockController.new @mock_warden = OpenStruct.new @controller.env = { 'warden' => @mock_warden } + @controller.session = {} end test 'setup warden' do @@ -47,12 +49,6 @@ class ControllerAuthenticableTest < ActionController::TestCase @controller.current_user end - test 'proxy logout to warden' do - @mock_warden.expects(:user).with(:user).returns(true) - @mock_warden.expects(:logout).with(:user).returns(true) - @controller.sign_out(:user) - end - test 'proxy user_authenticate! to authenticate with user scope' do @mock_warden.expects(:authenticate!).with(:scope => :user) @controller.authenticate_user! @@ -83,11 +79,48 @@ class ControllerAuthenticableTest < ActionController::TestCase @controller.admin_session end - test 'sign in automatically proxy to set user on warden' do - @mock_warden.expects(:set_user).with(user = mock, :scope => :user).returns(true) + test 'sign in proxy to set_user on warden' do + user = User.new + @mock_warden.expects(:set_user).with(user, :scope => :user).returns(true) @controller.sign_in(:user, user) end + test 'sign in accepts a resource as argument' do + user = User.new + @mock_warden.expects(:set_user).with(user, :scope => :user).returns(true) + @controller.sign_in(user) + end + + test 'sign out proxy to logout on warden' do + @mock_warden.expects(:user).with(:user).returns(true) + @mock_warden.expects(:logout).with(:user).returns(true) + @controller.sign_out(:user) + end + + test 'sign out accepts a resource as argument' do + @mock_warden.expects(:user).with(:user).returns(true) + @mock_warden.expects(:logout).with(:user).returns(true) + @controller.sign_out(User.new) + end + + test 'stored location for returns the location for a given scope' do + assert_nil @controller.stored_location_for(:user) + @controller.session[:"user.return_to"] = "/foo.bar" + assert_equal "/foo.bar", @controller.stored_location_for(:user) + end + + test 'stored location for accepts a resource as argument' do + assert_nil @controller.stored_location_for(:user) + @controller.session[:"user.return_to"] = "/foo.bar" + assert_equal "/foo.bar", @controller.stored_location_for(User.new) + end + + test 'stored location cleans information after reading' do + @controller.session[:"user.return_to"] = "/foo.bar" + assert_equal "/foo.bar", @controller.stored_location_for(:user) + assert_nil @controller.session[:"user.return_to"] + end + test 'is not a devise controller' do assert_not @controller.devise_controller? end