diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index b5c30432cdba987381973a146b8a27ac29592ffd..63c1b0d6366bd761f42fb0417d39210dcfa70e57 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -398,12 +398,21 @@ def dispatcher(raise_on_name_error) end end - # Invokes Journey::Router::Utils.normalize_path and ensure that - # (:locale) becomes (/:locale) instead of /(:locale). Except - # for root cases, where the latter is the correct one. + # Invokes Journey::Router::Utils.normalize_path, then ensures that + # /(:locale) becomes (/:locale). Except for root cases, where the + # former is the correct one. def self.normalize_path(path) path = Journey::Router::Utils.normalize_path(path) - path.gsub!(%r{/(\(+)/?}, '\1/') unless %r{^/(\(+[^)]+\)){1,}$}.match?(path) + + # the path for a root URL at this point can be something like + # "/(/:locale)(/:platform)/(:browser)", and we would want + # "/(:locale)(/:platform)(/:browser)" + + # reverse "/(", "/((" etc to "(/", "((/" etc + path.gsub!(%r{/(\(+)/?}, '\1/') + # if a path is all optional segments, change the leading "(/" back to + # "/(" so it evaluates to "/" when interpreted with no options. + path.sub!(%r{^(\(+)/}, '/\1') if %r{^(\(+[^)]+\)){1,}$}.match?(path) path end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index ce3f32d950232a6ede4e9395c4d4da92e0f2ac00..e3d537d84dc7b066cf5971348ee229d3e6c8de1a 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -1382,7 +1382,7 @@ def test_scoped_root_as_name assert_equal "projects#index", @response.body end - def test_optionally_scoped_root_unscoped_access + def test_optional_scoped_root_hierarchy draw do scope "(:locale)" do scope "(:platform)" do @@ -1394,8 +1394,22 @@ def test_optionally_scoped_root_unscoped_access end assert_equal "/", root_path + assert_equal "/en", root_path(locale: "en") + assert_equal "/en/osx", root_path(locale: "en", platform: "osx") + assert_equal "/en/osx/chrome", + root_path(locale: "en", platform: "osx", browser: "chrome") + get "/" assert_equal "projects#index", @response.body + + get "/en" + assert_equal "projects#index", @response.body + + get "/en/osx" + assert_equal "projects#index", @response.body + + get "/en/osx/chrome" + assert_equal "projects#index", @response.body end def test_scope_with_format_option