diff options
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/CHANGELOG.md | 9 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/strong_parameters.rb | 5 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/cookies.rb | 196 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/remote_ip.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 10 | ||||
-rw-r--r-- | actionpack/lib/action_pack/version.rb | 13 | ||||
-rw-r--r-- | actionpack/test/abstract/callbacks_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/abstract/collector_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/routing_test.rb | 11 | ||||
-rw-r--r-- | actionpack/test/dispatch/cookies_test.rb | 55 | ||||
-rw-r--r-- | actionpack/test/dispatch/mount_test.rb | 2 | ||||
-rw-r--r-- | actionpack/test/dispatch/routing_test.rb | 37 |
12 files changed, 209 insertions, 135 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 0c6973f9b6..52e0f68279 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,14 @@ ## Rails 4.0.0 (unreleased) ## +* Create `UpgradeLegacySignedCookieJar` to transparently upgrade existing signed + cookies generated by Rails 3.x to avoid invalidating them when upgrading to Rails 4.x. + + *Trevor Turk + Neeraj Singh* + +* Raise an `ArgumentError` when a clashing named route is defined. + + *Trevor Turk* + * Allow default url options to accept host with protocol such as `http://` config.action_mailer.default_url_options = { host: "http://mydomain.com" } diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index acad8a0799..23d70c9ea2 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/array/wrap' require 'active_support/rescuable' require 'action_dispatch/http/upload' +require 'stringio' module ActionController # Raised when a required parameter is missing. @@ -68,6 +69,8 @@ module ActionController # ActionController::UnpermittedParameters exception. The default value is <tt>:log</tt> # in test and development environments, +false+ otherwise. # + # Examples: + # # params = ActionController::Parameters.new # params.permitted? # => false # @@ -418,7 +421,7 @@ module ActionController # Declaration { comment_ids: [] }. array_of_permitted_scalars_filter(params, key) else - # Declaration { user: :name } or { user: [:name, :age, { adress: ... }] }. + # Declaration { user: :name } or { user: [:name, :age, { address: ... }] }. params[key] = each_element(value) do |element| if element.is_a?(Hash) element = self.class.new(element) unless element.respond_to?(:permit) diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 36a0db6e61..f21d1d4ee5 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/hash/keys' require 'active_support/core_ext/module/attribute_accessors' +require 'active_support/core_ext/object/blank' require 'active_support/key_generator' require 'active_support/message_verifier' @@ -86,7 +87,8 @@ module ActionDispatch SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt".freeze ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt".freeze ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze - TOKEN_KEY = "action_dispatch.secret_token".freeze + SECRET_TOKEN = "action_dispatch.secret_token".freeze + SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze # Cookies can typically store 4096 bytes. MAX_COOKIE_SIZE = 4096 @@ -94,8 +96,68 @@ module ActionDispatch # Raised when storing more than 4K of session data. CookieOverflow = Class.new StandardError + # Include in a cookie jar to allow chaining, e.g. cookies.permanent.signed + module ChainedCookieJars + # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example: + # + # cookies.permanent[:prefers_open_id] = true + # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + # + # This jar is only meant for writing. You'll read permanent cookies through the regular accessor. + # + # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples: + # + # cookies.permanent.signed[:remember_me] = current_user.id + # # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + def permanent + @permanent ||= PermanentCookieJar.new(self, @key_generator, @options) + end + + # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from + # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed + # cookie was tampered with by the user (or a 3rd party), nil will be returned. + # + # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+. + # + # Example: + # + # cookies.signed[:discount] = 45 + # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ + # + # cookies.signed[:discount] # => 45 + def signed + @signed ||= begin + if @options[:upgrade_legacy_signed_cookie_jar] + UpgradeLegacySignedCookieJar.new(self, @key_generator, @options) + else + SignedCookieJar.new(self, @key_generator, @options) + end + end + end + + # Only needed for supporting the +UpgradeSignatureToEncryptionCookieStore+, users and plugin authors should not use this + def signed_using_old_secret #:nodoc: + @signed_using_old_secret ||= SignedCookieJar.new(self, ActiveSupport::DummyKeyGenerator.new(@options[:secret_token]), @options) + end + + # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read. + # If the cookie was tampered with by the user (or a 3rd party), nil will be returned. + # + # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+. + # + # Example: + # + # cookies.encrypted[:discount] = 45 + # # => Set-Cookie: discount=ZS9ZZ1R4cG1pcUJ1bm80anhQang3dz09LS1mbDZDSU5scGdOT3ltQ2dTdlhSdWpRPT0%3D--ab54663c9f4e3bc340c790d6d2b71e92f5b60315; path=/ + # + # cookies.encrypted[:discount] # => 45 + def encrypted + @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options) + end + end + class CookieJar #:nodoc: - include Enumerable + include Enumerable, ChainedCookieJars # This regular expression is used to split the levels of a domain. # The top level domain can be any string without a period or @@ -115,7 +177,10 @@ module ActionDispatch { signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '', encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '', encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '', - token_key: env[TOKEN_KEY] } + secret_token: env[SECRET_TOKEN], + secret_key_base: env[SECRET_KEY_BASE], + upgrade_legacy_signed_cookie_jar: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present? + } end def self.build(request) @@ -232,59 +297,6 @@ module ActionDispatch @cookies.each_key{ |k| delete(k, options) } end - # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example: - # - # cookies.permanent[:prefers_open_id] = true - # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT - # - # This jar is only meant for writing. You'll read permanent cookies through the regular accessor. - # - # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples: - # - # cookies.permanent.signed[:remember_me] = current_user.id - # # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT - def permanent - @permanent ||= PermanentCookieJar.new(self, @key_generator, @options) - end - - # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from - # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed - # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will - # be raised. - # - # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+. - # - # Example: - # - # cookies.signed[:discount] = 45 - # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ - # - # cookies.signed[:discount] # => 45 - def signed - @signed ||= SignedCookieJar.new(self, @key_generator, @options) - end - - # Only needed for supporting the +UpgradeSignatureToEncryptionCookieStore+, users and plugin authors should not use this - def signed_using_old_secret #:nodoc: - @signed_using_old_secret ||= SignedCookieJar.new(self, ActiveSupport::DummyKeyGenerator.new(@options[:token_key]), @options) - end - - # Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read. - # If the cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception - # will be raised. - # - # This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+. - # - # Example: - # - # cookies.encrypted[:discount] = 45 - # # => Set-Cookie: discount=ZS9ZZ1R4cG1pcUJ1bm80anhQang3dz09LS1mbDZDSU5scGdOT3ltQ2dTdlhSdWpRPT0%3D--ab54663c9f4e3bc340c790d6d2b71e92f5b60315; path=/ - # - # cookies.encrypted[:discount] # => 45 - def encrypted - @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options) - end - def write(headers) @set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) } @delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) } @@ -306,6 +318,8 @@ module ActionDispatch end class PermanentCookieJar #:nodoc: + include ChainedCookieJars + def initialize(parent_jar, key_generator, options = {}) @parent_jar = parent_jar @key_generator = key_generator @@ -326,26 +340,11 @@ module ActionDispatch options[:expires] = 20.years.from_now @parent_jar[key] = options end - - def permanent - @permanent ||= PermanentCookieJar.new(self, @key_generator, @options) - end - - def signed - @signed ||= SignedCookieJar.new(self, @key_generator, @options) - end - - def encrypted - @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options) - end - - def method_missing(method, *arguments, &block) - ActiveSupport::Deprecation.warn "#{method} is deprecated with no replacement. " + - "You probably want to try this method over the parent CookieJar." - end end class SignedCookieJar #:nodoc: + include ChainedCookieJars + def initialize(parent_jar, key_generator, options = {}) @parent_jar = parent_jar @options = options @@ -372,26 +371,42 @@ module ActionDispatch raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE @parent_jar[key] = options end + end - def permanent - @permanent ||= PermanentCookieJar.new(self, @key_generator, @options) + # UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if + # config.secret_token and config.secret_key_base are both set. It reads + # legacy cookies signed with the old dummy key generator and re-saves + # them using the new key generator to provide a smooth upgrade path. + class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc: + def initialize(*args) + super + @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token]) end - def signed - @signed ||= SignedCookieJar.new(self, @key_generator, @options) + def [](name) + if signed_message = @parent_jar[name] + verify_signed_message(signed_message) || verify_and_upgrade_legacy_signed_message(name, signed_message) + end end - def encrypted - @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options) + def verify_signed_message(signed_message) + @verifier.verify(signed_message) + rescue ActiveSupport::MessageVerifier::InvalidSignature + nil end - def method_missing(method, *arguments, &block) - ActiveSupport::Deprecation.warn "#{method} is deprecated with no replacement. " + - "You probably want to try this method over the parent CookieJar." + def verify_and_upgrade_legacy_signed_message(name, signed_message) + @legacy_verifier.verify(signed_message).tap do |value| + self[name] = value + end + rescue ActiveSupport::MessageVerifier::InvalidSignature + nil end end class EncryptedCookieJar #:nodoc: + include ChainedCookieJars + def initialize(parent_jar, key_generator, options = {}) if ActiveSupport::DummyKeyGenerator === key_generator raise "Encrypted Cookies must be used in conjunction with config.secret_key_base." + @@ -425,23 +440,6 @@ module ActionDispatch raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE @parent_jar[key] = options end - - def permanent - @permanent ||= PermanentCookieJar.new(self, @key_generator, @options) - end - - def signed - @signed ||= SignedCookieJar.new(self, @key_generator, @options) - end - - def encrypted - @encrypted ||= EncryptedCookieJar.new(self, @key_generator, @options) - end - - def method_missing(method, *arguments, &block) - ActiveSupport::Deprecation.warn "#{method} is deprecated with no replacement. " + - "You probably want to try this method over the parent CookieJar." - end end def initialize(app) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 93a2b52996..8879291dbd 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -101,7 +101,7 @@ module ActionDispatch (([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 (::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 ([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4 - (::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the begining + (::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the beginning (([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending )$) }x diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 619dd22ec1..7fb4719fa0 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -403,11 +403,19 @@ module ActionDispatch def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true) raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i) + if name && named_routes[name] + raise ArgumentError, "Invalid route name, already in use: '#{name}' \n" \ + "You may have defined two routes with the same name using the `:as` option, or " + "you may be overriding a route already defined by a resource with the same naming. " \ + "For the latter, you can restrict the routes created with `resources` as explained here: \n" \ + "http://guides.rubyonrails.org/routing.html#restricting-the-routes-created" + end + path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor) conditions = build_conditions(conditions, path.names.map { |x| x.to_sym }) route = @set.add_route(app, path, conditions, defaults, name) - named_routes[name] = route if name && !named_routes[name] + named_routes[name] = route if name route end diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 5c87a9cd7c..b5e47d78d1 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -1,10 +1,11 @@ module ActionPack - module VERSION #:nodoc: - MAJOR = 4 - MINOR = 0 - TINY = 0 - PRE = "beta1" + # Returns the version of the currently loaded ActionPack as a Gem::Version + def self.version + Gem::Version.new "4.0.0.beta1" + end - STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') + module VERSION #:nodoc: + MAJOR, MINOR, TINY, PRE = ActionPack.version.segments + STRING = ActionPack.version.to_s end end diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb index 1090af3060..8cba049485 100644 --- a/actionpack/test/abstract/callbacks_test.rb +++ b/actionpack/test/abstract/callbacks_test.rb @@ -259,7 +259,7 @@ module AbstractController end class TestCallbacksWithArgs < ActiveSupport::TestCase - test "callbacks still work when invoking process with multiple args" do + test "callbacks still work when invoking process with multiple arguments" do controller = CallbacksWithArgs.new controller.process(:index, " Howdy!") assert_equal "Hello world Howdy!", controller.response_body diff --git a/actionpack/test/abstract/collector_test.rb b/actionpack/test/abstract/collector_test.rb index c14d24905b..5709ad0378 100644 --- a/actionpack/test/abstract/collector_test.rb +++ b/actionpack/test/abstract/collector_test.rb @@ -42,7 +42,7 @@ module AbstractController end end - test "generated methods call custom with args received" do + test "generated methods call custom with arguments received" do collector = MyCollector.new collector.html collector.text(:foo) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 93e94f0f48..978c5aa7ac 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -908,12 +908,13 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal set.routes.first, set.named_routes[:hello] end - def test_earlier_named_routes_take_precedence - set.draw do - get '/hello/world' => 'a#b', :as => 'hello' - get '/hello' => 'a#b', :as => 'hello' + def test_duplicate_named_route_raises_rather_than_pick_precedence + assert_raise ArgumentError do + set.draw do + get '/hello/world' => 'a#b', :as => 'hello' + get '/hello' => 'a#b', :as => 'hello' + end end - assert_equal set.routes.first, set.named_routes[:hello] end def setup_named_route_test diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 5ada5a7603..892b89b12e 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' # FIXME remove DummyKeyGenerator and this require in 4.1 require 'active_support/key_generator' +require 'active_support/message_verifier' class CookiesTest < ActionController::TestCase class TestController < ActionController::Base @@ -67,6 +68,11 @@ class CookiesTest < ActionController::TestCase head :ok end + def get_signed_cookie + cookies.signed[:user_id] + head :ok + end + def set_encrypted_cookie cookies.encrypted[:foo] = 'bar' head :ok @@ -421,6 +427,55 @@ class CookiesTest < ActionController::TestCase } end + def test_signed_uses_signed_cookie_jar_if_only_secret_token_is_set + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = nil + get :set_signed_cookie + assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed + end + + def test_signed_uses_signed_cookie_jar_if_only_secret_key_base_is_set + @request.env["action_dispatch.secret_token"] = nil + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + get :set_signed_cookie + assert_kind_of ActionDispatch::Cookies::SignedCookieJar, cookies.signed + end + + def test_signed_uses_upgrade_legacy_signed_cookie_jar_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + get :set_signed_cookie + assert_kind_of ActionDispatch::Cookies::UpgradeLegacySignedCookieJar, cookies.signed + end + + def test_legacy_signed_cookie_is_read_and_transparently_upgraded_if_both_secret_token_and_secret_key_base_are_set + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + legacy_value = ActiveSupport::MessageVerifier.new("b3c631c314c0bbca50c1b2843150fe33").generate(45) + + @request.headers["Cookie"] = "user_id=#{legacy_value}" + get :get_signed_cookie + + assert_equal 45, @controller.send(:cookies).signed[:user_id] + + key_generator = @request.env["action_dispatch.key_generator"] + secret = key_generator.generate_key(@request.env["action_dispatch.signed_cookie_salt"]) + verifier = ActiveSupport::MessageVerifier.new(secret) + assert_equal 45, verifier.verify(@response.cookies["user_id"]) + end + + def test_legacy_signed_cookie_is_nil_if_tampered + @request.env["action_dispatch.secret_token"] = "b3c631c314c0bbca50c1b2843150fe33" + @request.env["action_dispatch.secret_key_base"] = "c3b95688f35581fad38df788add315ff" + + @request.headers["Cookie"] = "user_id=45" + get :get_signed_cookie + + assert_equal nil, @controller.send(:cookies).signed[:user_id] + assert_equal nil, @response.cookies["user_id"] + end + def test_cookie_with_all_domain_option get :set_cookie_with_domain assert_response :success diff --git a/actionpack/test/dispatch/mount_test.rb b/actionpack/test/dispatch/mount_test.rb index 3b008fdff0..e5e28c28be 100644 --- a/actionpack/test/dispatch/mount_test.rb +++ b/actionpack/test/dispatch/mount_test.rb @@ -21,7 +21,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest mount SprocketsApp, :at => "/sprockets" mount SprocketsApp => "/shorthand" - mount FakeEngine, :at => "/fakeengine" + mount FakeEngine, :at => "/fakeengine", :as => :fake mount FakeEngine, :at => "/getfake", :via => :get scope "/its_a" do diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 2bf7056ff7..df359ba77d 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -2577,22 +2577,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_raises(ActionController::UrlGenerationError){ list_todo_path(:list_id => '2', :id => '1') } end - def test_named_routes_collision_is_avoided_unless_explicitly_given_as - draw do - scope :as => "routes" do - get "/c/:id", :as => :collision, :to => "collision#show" - get "/collision", :to => "collision#show" - get "/no_collision", :to => "collision#show", :as => nil - - get "/fc/:id", :as => :forced_collision, :to => "forced_collision#show" - get "/forced_collision", :as => :forced_collision, :to => "forced_collision#show" - end - end - - assert_equal "/c/1", routes_collision_path(1) - assert_equal "/fc/1", routes_forced_collision_path(1) - end - def test_redirect_argument_error routes = Class.new { include ActionDispatch::Routing::Redirection }.new assert_raises(ArgumentError) { routes.redirect Object.new } @@ -2604,9 +2588,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get "/c/:id", :as => :collision, :to => "collision#show" get "/collision", :to => "collision#show" get "/no_collision", :to => "collision#show", :as => nil - - get "/fc/:id", :as => :forced_collision, :to => "forced_collision#show" - get "/forced_collision", :as => :forced_collision, :to => "forced_collision#show" end end @@ -2657,6 +2638,24 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_duplicate_route_name_raises_error + assert_raise(ArgumentError) do + draw do + get '/collision', :to => 'collision#show', :as => 'collision' + get '/duplicate', :to => 'duplicate#show', :as => 'collision' + end + end + end + + def test_duplicate_route_name_via_resources_raises_error + assert_raise(ArgumentError) do + draw do + resources :collisions + get '/collision', :to => 'collision#show', :as => 'collision' + end + end + end + def test_nested_route_in_nested_resource draw do resources :posts, :only => [:index, :show] do |