diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index ba7b398b..b036f396 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -2,9 +2,6 @@ * Rememberable module allows user to be remembered across browsers and is enabled by default (by github.com/trevorturk) * devise_for can now be used together with scope method in routes but with a few limitations (check the documentation) -* deprecation - * :path_prefix option in devise_for is no longer supported - * bug fix * Fix a bug in Devise::TestHelpers where current_user was returning a Response object for non active accounts * Devise should respect script_name and path_info contracts diff --git a/lib/devise/mapping.rb b/lib/devise/mapping.rb index 2f71f666..c24fc7e7 100644 --- a/lib/devise/mapping.rb +++ b/lib/devise/mapping.rb @@ -56,23 +56,13 @@ module Devise end def initialize(name, options) #:nodoc: - if as = options.delete(:as) - ActiveSupport::Deprecation.warn ":as is deprecated, please use :path instead." - options[:path] ||= as - end - - if scope = options.delete(:scope) - ActiveSupport::Deprecation.warn ":scope is deprecated, please use :singular instead." - options[:singular] ||= scope - end - - @plural = name.to_sym - @path = (options.delete(:path) || name).to_sym - @singular = (options.delete(:singular) || name.to_s.singularize).to_sym + @plural = (options[:as] ? "#{options.delete(:as)}_#{name}" : name).to_sym + @singular = (options.delete(:singular) || @plural.to_s.singularize).to_sym @class_name = (options.delete(:class_name) || name.to_s.classify).to_s @ref = ActiveSupport::Dependencies.ref(@class_name) + @path = (options.delete(:path) || name).to_sym @path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/") if @path_prefix =~ /\(.*\)/ && Devise.ignore_optional_segments != true @@ -81,7 +71,8 @@ module Devise "what you are doing, you can set config.ignore_optional_segments = true in your devise initializer." end - @controllers = Hash.new { |h,k| h[k] = "devise/#{k}" } + mod = options.delete(:module) || "devise" + @controllers = Hash.new { |h,k| h[k] = "#{mod}/#{k}" } @controllers.merge!(options.delete(:controllers) || {}) @path_names = Hash.new { |h,k| h[k] = k.to_s } @@ -117,11 +108,16 @@ module Devise end end - # Return in which position in the path prefix devise should find the as mapping. + # Returns in which position in the path prefix devise should find the as mapping. def segment_position self.path_prefix.count("/") end + # Returns fullpath for route generation. + def fullpath + @path_prefix + @path.to_s + end + def authenticatable? @authenticatable ||= self.modules.any? { |m| m.to_s =~ /authenticatable/ } end diff --git a/lib/devise/rails/routes.rb b/lib/devise/rails/routes.rb index dd2433fd..97054951 100644 --- a/lib/devise/rails/routes.rb +++ b/lib/devise/rails/routes.rb @@ -98,13 +98,6 @@ module ActionDispatch::Routing # end # end # - # Second, since Devise expects routes in the format "user_session_path" to be defined, you cannot - # scope to a given route name as below: - # - # scope "/special_scope", :as => :special_scope do # THIS WILL FAIL - # devise_for :users - # end - # # Finally, Devise does not (and cannot) support optional segments, either static or dynamic. That # said, the following does not work: # @@ -115,12 +108,21 @@ module ActionDispatch::Routing def devise_for(*resources) options = resources.extract_options! - if options.key?(:path_prefix) - ActiveSupport::Deprecation.warn "Giving :path_prefix to devise_for is deprecated and has no effect. " << - "Please use scope from the new router DSL instead." + if as = options.delete(:as) + ActiveSupport::Deprecation.warn ":as is deprecated, please use :path instead." + options[:path] ||= as end - options[:path_prefix] = @scope[:path] + if scope = options.delete(:scope) + ActiveSupport::Deprecation.warn ":scope is deprecated, please use :singular instead." + options[:singular] ||= scope + end + + options[:as] ||= @scope[:as] if @scope[:as].present? + options[:module] ||= @scope[:module] if @scope[:module].present? + options[:path_prefix] ||= @scope[:path] if @scope[:path].present? + options[:path_names] = (@scope[:path_names] || {}).merge(options[:path_names] || {}) + resources.map!(&:to_sym) resources.each do |resource| @@ -141,7 +143,7 @@ module ActionDispatch::Routing routes = mapping.routes routes -= Array(options.delete(:skip)).map { |s| s.to_s.singularize.to_sym } - scope mapping.path.to_s, :as => mapping.name do + with_devise_scope mapping.fullpath, mapping.name do routes.each { |mod| send(:"devise_#{mod}", mapping, mapping.controllers) } end end @@ -193,6 +195,14 @@ module ActionDispatch::Routing :path_names => { :new => mapping.path_names[:sign_up] }, :controller => controllers[:registrations] end + def with_devise_scope(new_path, new_as) + old_as, old_path, old_module = @scope[:as], @scope[:path], @scope[:module] + @scope[:as], @scope[:path], @scope[:module] = new_as, new_path, nil + yield + ensure + @scope[:as], @scope[:path], @scope[:module] = old_as, old_path, old_module + end + def raise_no_devise_method_error!(klass) raise "#{klass} does not respond to 'devise' method. This usually means you haven't " << "loaded your ORM file or it's being loaded too late. To fix it, be sure to require 'devise/orm/YOUR_ORM' " << diff --git a/test/controllers/url_helpers_test.rb b/test/controllers/url_helpers_test.rb index 1fad5be0..00ff5b82 100644 --- a/test/controllers/url_helpers_test.rb +++ b/test/controllers/url_helpers_test.rb @@ -44,4 +44,15 @@ class RoutesTest < ActionController::TestCase assert_path_and_url :confirmation assert_path_and_url :confirmation, :new end + + test 'should alias unlock to mapped user unlock' do + assert_path_and_url :unlock + assert_path_and_url :unlock, :new + end + + test 'should alias registration to mapped user registration' do + assert_path_and_url :registration + assert_path_and_url :registration, :new + assert_path_and_url :registration, :edit + end end diff --git a/test/rails_app/app/controllers/publisher/registrations_controller.rb b/test/rails_app/app/controllers/publisher/registrations_controller.rb new file mode 100644 index 00000000..daf5e7fa --- /dev/null +++ b/test/rails_app/app/controllers/publisher/registrations_controller.rb @@ -0,0 +1,2 @@ +class Publisher::RegistrationsController < ApplicationController +end \ No newline at end of file diff --git a/test/rails_app/app/controllers/publisher/sessions_controller.rb b/test/rails_app/app/controllers/publisher/sessions_controller.rb new file mode 100644 index 00000000..03e106d7 --- /dev/null +++ b/test/rails_app/app/controllers/publisher/sessions_controller.rb @@ -0,0 +1,2 @@ +class Publisher::SessionsController < ApplicationController +end \ No newline at end of file diff --git a/test/rails_app/config/routes.rb b/test/rails_app/config/routes.rb index 31478e8d..739dcaa2 100644 --- a/test/rails_app/config/routes.rb +++ b/test/rails_app/config/routes.rb @@ -9,6 +9,10 @@ Rails.application.routes.draw do devise_for :users devise_for :admin, :path => "admin_area", :controllers => { :sessions => "sessions" }, :skip => :passwords + namespace :publisher, :path_names => { :sign_in => "i_don_care", :sign_out => "get_out" } do + devise_for :accounts, :class_name => "User", :path_names => { :sign_in => "get_in" } + end + scope ":locale" do devise_for :accounts, :singular => "manager", :class_name => "User", :path_names => { diff --git a/test/routes_test.rb b/test/routes_test.rb index 86d1e1e5..e5d1ac27 100644 --- a/test/routes_test.rb +++ b/test/routes_test.rb @@ -1,25 +1,29 @@ require 'test_helper' -class MapRoutingTest < ActionController::TestCase - +class DefaultRoutingTest < ActionController::TestCase test 'map new user session' do assert_recognizes({:controller => 'devise/sessions', :action => 'new'}, {:path => 'users/sign_in', :method => :get}) + assert_named_route "/users/sign_in", :new_user_session_path end test 'map create user session' do assert_recognizes({:controller => 'devise/sessions', :action => 'create'}, {:path => 'users/sign_in', :method => :post}) + assert_named_route "/users/sign_in", :user_session_path end test 'map destroy user session' do assert_recognizes({:controller => 'devise/sessions', :action => 'destroy'}, {:path => 'users/sign_out', :method => :get}) + assert_named_route "/users/sign_out", :destroy_user_session_path end test 'map new user confirmation' do assert_recognizes({:controller => 'devise/confirmations', :action => 'new'}, 'users/confirmation/new') + assert_named_route "/users/confirmation/new", :new_user_confirmation_path end test 'map create user confirmation' do assert_recognizes({:controller => 'devise/confirmations', :action => 'create'}, {:path => 'users/confirmation', :method => :post}) + assert_named_route "/users/confirmation", :user_confirmation_path end test 'map show user confirmation' do @@ -28,14 +32,17 @@ class MapRoutingTest < ActionController::TestCase test 'map new user password' do assert_recognizes({:controller => 'devise/passwords', :action => 'new'}, 'users/password/new') + assert_named_route "/users/password/new", :new_user_password_path end test 'map create user password' do assert_recognizes({:controller => 'devise/passwords', :action => 'create'}, {:path => 'users/password', :method => :post}) + assert_named_route "/users/password", :user_password_path end test 'map edit user password' do assert_recognizes({:controller => 'devise/passwords', :action => 'edit'}, 'users/password/edit') + assert_named_route "/users/password/edit", :edit_user_password_path end test 'map update user password' do @@ -44,10 +51,12 @@ class MapRoutingTest < ActionController::TestCase test 'map new user unlock' do assert_recognizes({:controller => 'devise/unlocks', :action => 'new'}, 'users/unlock/new') + assert_named_route "/users/unlock/new", :new_user_unlock_path end test 'map create user unlock' do assert_recognizes({:controller => 'devise/unlocks', :action => 'create'}, {:path => 'users/unlock', :method => :post}) + assert_named_route "/users/unlock", :user_unlock_path end test 'map show user unlock' do @@ -56,14 +65,17 @@ class MapRoutingTest < ActionController::TestCase test 'map new user registration' do assert_recognizes({:controller => 'devise/registrations', :action => 'new'}, 'users/sign_up') + assert_named_route "/users/sign_up", :new_user_registration_path end test 'map create user registration' do assert_recognizes({:controller => 'devise/registrations', :action => 'create'}, {:path => 'users', :method => :post}) + assert_named_route "/users", :user_registration_path end test 'map edit user registration' do assert_recognizes({:controller => 'devise/registrations', :action => 'edit'}, {:path => 'users/edit', :method => :get}) + assert_named_route "/users/edit", :edit_user_registration_path end test 'map update user registration' do @@ -74,6 +86,14 @@ class MapRoutingTest < ActionController::TestCase assert_recognizes({:controller => 'devise/registrations', :action => 'destroy'}, {:path => 'users', :method => :delete}) end + protected + + def assert_named_route(result, name) + assert_equal result, @routes.url_helpers.send(name) + end +end + +class CustomizedRoutingTest < ActionController::TestCase test 'map admin with :path option' do assert_recognizes({:controller => 'devise/registrations', :action => 'new'}, {:path => 'admin_area/sign_up', :method => :get}) end @@ -112,3 +132,15 @@ class MapRoutingTest < ActionController::TestCase assert_recognizes({:controller => 'devise/registrations', :action => 'new', :locale => 'en'}, '/en/accounts/management/register') end end + +class ScopedRoutingTest < ActionController::TestCase + test 'map publisher account' do + assert_recognizes({:controller => 'publisher/registrations', :action => 'new'}, {:path => '/publisher/accounts/sign_up', :method => :get}) + assert_equal '/publisher/accounts/sign_up', @routes.url_helpers.new_publisher_account_registration_path + end + + test 'map publisher account merges path names' do + assert_recognizes({:controller => 'publisher/sessions', :action => 'new'}, {:path => '/publisher/accounts/get_in', :method => :get}) + assert_equal '/publisher/accounts/get_in', @routes.url_helpers.new_publisher_account_session_path + end +end