diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index b02f1985..45d305c6 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,17 +1,11 @@ class SessionsController < ApplicationController include Devise::Controllers::Helpers - # Maps the messages types that comes from warden to a flash type. - WARDEN_MESSAGES = { - :unauthenticated => :success, - :unconfirmed => :failure - } - before_filter :require_no_authentication, :only => [ :new, :create ] # GET /resource/sign_in def new - WARDEN_MESSAGES.each do |message, type| + Devise::FLASH_MESSAGES.each do |message, type| set_now_flash_message type, message if params.key?(message) end build_resource diff --git a/lib/devise.rb b/lib/devise.rb index c3c6644c..d1a4d0ff 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -11,9 +11,19 @@ module Devise STRATEGIES = [:rememberable, :authenticatable].freeze TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].freeze + # Maps the messages types that comes from warden to a flash type. + FLASH_MESSAGES = { + :unauthenticated => :success, + :unconfirmed => :failure + } + # Models configuration mattr_accessor :pepper, :stretches, :remember_for, :confirm_within + # Mappings + mattr_accessor :mappings + self.mappings = {} + class << self # Default way to setup Devise. Run script/generate devise_install to create # a fresh initializer with all configuration values. @@ -56,5 +66,4 @@ module Devise end require 'devise/warden' -require 'devise/mapping' require 'devise/rails' diff --git a/lib/devise/controllers/helpers.rb b/lib/devise/controllers/helpers.rb index ce8ae09d..7437f164 100644 --- a/lib/devise/controllers/helpers.rb +++ b/lib/devise/controllers/helpers.rb @@ -31,7 +31,7 @@ module Devise # Attempt to find the mapped route for devise based on request path def devise_mapping - @devise_mapping ||= Devise.find_mapping_by_path(request.path) + @devise_mapping ||= Devise::Mapping.find_by_path(request.path) end protected diff --git a/lib/devise/failure.rb b/lib/devise/failure.rb index 3d4ebcb9..cd2f35a9 100644 --- a/lib/devise/failure.rb +++ b/lib/devise/failure.rb @@ -19,7 +19,7 @@ module Devise end redirect_path = if mapping = Devise.mappings[scope] - "/#{mapping.as}/#{mapping.path_names[:sign_in]}" + "#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}" else "/#{default_url}" end diff --git a/lib/devise/mapping.rb b/lib/devise/mapping.rb index 21e50694..9a6083b4 100644 --- a/lib/devise/mapping.rb +++ b/lib/devise/mapping.rb @@ -22,13 +22,28 @@ module Devise # # is the modules included in the class # class Mapping #:nodoc: - attr_reader :name, :as, :path_names + attr_reader :name, :as, :path_names, :path_prefix + + # Loop through all mappings looking for a map that matches with the requested + # path (ie /users/sign_in). If a path prefix is given, it's taken into account. + def self.find_by_path(path) + Devise.mappings.each_value do |mapping| + route = path.split("/")[mapping.as_position] + return mapping if mapping.as == route.to_sym + end + nil + end def initialize(name, options) + options.assert_valid_keys(:class_name, :as, :path_names, :singular, :path_prefix) + @as = (options[:as] || name).to_sym @klass = (options[:class_name] || name.to_s.classify).to_s @name = (options[:singular] || name.to_s.singularize).to_sym - @path_names = options[:path_names] || {} + @path_names = options[:path_names] || {} + @path_prefix = options[:path_prefix] || "" + @path_prefix << "/" unless @path_prefix[-1] == ?/ + setup_path_names end @@ -50,6 +65,23 @@ module Devise self.for.include?(CONTROLLERS[controller.to_sym]) end + # Return in which position in the path prefix devise should find the as mapping. + def as_position + self.path_prefix.count("/") + end + + # Returns the raw path using path_prefix and as. + def raw_path + path_prefix + as.to_s + end + + # Returns the parsed path. If you need meta information in your path_prefix, + # you should overwrite this method to use it. The only information supported + # by default is I18n.locale. + def parsed_path + raw_path.gsub(":locale", I18n.locale.to_s) + end + # Create magic predicates for verifying what module is activated by this map. # Example: # @@ -75,21 +107,4 @@ module Devise end end end - - mattr_accessor :mappings - self.mappings = {} - - # Loop through all mappings looking for a map that matches with the requested - # path (ie /users/sign_in). The important part here is the key :users. If no - # map is found just returns nil. - def self.find_mapping_by_path(path) - route = path.split("/")[1] - return nil unless route - - route = route.to_sym - mappings.each do |key, map| - return map if map.as == route.to_sym - end - nil - end end diff --git a/lib/devise/rails/routes.rb b/lib/devise/rails/routes.rb index 0c6d2633..eb5dc06f 100644 --- a/lib/devise/rails/routes.rb +++ b/lib/devise/rails/routes.rb @@ -60,40 +60,42 @@ module ActionController::Routing # # map.devise_for :users, :path_names => { :sign_in => 'login', :sign_out => 'logout', :password => 'secret', :confirmation => 'verification' } # + # * :path_prefix => the path prefix to be used in all routes. Only :locale is supported as dynamic prefix: + # + # map.devise_for :users, :path_prefix => "/:locale" + # def devise_for(*resources) options = resources.extract_options! resources.map!(&:to_sym) - options.assert_valid_keys(:class_name, :as, :path_names, :singular) - resources.each do |resource| mapping = Devise::Mapping.new(resource, options) Devise.mappings[mapping.name] = mapping - mapping.for.each do |strategy| - send(strategy, mapping) if self.respond_to?(strategy, true) + with_options(:path_prefix => mapping.raw_path, :name_prefix => "#{mapping.name}_") do |routes| + mapping.for.each do |strategy| + send(strategy, routes, mapping) if self.respond_to?(strategy, true) + end end end end protected - def authenticatable(mapping) - with_options(:controller => 'sessions', :path_prefix => mapping.as) do |session| + def authenticatable(routes, mapping) + routes.with_options(:controller => 'sessions', :name_prefix => nil) do |session| session.send(:"new_#{mapping.name}_session", mapping.path_names[:sign_in], :action => 'new', :conditions => { :method => :get }) session.send(:"#{mapping.name}_session", mapping.path_names[:sign_in], :action => 'create', :conditions => { :method => :post }) session.send(:"destroy_#{mapping.name}_session", mapping.path_names[:sign_out], :action => 'destroy', :conditions => { :method => :get }) end end - def recoverable(mapping) - resource :password, :only => [:new, :create, :edit, :update], :as => mapping.path_names[:password], - :path_prefix => mapping.as, :name_prefix => "#{mapping.name}_" + def recoverable(routes, mapping) + routes.resource :password, :only => [:new, :create, :edit, :update], :as => mapping.path_names[:password] end - def confirmable(mapping) - resource :confirmation, :only => [:new, :create, :show], :as => mapping.path_names[:confirmation], - :path_prefix => mapping.as, :name_prefix => "#{mapping.name}_" + def confirmable(routes, mapping) + routes.resource :confirmation, :only => [:new, :create, :show], :as => mapping.path_names[:confirmation] end end diff --git a/test/mapping_test.rb b/test/mapping_test.rb index 58d8658d..d45d7c3f 100644 --- a/test/mapping_test.rb +++ b/test/mapping_test.rb @@ -31,12 +31,12 @@ class MappingTest < ActiveSupport::TestCase end test 'return mapping by path' do - assert_nil Devise.find_mapping_by_path("/foo/bar") - assert_equal Devise.mappings[:user], Devise.find_mapping_by_path("/users/session") + assert_nil Devise::Mapping.find_by_path("/foo/bar") + assert_equal Devise.mappings[:user], Devise::Mapping.find_by_path("/users/session") end test 'return mapping by customized path' do - assert_equal Devise.mappings[:admin], Devise.find_mapping_by_path("/admin_area/session") + assert_equal Devise.mappings[:admin], Devise::Mapping.find_by_path("/admin_area/session") end test 'return default path names' do @@ -55,6 +55,31 @@ class MappingTest < ActiveSupport::TestCase assert_equal 'verification', mapping.path_names[:confirmation] end + test 'has an empty path as default path prefix' do + mapping = Devise.mappings[:account] + assert_equal '/', mapping.path_prefix + end + + test 'allow path prefix to be configured' do + mapping = Devise.mappings[:manager] + assert_equal '/:locale/', mapping.path_prefix + end + + test 'retrieve as from the proper position' do + assert_equal 1, Devise.mappings[:account].as_position + assert_equal 2, Devise.mappings[:manager].as_position + end + + test 'raw path is returned' do + assert_equal '/account', Devise.mappings[:account].raw_path + assert_equal '/:locale/organizers', Devise.mappings[:manager].raw_path + end + + test 'parsed path is returned' do + assert_equal '/account', Devise.mappings[:account].parsed_path + assert_equal '/en/organizers', Devise.mappings[:manager].parsed_path + end + test 'magic predicates' do mapping = Devise.mappings[:user] assert mapping.authenticatable? diff --git a/test/rails_app/config/routes.rb b/test/rails_app/config/routes.rb index 34c41c95..00d75f28 100644 --- a/test/rails_app/config/routes.rb +++ b/test/rails_app/config/routes.rb @@ -4,7 +4,7 @@ ActionController::Routing::Routes.draw do |map| map.devise_for :account, :path_names => { :sign_in => 'login', :sign_out => 'logout', :password => 'secret', :confirmation => 'verification' } - map.devise_for :organizers, :singular => 'manager' + map.devise_for :organizers, :singular => 'manager', :path_prefix => '/:locale' map.resources :users, :only => :index map.resources :admins, :only => :index diff --git a/test/routes_test.rb b/test/routes_test.rb index 6e1d41c4..0b1ce6cc 100644 --- a/test/routes_test.rb +++ b/test/routes_test.rb @@ -69,7 +69,11 @@ class MapRoutingTest < ActionController::TestCase end test 'map organizer with custom singular name' do - assert_recognizes({:controller => 'passwords', :action => 'new'}, 'organizers/password/new') + assert_recognizes({:controller => 'passwords', :action => 'new', :locale => "en"}, '/en/organizers/password/new') + end + + test 'map organizer with path prefix' do + assert_recognizes({:controller => 'sessions', :action => 'new', :locale => "en"}, '/en/organizers/sign_in') end end