aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/test/controller/routing_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/test/controller/routing_test.rb')
-rw-r--r--actionpack/test/controller/routing_test.rb2105
1 files changed, 2105 insertions, 0 deletions
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
new file mode 100644
index 0000000000..b378bb80b8
--- /dev/null
+++ b/actionpack/test/controller/routing_test.rb
@@ -0,0 +1,2105 @@
+# frozen_string_literal: true
+
+require "abstract_unit"
+require "controller/fake_controllers"
+require "active_support/core_ext/object/with_options"
+require "active_support/core_ext/object/json"
+
+class MilestonesController < ActionController::Base
+ def index() head :ok end
+ alias_method :show, :index
+end
+
+# See RFC 3986, section 3.3 for allowed path characters.
+class UriReservedCharactersRoutingTest < ActiveSupport::TestCase
+ include RoutingTestHelpers
+
+ def setup
+ @set = ActionDispatch::Routing::RouteSet.new
+ @set.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:variable/*additional"
+ end
+ end
+
+ safe, unsafe = %w(: @ & = + $ , ;), %w(^ ? # [ ])
+ hex = unsafe.map { |char| "%" + char.unpack1("H2").upcase }
+
+ @segment = "#{safe.join}#{unsafe.join}"
+ @escaped = "#{safe.join}#{hex.join}"
+ end
+
+ def test_route_generation_escapes_unsafe_path_characters
+ assert_equal "/content/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2",
+ url_for(@set,
+ controller: "content",
+ action: "act#{@segment}ion",
+ variable: "var#{@segment}iable",
+ additional: ["add#{@segment}itional-1", "add#{@segment}itional-2"])
+ end
+
+ def test_route_recognition_unescapes_path_components
+ options = { controller: "content",
+ action: "act#{@segment}ion",
+ variable: "var#{@segment}iable",
+ additional: "add#{@segment}itional-1/add#{@segment}itional-2" }
+ assert_equal options, @set.recognize_path("/content/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2")
+ end
+
+ def test_route_generation_allows_passing_non_string_values_to_generated_helper
+ assert_equal "/content/action/variable/1/2",
+ url_for(@set,
+ controller: "content",
+ action: "action",
+ variable: "variable",
+ additional: [1, 2])
+ end
+end
+
+class MockController
+ def self.build(helpers, additional_options = {})
+ Class.new do
+ define_method :url_options do
+ options = super()
+ options[:protocol] ||= "http"
+ options[:host] ||= "test.host"
+ options.merge(additional_options)
+ end
+
+ include helpers
+ end
+ end
+end
+
+class LegacyRouteSetTests < ActiveSupport::TestCase
+ include RoutingTestHelpers
+ include ActionDispatch::RoutingVerbs
+
+ attr_reader :rs
+ attr_accessor :controller
+ alias :routes :rs
+
+ def setup
+ @rs = make_set
+ @response = nil
+ end
+
+ def test_symbols_with_dashes
+ rs.draw do
+ get "/:artist/:song-omg", to: lambda { |env|
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/faithfully-omg"))
+ assert_equal({ "artist" => "journey", "song" => "faithfully" }, hash)
+ end
+
+ def test_id_encoding
+ rs.draw do
+ get "/journey/:id", to: lambda { |env|
+ param = ActionDispatch::Request.new(env).path_parameters
+ resp = ActiveSupport::JSON.encode param
+ [200, {}, [resp]]
+ }
+ end
+
+ # The encoding of the URL in production is *binary*, so we add a
+ # .b here.
+ hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/%E5%A4%AA%E9%83%8E".b))
+ assert_equal({ "id" => "太郎" }, hash)
+ assert_equal ::Encoding::UTF_8, hash["id"].encoding
+ end
+
+ def test_id_with_dash
+ rs.draw do
+ get "/journey/:id", to: lambda { |env|
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/faithfully-omg"))
+ assert_equal({ "id" => "faithfully-omg" }, hash)
+ end
+
+ def test_dash_with_custom_regexp
+ rs.draw do
+ get "/:artist/:song-omg", constraints: { song: /\d+/ }, to: lambda { |env|
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/123-omg"))
+ assert_equal({ "artist" => "journey", "song" => "123" }, hash)
+ assert_equal "Not Found", get(URI("http://example.org/journey/faithfully-omg"))
+ end
+
+ def test_pre_dash
+ rs.draw do
+ get "/:artist/omg-:song", to: lambda { |env|
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/omg-faithfully"))
+ assert_equal({ "artist" => "journey", "song" => "faithfully" }, hash)
+ end
+
+ def test_pre_dash_with_custom_regexp
+ rs.draw do
+ get "/:artist/omg-:song", constraints: { song: /\d+/ }, to: lambda { |env|
+ resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
+ [200, {}, [resp]]
+ }
+ end
+
+ hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/omg-123"))
+ assert_equal({ "artist" => "journey", "song" => "123" }, hash)
+ assert_equal "Not Found", get(URI("http://example.org/journey/omg-faithfully"))
+ end
+
+ def test_star_paths_are_greedy
+ rs.draw do
+ get "/*path", to: lambda { |env|
+ x = env["action_dispatch.request.path_parameters"][:path]
+ [200, {}, [x]]
+ }, format: false
+ end
+
+ u = URI("http://example.org/foo/bar.html")
+ assert_equal u.path.sub(/^\//, ""), get(u)
+ end
+
+ def test_star_paths_are_greedy_but_not_too_much
+ rs.draw do
+ get "/*path", to: lambda { |env|
+ x = ActiveSupport::JSON.encode env["action_dispatch.request.path_parameters"]
+ [200, {}, [x]]
+ }
+ end
+
+ expected = { "path" => "foo/bar", "format" => "html" }
+ u = URI("http://example.org/foo/bar.html")
+ assert_equal expected, ActiveSupport::JSON.decode(get(u))
+ end
+
+ def test_optional_star_paths_are_greedy
+ rs.draw do
+ get "/(*filters)", to: lambda { |env|
+ x = env["action_dispatch.request.path_parameters"][:filters]
+ [200, {}, [x]]
+ }, format: false
+ end
+
+ u = URI("http://example.org/ne_27.065938,-80.6092/sw_25.489856,-82.542794")
+ assert_equal u.path.sub(/^\//, ""), get(u)
+ end
+
+ def test_optional_star_paths_are_greedy_but_not_too_much
+ rs.draw do
+ get "/(*filters)", to: lambda { |env|
+ x = ActiveSupport::JSON.encode env["action_dispatch.request.path_parameters"]
+ [200, {}, [x]]
+ }
+ end
+
+ expected = { "filters" => "ne_27.065938,-80.6092/sw_25.489856,-82",
+ "format" => "542794" }
+ u = URI("http://example.org/ne_27.065938,-80.6092/sw_25.489856,-82.542794")
+ assert_equal expected, ActiveSupport::JSON.decode(get(u))
+ end
+
+ def test_regexp_precedence
+ rs.draw do
+ get "/whois/:domain", constraints: {
+ domain: /\w+\.[\w\.]+/ },
+ to: lambda { |env| [200, {}, %w{regexp}] }
+
+ get "/whois/:id", to: lambda { |env| [200, {}, %w{id}] }
+ end
+
+ assert_equal "regexp", get(URI("http://example.org/whois/example.org"))
+ assert_equal "id", get(URI("http://example.org/whois/123"))
+ end
+
+ def test_class_and_lambda_constraints
+ subdomain = Class.new {
+ def matches?(request)
+ request.subdomain.present? && request.subdomain != "clients"
+ end
+ }
+
+ rs.draw do
+ get "/", constraints: subdomain.new,
+ to: lambda { |env| [200, {}, %w{default}] }
+ get "/", constraints: { subdomain: "clients" },
+ to: lambda { |env| [200, {}, %w{clients}] }
+ end
+
+ assert_equal "default", get(URI("http://www.example.org/"))
+ assert_equal "clients", get(URI("http://clients.example.org/"))
+ end
+
+ def test_lambda_constraints
+ rs.draw do
+ get "/", constraints: lambda { |req|
+ req.subdomain.present? && req.subdomain != "clients" },
+ to: lambda { |env| [200, {}, %w{default}] }
+
+ get "/", constraints: lambda { |req|
+ req.subdomain.present? && req.subdomain == "clients" },
+ to: lambda { |env| [200, {}, %w{clients}] }
+ end
+
+ assert_equal "default", get(URI("http://www.example.org/"))
+ assert_equal "clients", get(URI("http://clients.example.org/"))
+ end
+
+ def test_scoped_lambda
+ scope_called = false
+ rs.draw do
+ scope "/foo", constraints: lambda { |req| scope_called = true } do
+ get "/", to: lambda { |env| [200, {}, %w{default}] }
+ end
+ end
+
+ assert_equal "default", get(URI("http://www.example.org/foo/"))
+ assert scope_called, "scope constraint should be called"
+ end
+
+ def test_scoped_lambda_with_get_lambda
+ inner_called = false
+
+ rs.draw do
+ scope "/foo", constraints: lambda { |req| flunk "should not be called" } do
+ get "/", constraints: lambda { |req| inner_called = true },
+ to: lambda { |env| [200, {}, %w{default}] }
+ end
+ end
+
+ assert_equal "default", get(URI("http://www.example.org/foo/"))
+ assert inner_called, "inner constraint should be called"
+ end
+
+ def test_empty_string_match
+ rs.draw do
+ get "/:username", constraints: { username: /[^\/]+/ },
+ to: lambda { |e| [200, {}, ["foo"]] }
+ end
+ assert_equal "Not Found", get(URI("http://example.org/"))
+ assert_equal "foo", get(URI("http://example.org/hello"))
+ end
+
+ def test_non_greedy_glob_regexp
+ params = nil
+ rs.draw do
+ get "/posts/:id(/*filters)", constraints: { filters: /.+?/ },
+ to: lambda { |e|
+ params = e["action_dispatch.request.path_parameters"]
+ [200, {}, ["foo"]]
+ }
+ end
+ assert_equal "foo", get(URI("http://example.org/posts/1/foo.js"))
+ assert_equal({ id: "1", filters: "foo", format: "js" }, params)
+ end
+
+ def test_specific_controller_action_failure
+ rs.draw do
+ mount lambda { } => "/foo"
+ end
+
+ assert_raises(ActionController::UrlGenerationError) do
+ url_for(rs, controller: "omg", action: "lol")
+ end
+ end
+
+ def test_default_setup
+ rs.draw { ActiveSupport::Deprecation.silence { get "/:controller(/:action(/:id))" } }
+ assert_equal({ controller: "content", action: "index" }, rs.recognize_path("/content"))
+ assert_equal({ controller: "content", action: "list" }, rs.recognize_path("/content/list"))
+ assert_equal({ controller: "content", action: "show", id: "10" }, rs.recognize_path("/content/show/10"))
+
+ assert_equal({ controller: "admin/user", action: "show", id: "10" }, rs.recognize_path("/admin/user/show/10"))
+
+ assert_equal "/admin/user/show/10", url_for(rs, controller: "admin/user", action: "show", id: 10)
+
+ get URI("http://test.host/admin/user/list/10")
+
+ assert_equal({ controller: "admin/user", action: "list", id: "10" },
+ controller.request.path_parameters)
+
+ assert_equal "/admin/user/show", controller.url_for(action: "show", only_path: true)
+ assert_equal "/admin/user/list/10", controller.url_for(only_path: true)
+
+ assert_equal "/admin/stuff", controller.url_for(controller: "stuff", only_path: true)
+ assert_equal "/stuff", controller.url_for(controller: "/stuff", only_path: true)
+ end
+
+ def test_route_with_colon_first
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/:controller/:action/:id", action: "index", id: nil
+ end
+
+ get ":url", controller: "content", action: "translate"
+ end
+
+ assert_equal({ controller: "content", action: "translate", url: "example" }, rs.recognize_path("/example"))
+ end
+
+ def test_route_with_regexp_for_action
+ rs.draw { ActiveSupport::Deprecation.silence { get "/:controller/:action", action: /auth[-|_].+/ } }
+
+ assert_equal({ action: "auth_google", controller: "content" }, rs.recognize_path("/content/auth_google"))
+ assert_equal({ action: "auth-twitter", controller: "content" }, rs.recognize_path("/content/auth-twitter"))
+
+ assert_equal "/content/auth_google", url_for(rs, controller: "content", action: "auth_google")
+ assert_equal "/content/auth-twitter", url_for(rs, controller: "content", action: "auth-twitter")
+ end
+
+ def test_route_with_regexp_for_controller
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:admintoken(/:action(/:id))", controller: /admin\/.+/
+ get "/:controller(/:action(/:id))"
+ end
+ end
+
+ assert_equal({ controller: "admin/user", admintoken: "foo", action: "index" },
+ rs.recognize_path("/admin/user/foo"))
+ assert_equal({ controller: "content", action: "foo" },
+ rs.recognize_path("/content/foo"))
+
+ assert_equal "/admin/user/foo", url_for(rs, controller: "admin/user", admintoken: "foo", action: "index")
+ assert_equal "/content/foo", url_for(rs, controller: "content", action: "foo")
+ end
+
+ def test_route_with_regexp_and_captures_for_controller
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/:controller(/:action(/:id))", controller: /admin\/(accounts|users)/
+ end
+ end
+ assert_equal({ controller: "admin/accounts", action: "index" }, rs.recognize_path("/admin/accounts"))
+ assert_equal({ controller: "admin/users", action: "index" }, rs.recognize_path("/admin/users"))
+ assert_raise(ActionController::RoutingError) { rs.recognize_path("/admin/products") }
+ end
+
+ def test_route_with_regexp_and_dot
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:file",
+ controller: /admin|user/,
+ action: /upload|download/,
+ defaults: { file: nil },
+ constraints: { file: %r{[^/]+(\.[^/]+)?} }
+ end
+ end
+ # Without a file extension
+ assert_equal "/user/download/file",
+ url_for(rs, controller: "user", action: "download", file: "file")
+
+ assert_equal({ controller: "user", action: "download", file: "file" },
+ rs.recognize_path("/user/download/file"))
+
+ # Now, let's try a file with an extension, really a dot (.)
+ assert_equal "/user/download/file.jpg",
+ url_for(rs, controller: "user", action: "download", file: "file.jpg")
+
+ assert_equal({ controller: "user", action: "download", file: "file.jpg" },
+ rs.recognize_path("/user/download/file.jpg"))
+ end
+
+ def test_basic_named_route
+ rs.draw do
+ root to: "content#list", as: "home"
+ end
+ assert_equal("http://test.host/", setup_for_named_route.send(:home_url))
+ end
+
+ def test_named_route_with_option
+ rs.draw do
+ get "page/:title" => "content#show_page", :as => "page"
+ end
+
+ assert_equal("http://test.host/page/new%20stuff",
+ setup_for_named_route.send(:page_url, title: "new stuff"))
+ end
+
+ def test_named_route_with_default
+ rs.draw do
+ get "page/:title" => "content#show_page", :title => "AboutPage", :as => "page"
+ end
+
+ assert_equal("http://test.host/page/AboutRails",
+ setup_for_named_route.send(:page_url, title: "AboutRails"))
+ end
+
+ def test_named_route_with_path_prefix
+ rs.draw do
+ scope "my" do
+ get "page" => "content#show_page", :as => "page"
+ end
+ end
+
+ assert_equal("http://test.host/my/page",
+ setup_for_named_route.send(:page_url))
+ end
+
+ def test_named_route_with_blank_path_prefix
+ rs.draw do
+ scope "" do
+ get "page" => "content#show_page", :as => "page"
+ end
+ end
+
+ assert_equal("http://test.host/page",
+ setup_for_named_route.send(:page_url))
+ end
+
+ def test_named_route_with_nested_controller
+ rs.draw do
+ get "admin/user" => "admin/user#index", :as => "users"
+ end
+
+ assert_equal("http://test.host/admin/user",
+ setup_for_named_route.send(:users_url))
+ end
+
+ def test_optimised_named_route_with_host
+ rs.draw do
+ get "page" => "content#show_page", :as => "pages", :host => "foo.com"
+ end
+ routes = setup_for_named_route
+ assert_equal "http://foo.com/page", routes.pages_url
+ end
+
+ def setup_for_named_route(options = {})
+ MockController.build(rs.url_helpers, options).new
+ end
+
+ def test_named_route_without_hash
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id", as: "normal"
+ end
+ end
+ end
+
+ def test_named_route_root
+ rs.draw do
+ root to: "hello#index"
+ end
+ routes = setup_for_named_route
+ assert_equal("http://test.host/", routes.send(:root_url))
+ assert_equal("/", routes.send(:root_path))
+ end
+
+ def test_named_route_root_without_hash
+ rs.draw do
+ root "hello#index"
+ end
+ routes = setup_for_named_route
+ assert_equal("http://test.host/", routes.send(:root_url))
+ assert_equal("/", routes.send(:root_path))
+ end
+
+ def test_named_route_root_with_hash
+ rs.draw do
+ root "hello#index", as: :index
+ end
+
+ routes = setup_for_named_route
+ assert_equal("http://test.host/", routes.send(:index_url))
+ assert_equal("/", routes.send(:index_path))
+ end
+
+ def test_root_without_path_raises_argument_error
+ assert_raises ArgumentError do
+ rs.draw { root nil }
+ end
+ end
+
+ def test_named_route_root_with_trailing_slash
+ rs.draw do
+ root "hello#index"
+ end
+
+ routes = setup_for_named_route(trailing_slash: true)
+ assert_equal("http://test.host/", routes.send(:root_url))
+ assert_equal("http://test.host/?foo=bar", routes.send(:root_url, foo: :bar))
+ end
+
+ def test_named_route_with_regexps
+ rs.draw do
+ get "page/:year/:month/:day/:title" => "page#show", :as => "article",
+ :year => /\d+/, :month => /\d+/, :day => /\d+/
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ routes = setup_for_named_route
+
+ assert_equal "http://test.host/page/2005/6/10/hi",
+ routes.send(:article_url, title: "hi", day: 10, year: 2005, month: 6)
+ end
+
+ def test_changing_controller
+ rs.draw { ActiveSupport::Deprecation.silence { get ":controller/:action/:id" } }
+
+ get URI("http://test.host/admin/user/index/10")
+
+ assert_equal "/admin/stuff/show/10",
+ controller.url_for(controller: "stuff", action: "show", id: 10, only_path: true)
+ end
+
+ def test_paths_escaped
+ rs.draw do
+ get "file/*path" => "content#show_file", :as => "path"
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ # No + to space in URI escaping, only for query params.
+ results = rs.recognize_path "/file/hello+world/how+are+you%3F"
+ assert results, "Recognition should have succeeded"
+ assert_equal "hello+world/how+are+you?", results[:path]
+
+ # Use %20 for space instead.
+ results = rs.recognize_path "/file/hello%20world/how%20are%20you%3F"
+ assert results, "Recognition should have succeeded"
+ assert_equal "hello world/how are you?", results[:path]
+ end
+
+ def test_paths_slashes_unescaped_with_ordered_parameters
+ rs.draw do
+ get "/file/*path" => "content#index", :as => "path"
+ end
+
+ # No / to %2F in URI, only for query params.
+ assert_equal("/file/hello/world", setup_for_named_route.send(:path_path, ["hello", "world"]))
+ end
+
+ def test_non_controllers_cannot_be_matched
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+ assert_raise(ActionController::RoutingError) { rs.recognize_path("/not_a/show/10") }
+ end
+
+ def test_should_list_options_diff_when_routing_constraints_dont_match
+ rs.draw do
+ get "post/:id" => "post#show", :constraints => { id: /\d+/ }, :as => "post"
+ end
+ assert_raise(ActionController::UrlGenerationError) do
+ url_for(rs, controller: "post", action: "show", bad_param: "foo", use_route: "post")
+ end
+ end
+
+ def test_dynamic_path_allowed
+ rs.draw do
+ get "*path" => "content#show_file"
+ end
+
+ assert_equal "/pages/boo",
+ url_for(rs, controller: "content", action: "show_file", path: %w(pages boo))
+ end
+
+ def test_dynamic_recall_paths_allowed
+ rs.draw do
+ get "*path" => "content#show_file"
+ end
+
+ get URI("http://test.host/pages/boo")
+ assert_equal({ controller: "content", action: "show_file", path: "pages/boo" },
+ controller.request.path_parameters)
+
+ assert_equal "/pages/boo",
+ controller.url_for(only_path: true)
+ end
+
+ def test_backwards
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get "page/:id(/:action)" => "pages#show"
+ get ":controller(/:action(/:id))"
+ end
+ end
+
+ get URI("http://test.host/pages/show")
+ assert_equal "/page/20", controller.url_for(id: 20, only_path: true)
+ assert_equal "/page/20", url_for(rs, controller: "pages", id: 20, action: "show")
+ assert_equal "/pages/boo", url_for(rs, controller: "pages", action: "boo")
+ end
+
+ def test_route_with_integer_default
+ rs.draw do
+ get "page(/:id)" => "content#show_page", :id => 1
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ assert_equal "/page", url_for(rs, controller: "content", action: "show_page")
+ assert_equal "/page", url_for(rs, controller: "content", action: "show_page", id: 1)
+ assert_equal "/page", url_for(rs, controller: "content", action: "show_page", id: "1")
+ assert_equal "/page/10", url_for(rs, controller: "content", action: "show_page", id: 10)
+
+ assert_equal({ controller: "content", action: "show_page", id: 1 }, rs.recognize_path("/page"))
+ assert_equal({ controller: "content", action: "show_page", id: "1" }, rs.recognize_path("/page/1"))
+ assert_equal({ controller: "content", action: "show_page", id: "10" }, rs.recognize_path("/page/10"))
+ end
+
+ # For newer revision
+ def test_route_with_text_default
+ rs.draw do
+ get "page/:id" => "content#show_page", :id => 1
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ assert_equal "/page/foo", url_for(rs, controller: "content", action: "show_page", id: "foo")
+ assert_equal({ controller: "content", action: "show_page", id: "foo" }, rs.recognize_path("/page/foo"))
+
+ token = +"\321\202\320\265\320\272\321\201\321\202" # 'text' in Russian
+ token.force_encoding(Encoding::BINARY)
+ escaped_token = CGI.escape(token)
+
+ assert_equal "/page/" + escaped_token, url_for(rs, controller: "content", action: "show_page", id: token)
+ assert_equal({ controller: "content", action: "show_page", id: token }, rs.recognize_path("/page/#{escaped_token}"))
+ end
+
+ def test_action_expiry
+ rs.draw { ActiveSupport::Deprecation.silence { get ":controller(/:action(/:id))" } }
+ get URI("http://test.host/content/show")
+ assert_equal "/content", controller.url_for(controller: "content", only_path: true)
+ end
+
+ def test_requirement_should_prevent_optional_id
+ rs.draw do
+ get "post/:id" => "post#show", :constraints => { id: /\d+/ }, :as => "post"
+ end
+
+ assert_equal "/post/10", url_for(rs, controller: "post", action: "show", id: 10)
+
+ assert_raise(ActionController::UrlGenerationError) do
+ url_for(rs, controller: "post", action: "show")
+ end
+ end
+
+ def test_both_requirement_and_optional
+ rs.draw do
+ get("test(/:year)" => "post#show", :as => "blog",
+ :defaults => { year: nil },
+ :constraints => { year: /\d{4}/ }
+ )
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ assert_equal "/test", url_for(rs, controller: "post", action: "show")
+ assert_equal "/test", url_for(rs, controller: "post", action: "show", year: nil)
+
+ assert_equal("http://test.host/test", setup_for_named_route.send(:blog_url))
+ end
+
+ def test_set_to_nil_forgets
+ rs.draw do
+ get "pages(/:year(/:month(/:day)))" => "content#list_pages", :month => nil, :day => nil
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ assert_equal "/pages/2005",
+ url_for(rs, controller: "content", action: "list_pages", year: 2005)
+ assert_equal "/pages/2005/6",
+ url_for(rs, controller: "content", action: "list_pages", year: 2005, month: 6)
+ assert_equal "/pages/2005/6/12",
+ url_for(rs, controller: "content", action: "list_pages", year: 2005, month: 6, day: 12)
+
+ get URI("http://test.host/pages/2005/6/12")
+ assert_equal({ controller: "content", action: "list_pages", year: "2005", month: "6", day: "12" },
+ controller.request.path_parameters)
+
+ assert_equal "/pages/2005/6/4",
+ controller.url_for(day: 4, only_path: true)
+
+ assert_equal "/pages/2005/6",
+ controller.url_for(day: nil, only_path: true)
+
+ assert_equal "/pages/2005",
+ controller.url_for(day: nil, month: nil, only_path: true)
+ end
+
+ def test_root_url_generation_with_controller_and_action
+ rs.draw do
+ root to: "content#index"
+ end
+
+ assert_equal "/", url_for(rs, controller: "content", action: "index")
+ assert_equal "/", url_for(rs, controller: "content")
+ end
+
+ def test_named_root_url_generation_with_controller_and_action
+ rs.draw do
+ root to: "content#index", as: "home"
+ end
+
+ assert_equal "/", url_for(rs, controller: "content", action: "index")
+ assert_equal "/", url_for(rs, controller: "content")
+
+ assert_equal("http://test.host/", setup_for_named_route.send(:home_url))
+ end
+
+ def test_named_route_method
+ rs.draw do
+ get "categories" => "content#categories", :as => "categories"
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller(/:action(/:id))"
+ end
+ end
+
+ assert_equal "/categories", url_for(rs, controller: "content", action: "categories")
+ assert_equal "/content/hi", url_for(rs, controller: "content", action: "hi")
+ end
+
+ def test_named_routes_array
+ test_named_route_method
+ assert_equal [:categories], rs.named_routes.names
+ end
+
+ def test_nil_defaults
+ rs.draw do
+ get "journal" => "content#list_journal",
+ :date => nil, :user_id => nil
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ assert_equal "/journal", url_for(rs,
+ controller: "content",
+ action: "list_journal",
+ date: nil,
+ user_id: nil)
+ end
+
+ def setup_request_method_routes_for(method)
+ rs.draw do
+ match "/match" => "books##{method}", :via => method.to_sym
+ end
+ end
+
+ %w(GET PATCH POST PUT DELETE).each do |request_method|
+ define_method("test_request_method_recognized_with_#{request_method}") do
+ setup_request_method_routes_for(request_method.downcase)
+ params = rs.recognize_path("/match", method: request_method)
+ assert_equal request_method.downcase, params[:action]
+ end
+ end
+
+ def test_recognize_array_of_methods
+ rs.draw do
+ match "/match" => "books#get_or_post", :via => [:get, :post]
+ put "/match" => "books#not_get_or_post"
+ end
+
+ params = rs.recognize_path("/match", method: :post)
+ assert_equal "get_or_post", params[:action]
+
+ params = rs.recognize_path("/match", method: :put)
+ assert_equal "not_get_or_post", params[:action]
+ end
+
+ def test_subpath_recognized
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/books/:id/edit" => "subpath_books#edit"
+ get "/items/:id/:action" => "subpath_books"
+ get "/posts/new/:action" => "subpath_books"
+ get "/posts/:id" => "subpath_books#show"
+ end
+ end
+
+ hash = rs.recognize_path "/books/17/edit"
+ assert_not_nil hash
+ assert_equal %w(subpath_books 17 edit), [hash[:controller], hash[:id], hash[:action]]
+
+ hash = rs.recognize_path "/items/3/complete"
+ assert_not_nil hash
+ assert_equal %w(subpath_books 3 complete), [hash[:controller], hash[:id], hash[:action]]
+
+ hash = rs.recognize_path "/posts/new/preview"
+ assert_not_nil hash
+ assert_equal %w(subpath_books preview), [hash[:controller], hash[:action]]
+
+ hash = rs.recognize_path "/posts/7"
+ assert_not_nil hash
+ assert_equal %w(subpath_books show 7), [hash[:controller], hash[:action], hash[:id]]
+ end
+
+ def test_subpath_generated
+ rs.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/books/:id/edit" => "subpath_books#edit"
+ get "/items/:id/:action" => "subpath_books"
+ get "/posts/new/:action" => "subpath_books"
+ end
+ end
+
+ assert_equal "/books/7/edit", url_for(rs, controller: "subpath_books", id: 7, action: "edit")
+ assert_equal "/items/15/complete", url_for(rs, controller: "subpath_books", id: 15, action: "complete")
+ assert_equal "/posts/new/preview", url_for(rs, controller: "subpath_books", action: "preview")
+ end
+
+ def test_failed_constraints_raises_exception_with_violated_constraints
+ rs.draw do
+ get "foos/:id" => "foos#show", :as => "foo_with_requirement", :constraints => { id: /\d+/ }
+ end
+
+ assert_raise(ActionController::UrlGenerationError) do
+ setup_for_named_route.send(:foo_with_requirement_url, "I am Against the constraints")
+ end
+ end
+
+ def test_routes_changed_correctly_after_clear
+ rs = ::ActionDispatch::Routing::RouteSet.new
+ rs.draw do
+ get "ca" => "ca#aa"
+ get "cb" => "cb#ab"
+ get "cc" => "cc#ac"
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ get ":controller/:action/:id.:format"
+ end
+ end
+
+ hash = rs.recognize_path "/cc"
+
+ assert_not_nil hash
+ assert_equal %w(cc ac), [hash[:controller], hash[:action]]
+
+ rs.draw do
+ get "cb" => "cb#ab"
+ get "cc" => "cc#ac"
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ get ":controller/:action/:id.:format"
+ end
+ end
+
+ hash = rs.recognize_path "/cc"
+
+ assert_not_nil hash
+ assert_equal %w(cc ac), [hash[:controller], hash[:action]]
+ end
+end
+
+class RouteSetTest < ActiveSupport::TestCase
+ include RoutingTestHelpers
+ include ActionDispatch::RoutingVerbs
+
+ attr_reader :set
+ alias :routes :set
+ attr_accessor :controller
+
+ def setup
+ super
+ @set = make_set
+ end
+
+ def request
+ @request ||= ActionController::TestRequest.new
+ end
+
+ def default_route_set
+ @default_route_set ||= begin
+ set = ActionDispatch::Routing::RouteSet.new
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/:controller(/:action(/:id))"
+ end
+ end
+ set
+ end
+ end
+
+ def test_generate_extras
+ set.draw { ActiveSupport::Deprecation.silence { get ":controller/(:action(/:id))" } }
+ path, extras = set.generate_extras(controller: "foo", action: "bar", id: 15, this: "hello", that: "world")
+ assert_equal "/foo/bar/15", path
+ assert_equal %w(that this), extras.map(&:to_s).sort
+ end
+
+ def test_extra_keys
+ set.draw { ActiveSupport::Deprecation.silence { get ":controller/:action/:id" } }
+ extras = set.extra_keys(controller: "foo", action: "bar", id: 15, this: "hello", that: "world")
+ assert_equal %w(that this), extras.map(&:to_s).sort
+ end
+
+ def test_generate_extras_not_first
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id.:format"
+ get ":controller/:action/:id"
+ end
+ end
+ path, extras = set.generate_extras(controller: "foo", action: "bar", id: 15, this: "hello", that: "world")
+ assert_equal "/foo/bar/15", path
+ assert_equal %w(that this), extras.map(&:to_s).sort
+ end
+
+ def test_generate_not_first
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id.:format"
+ get ":controller/:action/:id"
+ end
+ end
+ assert_equal "/foo/bar/15?this=hello",
+ url_for(set, controller: "foo", action: "bar", id: 15, this: "hello")
+ end
+
+ def test_extra_keys_not_first
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id.:format"
+ get ":controller/:action/:id"
+ end
+ end
+ extras = set.extra_keys(controller: "foo", action: "bar", id: 15, this: "hello", that: "world")
+ assert_equal %w(that this), extras.map(&:to_s).sort
+ end
+
+ def test_draw
+ assert_equal 0, set.routes.size
+ set.draw do
+ get "/hello/world" => "a#b"
+ end
+ assert_equal 1, set.routes.size
+ end
+
+ def test_draw_symbol_controller_name
+ assert_equal 0, set.routes.size
+ set.draw do
+ get "/users/index" => "users#index"
+ end
+ set.recognize_path("/users/index", method: :get)
+ assert_equal 1, set.routes.size
+ end
+
+ def test_named_draw
+ assert_equal 0, set.routes.size
+ set.draw do
+ get "/hello/world" => "a#b", :as => "hello"
+ end
+ assert_equal 1, set.routes.size
+ assert_equal set.routes.first, set.named_routes[:hello]
+ end
+
+ 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
+ end
+
+ def setup_named_route_test
+ set.draw do
+ get "/people(/:id)" => "people#show", :as => "show"
+ get "/people" => "people#index", :as => "index"
+ get "/people/go/:foo/:bar/joe(/:id)" => "people#multi", :as => "multi"
+ get "/admin/users" => "admin/users#index", :as => "users"
+ end
+
+ get URI("http://test.host/people")
+ controller
+ end
+
+ def test_named_route_url_method
+ controller = setup_named_route_test
+
+ assert_equal "http://test.host/people/5", controller.send(:show_url, id: 5)
+ assert_equal "/people/5", controller.send(:show_path, id: 5)
+
+ assert_equal "http://test.host/people", controller.send(:index_url)
+ assert_equal "/people", controller.send(:index_path)
+
+ assert_equal "http://test.host/admin/users", controller.send(:users_url)
+ assert_equal "/admin/users", controller.send(:users_path)
+ end
+
+ def test_named_route_url_method_with_anchor
+ controller = setup_named_route_test
+
+ assert_equal "http://test.host/people/5#location", controller.send(:show_url, id: 5, anchor: "location")
+ assert_equal "/people/5#location", controller.send(:show_path, id: 5, anchor: "location")
+
+ assert_equal "http://test.host/people#location", controller.send(:index_url, anchor: "location")
+ assert_equal "/people#location", controller.send(:index_path, anchor: "location")
+
+ assert_equal "http://test.host/admin/users#location", controller.send(:users_url, anchor: "location")
+ assert_equal "/admin/users#location", controller.send(:users_path, anchor: "location")
+
+ assert_equal "http://test.host/people/go/7/hello/joe/5#location",
+ controller.send(:multi_url, 7, "hello", 5, anchor: "location")
+
+ assert_equal "http://test.host/people/go/7/hello/joe/5?baz=bar#location",
+ controller.send(:multi_url, 7, "hello", 5, baz: "bar", anchor: "location")
+
+ assert_equal "http://test.host/people?baz=bar#location",
+ controller.send(:index_url, baz: "bar", anchor: "location")
+
+ assert_equal "http://test.host/people", controller.send(:index_url, anchor: nil)
+ assert_equal "http://test.host/people", controller.send(:index_url, anchor: false)
+ end
+
+ def test_named_route_url_method_with_port
+ controller = setup_named_route_test
+ assert_equal "http://test.host:8080/people/5", controller.send(:show_url, 5, port: 8080)
+ end
+
+ def test_named_route_url_method_with_host
+ controller = setup_named_route_test
+ assert_equal "http://some.example.com/people/5", controller.send(:show_url, 5, host: "some.example.com")
+ end
+
+ def test_named_route_url_method_with_protocol
+ controller = setup_named_route_test
+ assert_equal "https://test.host/people/5", controller.send(:show_url, 5, protocol: "https")
+ end
+
+ def test_named_route_url_method_with_ordered_parameters
+ controller = setup_named_route_test
+ assert_equal "http://test.host/people/go/7/hello/joe/5",
+ controller.send(:multi_url, 7, "hello", 5)
+ end
+
+ def test_named_route_url_method_with_ordered_parameters_and_hash
+ controller = setup_named_route_test
+ assert_equal "http://test.host/people/go/7/hello/joe/5?baz=bar",
+ controller.send(:multi_url, 7, "hello", 5, baz: "bar")
+ end
+
+ def test_named_route_url_method_with_ordered_parameters_and_empty_hash
+ controller = setup_named_route_test
+ assert_equal "http://test.host/people/go/7/hello/joe/5",
+ controller.send(:multi_url, 7, "hello", 5, {})
+ end
+
+ def test_named_route_url_method_with_no_positional_arguments
+ controller = setup_named_route_test
+ assert_equal "http://test.host/people?baz=bar",
+ controller.send(:index_url, baz: "bar")
+ end
+
+ def test_draw_default_route
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ assert_equal 1, set.routes.size
+
+ assert_equal "/users/show/10", url_for(set, controller: "users", action: "show", id: 10)
+ assert_equal "/users/index/10", url_for(set, controller: "users", id: 10)
+
+ assert_equal({ controller: "users", action: "index", id: "10" }, set.recognize_path("/users/index/10"))
+ assert_equal({ controller: "users", action: "index", id: "10" }, set.recognize_path("/users/index/10/"))
+ end
+
+ def test_route_with_parameter_shell
+ set.draw do
+ get "page/:id" => "pages#show", :id => /\d+/
+
+ ActiveSupport::Deprecation.silence do
+ get "/:controller(/:action(/:id))"
+ end
+ end
+
+ assert_equal({ controller: "pages", action: "index" }, request_path_params("/pages"))
+ assert_equal({ controller: "pages", action: "index" }, request_path_params("/pages/index"))
+ assert_equal({ controller: "pages", action: "list" }, request_path_params("/pages/list"))
+
+ assert_equal({ controller: "pages", action: "show", id: "10" }, request_path_params("/pages/show/10"))
+ assert_equal({ controller: "pages", action: "show", id: "10" }, request_path_params("/page/10"))
+ end
+
+ def test_route_constraints_on_request_object_with_anchors_are_valid
+ assert_nothing_raised do
+ set.draw do
+ get "page/:id" => "pages#show", :constraints => { host: /^foo$/ }
+ end
+ end
+ end
+
+ def test_route_constraints_with_anchor_chars_are_invalid
+ assert_raise ArgumentError do
+ set.draw do
+ get "page/:id" => "pages#show", :id => /^\d+/
+ end
+ end
+ assert_raise ArgumentError do
+ set.draw do
+ get "page/:id" => "pages#show", :id => /\A\d+/
+ end
+ end
+ assert_raise ArgumentError do
+ set.draw do
+ get "page/:id" => "pages#show", :id => /\d+$/
+ end
+ end
+ assert_raise ArgumentError do
+ set.draw do
+ get "page/:id" => "pages#show", :id => /\d+\Z/
+ end
+ end
+ assert_raise ArgumentError do
+ set.draw do
+ get "page/:id" => "pages#show", :id => /\d+\z/
+ end
+ end
+ end
+
+ def test_route_constraints_with_options_method_condition_is_valid
+ assert_nothing_raised do
+ set.draw do
+ match "valid/route" => "pages#show", :via => :options
+ end
+ end
+ end
+
+ def test_route_error_with_missing_controller
+ set.draw do
+ get "/people" => "missing#index"
+ end
+
+ assert_raises(ActionController::RoutingError) { request_path_params "/people" }
+ end
+
+ def test_recognize_with_encoded_id_and_regex
+ set.draw do
+ get "page/:id" => "pages#show", :id => /[a-zA-Z0-9\+]+/
+ end
+
+ assert_equal({ controller: "pages", action: "show", id: "10" }, request_path_params("/page/10"))
+ assert_equal({ controller: "pages", action: "show", id: "hello+world" }, request_path_params("/page/hello+world"))
+ end
+
+ def test_recognize_with_http_methods
+ set.draw do
+ get "/people" => "people#index", :as => "people"
+ post "/people" => "people#create"
+ get "/people/:id" => "people#show", :as => "person"
+ put "/people/:id" => "people#update"
+ patch "/people/:id" => "people#update"
+ delete "/people/:id" => "people#destroy"
+ end
+
+ params = request_path_params("/people", method: :get)
+ assert_equal("index", params[:action])
+
+ params = request_path_params("/people", method: :post)
+ assert_equal("create", params[:action])
+
+ params = request_path_params("/people/5", method: :put)
+ assert_equal("update", params[:action])
+
+ params = request_path_params("/people/5", method: :patch)
+ assert_equal("update", params[:action])
+
+ assert_raise(ActionController::UnknownHttpMethod) {
+ request_path_params("/people", method: :bacon)
+ }
+
+ params = request_path_params("/people/5", method: :get)
+ assert_equal("show", params[:action])
+ assert_equal("5", params[:id])
+
+ params = request_path_params("/people/5", method: :put)
+ assert_equal("update", params[:action])
+ assert_equal("5", params[:id])
+
+ params = request_path_params("/people/5", method: :patch)
+ assert_equal("update", params[:action])
+ assert_equal("5", params[:id])
+
+ params = request_path_params("/people/5", method: :delete)
+ assert_equal("destroy", params[:action])
+ assert_equal("5", params[:id])
+
+ assert_raise(ActionController::RoutingError) {
+ request_path_params("/people/5", method: :post)
+ }
+ end
+
+ def test_recognize_with_alias_in_conditions
+ set.draw do
+ match "/people" => "people#index", :as => "people", :via => :get
+ root to: "people#index"
+ end
+
+ params = request_path_params("/people", method: :get)
+ assert_equal("people", params[:controller])
+ assert_equal("index", params[:action])
+
+ params = request_path_params("/", method: :get)
+ assert_equal("people", params[:controller])
+ assert_equal("index", params[:action])
+ end
+
+ def test_typo_recognition
+ set.draw do
+ get "articles/:year/:month/:day/:title" => "articles#permalink",
+ :year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/
+ end
+
+ params = request_path_params("/articles/2005/11/05/a-very-interesting-article", method: :get)
+ assert_equal("permalink", params[:action])
+ assert_equal("2005", params[:year])
+ assert_equal("11", params[:month])
+ assert_equal("05", params[:day])
+ assert_equal("a-very-interesting-article", params[:title])
+ end
+
+ def test_routing_traversal_does_not_load_extra_classes
+ assert_not Object.const_defined?("Profiler__"), "Profiler should not be loaded"
+ set.draw do
+ get "/profile" => "profile#index"
+ end
+
+ request_path_params("/profile") rescue nil
+
+ assert_not Object.const_defined?("Profiler__"), "Profiler should not be loaded"
+ end
+
+ def test_recognize_with_conditions_and_format
+ set.draw do
+ get "people/:id" => "people#show", :as => "person"
+ put "people/:id" => "people#update"
+ patch "people/:id" => "people#update"
+ get "people/:id(.:format)" => "people#show"
+ end
+
+ params = request_path_params("/people/5", method: :get)
+ assert_equal("show", params[:action])
+ assert_equal("5", params[:id])
+
+ params = request_path_params("/people/5", method: :put)
+ assert_equal("update", params[:action])
+
+ params = request_path_params("/people/5", method: :patch)
+ assert_equal("update", params[:action])
+
+ params = request_path_params("/people/5.png", method: :get)
+ assert_equal("show", params[:action])
+ assert_equal("5", params[:id])
+ assert_equal("png", params[:format])
+ end
+
+ def test_generate_with_default_action
+ set.draw do
+ get "/people", controller: "people", action: "index"
+ get "/people/list", controller: "people", action: "list"
+ end
+
+ url = url_for(set, controller: "people", action: "list")
+ assert_equal "/people/list", url
+ end
+
+ def test_root_map
+ set.draw { root to: "people#index" }
+
+ params = request_path_params("", method: :get)
+ assert_equal("people", params[:controller])
+ assert_equal("index", params[:action])
+ end
+
+ def test_namespace
+ set.draw do
+ namespace "api" do
+ get "inventory" => "products#inventory"
+ end
+ end
+
+ params = request_path_params("/api/inventory", method: :get)
+ assert_equal("api/products", params[:controller])
+ assert_equal("inventory", params[:action])
+ end
+
+ def test_namespaced_root_map
+ set.draw do
+ namespace "api" do
+ root to: "products#index"
+ end
+ end
+
+ params = request_path_params("/api", method: :get)
+ assert_equal("api/products", params[:controller])
+ assert_equal("index", params[:action])
+ end
+
+ def test_namespace_with_path_prefix
+ set.draw do
+ scope module: "api", path: "prefix" do
+ get "inventory" => "products#inventory"
+ end
+ end
+
+ params = request_path_params("/prefix/inventory", method: :get)
+ assert_equal("api/products", params[:controller])
+ assert_equal("inventory", params[:action])
+ end
+
+ def test_namespace_with_blank_path_prefix
+ set.draw do
+ scope module: "api", path: "" do
+ get "inventory" => "products#inventory"
+ end
+ end
+
+ params = request_path_params("/inventory", method: :get)
+ assert_equal("api/products", params[:controller])
+ assert_equal("inventory", params[:action])
+ end
+
+ def test_id_is_sticky_when_it_ought_to_be
+ @set = make_set false
+
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:id/:action"
+ end
+ end
+
+ get URI("http://test.host/people/7/show")
+
+ assert_equal "/people/7/destroy", controller.url_for(action: "destroy", only_path: true)
+ end
+
+ def test_use_static_path_when_possible
+ @set = make_set false
+
+ set.draw do
+ get "about" => "welcome#about"
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:id/:action"
+ end
+ end
+
+ get URI("http://test.host/welcom/get/7")
+
+ assert_equal "/about", controller.url_for(controller: "welcome",
+ action: "about",
+ only_path: true)
+ end
+
+ def test_generate
+ set.draw { ActiveSupport::Deprecation.silence { get ":controller/:action/:id" } }
+
+ args = { controller: "foo", action: "bar", id: "7", x: "y" }
+ assert_equal "/foo/bar/7?x=y", url_for(set, args)
+ assert_equal ["/foo/bar/7", [:x]], set.generate_extras(args)
+ assert_equal [:x], set.extra_keys(args)
+ end
+
+ def test_generate_with_path_prefix
+ set.draw do
+ scope "my" do
+ ActiveSupport::Deprecation.silence do
+ get ":controller(/:action(/:id))"
+ end
+ end
+ end
+
+ args = { controller: "foo", action: "bar", id: "7", x: "y" }
+ assert_equal "/my/foo/bar/7?x=y", url_for(set, args)
+ end
+
+ def test_generate_with_blank_path_prefix
+ set.draw do
+ scope "" do
+ ActiveSupport::Deprecation.silence do
+ get ":controller(/:action(/:id))"
+ end
+ end
+ end
+
+ args = { controller: "foo", action: "bar", id: "7", x: "y" }
+ assert_equal "/foo/bar/7?x=y", url_for(set, args)
+ end
+
+ def test_named_routes_are_never_relative_to_modules
+ @set = make_set false
+
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/connection/manage(/:action)" => "connection/manage#index"
+ get "/connection/connection" => "connection/connection#index"
+ get "/connection" => "connection#index", :as => "family_connection"
+ end
+ end
+
+ assert_equal({ controller: "connection/manage",
+ action: "index", }, request_path_params("/connection/manage"))
+
+ url = controller.url_for(controller: "connection", only_path: true)
+ assert_equal "/connection/connection", url
+
+ url = controller.url_for(use_route: "family_connection",
+ controller: "connection", only_path: true)
+ assert_equal "/connection", url
+ end
+
+ def test_action_left_off_when_id_is_recalled
+ @set = make_set false
+
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get ":controller(/:action(/:id))"
+ end
+ end
+
+ get URI("http://test.host/books/show/10")
+
+ assert_equal "/books", controller.url_for(controller: "books",
+ only_path: true,
+ action: "index")
+ end
+
+ def test_query_params_will_be_shown_when_recalled
+ @set = make_set false
+
+ set.draw do
+ get "show_weblog/:parameter" => "weblog#show"
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller(/:action(/:id))"
+ end
+ end
+
+ get URI("http://test.host/weblog/show/1")
+
+ assert_equal "/weblog/edit?parameter=1", controller.url_for(
+ action: "edit", parameter: 1, only_path: true)
+ end
+
+ def test_format_is_not_inherit
+ set.draw do
+ get "/posts(.:format)" => "posts#index"
+ end
+
+ get URI("http://test.host/posts.xml")
+ assert_equal({ controller: "posts", action: "index", format: "xml" },
+ controller.request.path_parameters)
+
+ assert_equal "/posts", controller.url_for(
+ controller: "posts", only_path: true)
+
+ assert_equal "/posts.xml", controller.url_for(
+ controller: "posts", format: "xml", only_path: true)
+ end
+
+ def test_expiry_determination_should_consider_values_with_to_param
+ @set = make_set false
+
+ set.draw { ActiveSupport::Deprecation.silence { get "projects/:project_id/:controller/:action" } }
+
+ get URI("http://test.host/projects/1/weblog/show")
+
+ assert_equal(
+ { controller: "weblog", action: "show", project_id: "1" },
+ controller.request.path_parameters)
+
+ assert_equal "/projects/1/weblog/show",
+ controller.url_for(action: "show", project_id: 1, only_path: true)
+ end
+
+ def test_named_route_in_nested_resource
+ set.draw do
+ resources :projects do
+ member do
+ get "milestones" => "milestones#index", :as => "milestones"
+ end
+ end
+ end
+
+ params = set.recognize_path("/projects/1/milestones", method: :get)
+ assert_equal("milestones", params[:controller])
+ assert_equal("index", params[:action])
+ end
+
+ def test_setting_root_in_namespace_using_symbol
+ assert_nothing_raised do
+ set.draw do
+ namespace :admin do
+ root to: "home#index"
+ end
+ end
+ end
+ end
+
+ def test_setting_root_in_namespace_using_string
+ assert_nothing_raised do
+ set.draw do
+ namespace "admin" do
+ root to: "home#index"
+ end
+ end
+ end
+ end
+
+ def test_route_constraints_with_unsupported_regexp_options_must_error
+ assert_raise ArgumentError do
+ set.draw do
+ get "page/:name" => "pages#show",
+ :constraints => { name: /(david|jamis)/m }
+ end
+ end
+ end
+
+ def test_route_constraints_with_supported_options_must_not_error
+ assert_nothing_raised do
+ set.draw do
+ get "page/:name" => "pages#show",
+ :constraints => { name: /(david|jamis)/i }
+ end
+ end
+ assert_nothing_raised do
+ set.draw do
+ get "page/:name" => "pages#show",
+ :constraints => { name: / # Desperately overcommented regexp
+ ( #Either
+ david #The Creator
+ | #Or
+ jamis #The Deployer
+ )/x }
+ end
+ end
+ end
+
+ def test_route_with_subdomain_and_constraints_must_receive_params
+ name_param = nil
+ set.draw do
+ get "page/:name" => "pages#show", :constraints => lambda { |request|
+ name_param = request.params[:name]
+ return true
+ }
+ end
+ assert_equal({ controller: "pages", action: "show", name: "mypage" },
+ set.recognize_path("http://subdomain.example.org/page/mypage"))
+ assert_equal(name_param, "mypage")
+ end
+
+ def test_route_requirement_recognize_with_ignore_case
+ set.draw do
+ get "page/:name" => "pages#show",
+ :constraints => { name: /(david|jamis)/i }
+ end
+ assert_equal({ controller: "pages", action: "show", name: "jamis" }, set.recognize_path("/page/jamis"))
+ assert_raise ActionController::RoutingError do
+ set.recognize_path("/page/davidjamis")
+ end
+ assert_equal({ controller: "pages", action: "show", name: "DAVID" }, set.recognize_path("/page/DAVID"))
+ end
+
+ def test_route_requirement_generate_with_ignore_case
+ set.draw do
+ get "page/:name" => "pages#show",
+ :constraints => { name: /(david|jamis)/i }
+ end
+
+ url = url_for(set, controller: "pages", action: "show", name: "david")
+ assert_equal "/page/david", url
+ assert_raise(ActionController::UrlGenerationError) do
+ url_for(set, controller: "pages", action: "show", name: "davidjamis")
+ end
+ url = url_for(set, controller: "pages", action: "show", name: "JAMIS")
+ assert_equal "/page/JAMIS", url
+ end
+
+ def test_route_requirement_recognize_with_extended_syntax
+ set.draw do
+ get "page/:name" => "pages#show",
+ :constraints => { name: / # Desperately overcommented regexp
+ ( #Either
+ david #The Creator
+ | #Or
+ jamis #The Deployer
+ )/x }
+ end
+ assert_equal({ controller: "pages", action: "show", name: "jamis" }, set.recognize_path("/page/jamis"))
+ assert_equal({ controller: "pages", action: "show", name: "david" }, set.recognize_path("/page/david"))
+ assert_raise ActionController::RoutingError do
+ set.recognize_path("/page/david #The Creator")
+ end
+ assert_raise ActionController::RoutingError do
+ set.recognize_path("/page/David")
+ end
+ end
+
+ def test_route_requirement_with_xi_modifiers
+ set.draw do
+ get "page/:name" => "pages#show",
+ :constraints => { name: / # Desperately overcommented regexp
+ ( #Either
+ david #The Creator
+ | #Or
+ jamis #The Deployer
+ )/xi }
+ end
+
+ assert_equal({ controller: "pages", action: "show", name: "JAMIS" },
+ set.recognize_path("/page/JAMIS"))
+
+ assert_equal "/page/JAMIS",
+ url_for(set, controller: "pages", action: "show", name: "JAMIS")
+ end
+
+ def test_routes_with_symbols
+ set.draw do
+ get "unnamed", controller: :pages, action: :show, name: :as_symbol
+ get "named", controller: :pages, action: :show, name: :as_symbol, as: :named
+ end
+ assert_equal({ controller: "pages", action: "show", name: :as_symbol }, set.recognize_path("/unnamed"))
+ assert_equal({ controller: "pages", action: "show", name: :as_symbol }, set.recognize_path("/named"))
+ end
+
+ def test_regexp_chunk_should_add_question_mark_for_optionals
+ set.draw do
+ get "/" => "foo#index"
+ get "/hello" => "bar#index"
+ end
+
+ assert_equal "/", url_for(set, controller: "foo")
+ assert_equal "/hello", url_for(set, controller: "bar")
+
+ assert_equal({ controller: "foo", action: "index" }, set.recognize_path("/"))
+ assert_equal({ controller: "bar", action: "index" }, set.recognize_path("/hello"))
+ end
+
+ def test_assign_route_options_with_anchor_chars
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/cars/:action/:person/:car/", controller: "cars"
+ end
+ end
+
+ assert_equal "/cars/buy/1/2", url_for(set, controller: "cars", action: "buy", person: "1", car: "2")
+
+ assert_equal({ controller: "cars", action: "buy", person: "1", car: "2" }, set.recognize_path("/cars/buy/1/2"))
+ end
+
+ def test_segmentation_of_dot_path
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/books/:action.rss", controller: "books"
+ end
+ end
+
+ assert_equal "/books/list.rss", url_for(set, controller: "books", action: "list")
+
+ assert_equal({ controller: "books", action: "list" }, set.recognize_path("/books/list.rss"))
+ end
+
+ def test_segmentation_of_dynamic_dot_path
+ set.draw do
+ ActiveSupport::Deprecation.silence do
+ get "/books(/:action(.:format))", controller: "books"
+ end
+ end
+
+ assert_equal "/books/list.rss", url_for(set, controller: "books", action: "list", format: "rss")
+ assert_equal "/books/list.xml", url_for(set, controller: "books", action: "list", format: "xml")
+ assert_equal "/books/list", url_for(set, controller: "books", action: "list")
+ assert_equal "/books", url_for(set, controller: "books", action: "index")
+
+ assert_equal({ controller: "books", action: "list", format: "rss" }, set.recognize_path("/books/list.rss"))
+ assert_equal({ controller: "books", action: "list", format: "xml" }, set.recognize_path("/books/list.xml"))
+ assert_equal({ controller: "books", action: "list" }, set.recognize_path("/books/list"))
+ assert_equal({ controller: "books", action: "index" }, set.recognize_path("/books"))
+ end
+
+ def test_slashes_are_implied
+ set.draw { ActiveSupport::Deprecation.silence { get("/:controller(/:action(/:id))") } }
+
+ assert_equal "/content", url_for(set, controller: "content", action: "index")
+ assert_equal "/content/list", url_for(set, controller: "content", action: "list")
+ assert_equal "/content/show/1", url_for(set, controller: "content", action: "show", id: "1")
+
+ assert_equal({ controller: "content", action: "index" }, set.recognize_path("/content"))
+ assert_equal({ controller: "content", action: "index" }, set.recognize_path("/content/index"))
+ assert_equal({ controller: "content", action: "list" }, set.recognize_path("/content/list"))
+ assert_equal({ controller: "content", action: "show", id: "1" }, set.recognize_path("/content/show/1"))
+ end
+
+ def test_default_route_recognition
+ expected = { controller: "pages", action: "show", id: "10" }
+ assert_equal expected, default_route_set.recognize_path("/pages/show/10")
+ assert_equal expected, default_route_set.recognize_path("/pages/show/10/")
+
+ expected[:id] = "jamis"
+ assert_equal expected, default_route_set.recognize_path("/pages/show/jamis/")
+
+ expected.delete :id
+ assert_equal expected, default_route_set.recognize_path("/pages/show")
+ assert_equal expected, default_route_set.recognize_path("/pages/show/")
+
+ expected[:action] = "index"
+ assert_equal expected, default_route_set.recognize_path("/pages/")
+ assert_equal expected, default_route_set.recognize_path("/pages")
+
+ assert_raise(ActionController::RoutingError) { default_route_set.recognize_path("/") }
+ assert_raise(ActionController::RoutingError) { default_route_set.recognize_path("/pages/how/goood/it/is/to/be/free") }
+ end
+
+ def test_default_route_should_omit_default_action
+ assert_equal "/accounts", url_for(default_route_set, controller: "accounts", action: "index")
+ end
+
+ def test_default_route_should_include_default_action_when_id_present
+ assert_equal "/accounts/index/20", url_for(default_route_set, controller: "accounts", action: "index", id: "20")
+ end
+
+ def test_default_route_should_work_with_action_but_no_id
+ assert_equal "/accounts/list_all", url_for(default_route_set, controller: "accounts", action: "list_all")
+ end
+
+ def test_default_route_should_uri_escape_pluses
+ expected = { controller: "pages", action: "show", id: "hello world" }
+ assert_equal expected, default_route_set.recognize_path("/pages/show/hello%20world")
+ assert_equal "/pages/show/hello%20world", url_for(default_route_set, expected)
+
+ expected[:id] = "hello+world"
+ assert_equal expected, default_route_set.recognize_path("/pages/show/hello+world")
+ assert_equal expected, default_route_set.recognize_path("/pages/show/hello%2Bworld")
+ assert_equal "/pages/show/hello+world", url_for(default_route_set, expected)
+ end
+
+ def test_build_empty_query_string
+ assert_uri_equal "/foo", url_for(default_route_set, controller: "foo")
+ end
+
+ def test_build_query_string_with_nil_value
+ assert_uri_equal "/foo", url_for(default_route_set, controller: "foo", x: nil)
+ end
+
+ def test_simple_build_query_string
+ assert_uri_equal "/foo?x=1&y=2", url_for(default_route_set, controller: "foo", x: "1", y: "2")
+ end
+
+ def test_convert_ints_build_query_string
+ assert_uri_equal "/foo?x=1&y=2", url_for(default_route_set, controller: "foo", x: 1, y: 2)
+ end
+
+ def test_escape_spaces_build_query_string
+ assert_uri_equal "/foo?x=hello+world&y=goodbye+world", url_for(default_route_set, controller: "foo", x: "hello world", y: "goodbye world")
+ end
+
+ def test_expand_array_build_query_string
+ assert_uri_equal "/foo?x%5B%5D=1&x%5B%5D=2", url_for(default_route_set, controller: "foo", x: [1, 2])
+ end
+
+ def test_escape_spaces_build_query_string_selected_keys
+ assert_uri_equal "/foo?x=hello+world", url_for(default_route_set, controller: "foo", x: "hello world")
+ end
+
+ def test_generate_with_default_params
+ set.draw do
+ get "dummy/page/:page" => "dummy#show"
+ get "dummy/dots/page.:page" => "dummy#dots"
+ get "ibocorp(/:page)" => "ibocorp#show",
+ :constraints => { page: /\d+/ },
+ :defaults => { page: 1 }
+
+ ActiveSupport::Deprecation.silence do
+ get ":controller/:action/:id"
+ end
+ end
+
+ assert_equal "/ibocorp", url_for(set, controller: "ibocorp", action: "show", page: 1)
+ end
+
+ include ActionDispatch::RoutingVerbs
+
+ alias :routes :set
+
+ def test_generate_with_optional_params_recalls_last_request
+ @set = make_set false
+
+ set.draw do
+ get "blog/", controller: "blog", action: "index"
+
+ get "blog(/:year(/:month(/:day)))",
+ controller: "blog",
+ action: "show_date",
+ constraints: { year: /(19|20)\d\d/, month: /[01]?\d/, day: /[0-3]?\d/ },
+ day: nil, month: nil
+
+ get "blog/show/:id", controller: "blog", action: "show", id: /\d+/
+
+ ActiveSupport::Deprecation.silence do
+ get "blog/:controller/:action(/:id)"
+ end
+
+ get "*anything", controller: "blog", action: "unknown_request"
+ end
+
+ recognize_path = ->(path) {
+ get(URI("http://example.org" + path))
+ controller.request.path_parameters
+ }
+
+ assert_equal({ controller: "blog", action: "index" }, recognize_path.("/blog"))
+ assert_equal({ controller: "blog", action: "show", id: "123" }, recognize_path.("/blog/show/123"))
+ assert_equal({ controller: "blog", action: "show_date", year: "2004", day: nil, month: nil }, recognize_path.("/blog/2004"))
+ assert_equal({ controller: "blog", action: "show_date", year: "2004", month: "12", day: nil }, recognize_path.("/blog/2004/12"))
+ assert_equal({ controller: "blog", action: "show_date", year: "2004", month: "12", day: "25" }, recognize_path.("/blog/2004/12/25"))
+ assert_equal({ controller: "articles", action: "edit", id: "123" }, recognize_path.("/blog/articles/edit/123"))
+ assert_equal({ controller: "articles", action: "show_stats" }, recognize_path.("/blog/articles/show_stats"))
+ assert_equal({ controller: "blog", action: "unknown_request", anything: "blog/wibble" }, recognize_path.("/blog/wibble"))
+ assert_equal({ controller: "blog", action: "unknown_request", anything: "junk" }, recognize_path.("/junk"))
+
+ get URI("http://example.org/blog/2006/07/28")
+
+ assert_equal({ controller: "blog", action: "show_date", year: "2006", month: "07", day: "28" }, controller.request.path_parameters)
+ assert_equal("/blog/2006/07/25", controller.url_for(day: 25, only_path: true))
+ assert_equal("/blog/2005", controller.url_for(year: 2005, only_path: true))
+ assert_equal("/blog/show/123", controller.url_for(action: "show", id: 123, only_path: true))
+ assert_equal("/blog/2006", controller.url_for(year: 2006, only_path: true))
+ assert_equal("/blog/2006", controller.url_for(year: 2006, month: nil, only_path: true))
+ end
+
+ private
+ def assert_uri_equal(expected, actual)
+ assert_equal(sort_query_string_params(expected), sort_query_string_params(actual))
+ end
+
+ def sort_query_string_params(uri)
+ path, qs = uri.split("?")
+ qs = qs.split("&").sort.join("&") if qs
+ qs ? "#{path}?#{qs}" : path
+ end
+end
+
+class RackMountIntegrationTests < ActiveSupport::TestCase
+ include RoutingTestHelpers
+
+ Model = Struct.new(:to_param)
+
+ Mapping = lambda {
+ namespace :admin do
+ resources :users, :posts
+ end
+
+ namespace "api" do
+ root to: "users#index"
+ end
+
+ get "/blog(/:year(/:month(/:day)))" => "posts#show_date",
+ :constraints => {
+ year: /(19|20)\d\d/,
+ month: /[01]?\d/,
+ day: /[0-3]?\d/
+ },
+ :day => nil,
+ :month => nil
+
+ get "archive/:year", controller: "archive", action: "index",
+ defaults: { year: nil },
+ constraints: { year: /\d{4}/ },
+ as: "blog"
+
+ resources :people
+ get "legacy/people" => "people#index", :legacy => "true"
+
+ get "symbols", controller: :symbols, action: :show, name: :as_symbol
+ get "id_default(/:id)" => "foo#id_default", :id => 1
+ match "get_or_post" => "foo#get_or_post", :via => [:get, :post]
+ get "optional/:optional" => "posts#index"
+ get "projects/:project_id" => "project#index", :as => "project"
+ get "clients" => "projects#index"
+
+ get "ignorecase/geocode/:postalcode" => "geocode#show", :postalcode => /hx\d\d-\d[a-z]{2}/i
+ get "extended/geocode/:postalcode" => "geocode#show", :constraints => {
+ postalcode: /# Postcode format
+ \d{5} #Prefix
+ (-\d{4})? #Suffix
+ /x
+ }, :as => "geocode"
+
+ get "news(.:format)" => "news#index"
+
+ ActiveSupport::Deprecation.silence do
+ get "comment/:id(/:action)" => "comments#show"
+ get "ws/:controller(/:action(/:id))", ws: true
+ get "account(/:action)" => "account#subscription"
+ get "pages/:page_id/:controller(/:action(/:id))"
+ get ":controller/ping", action: "ping"
+ end
+
+ get "こんにちは/世界", controller: "news", action: "index"
+
+ ActiveSupport::Deprecation.silence do
+ match ":controller(/:action(/:id))(.:format)", via: :all
+ end
+
+ root to: "news#index"
+ }
+
+ attr_reader :routes
+ attr_reader :controller
+
+ def setup
+ @routes = ActionDispatch::Routing::RouteSet.new
+ @routes.draw(&Mapping)
+ end
+
+ def test_recognize_path
+ assert_equal({ controller: "admin/users", action: "index" }, @routes.recognize_path("/admin/users", method: :get))
+ assert_equal({ controller: "admin/users", action: "create" }, @routes.recognize_path("/admin/users", method: :post))
+ assert_equal({ controller: "admin/users", action: "new" }, @routes.recognize_path("/admin/users/new", method: :get))
+ assert_equal({ controller: "admin/users", action: "show", id: "1" }, @routes.recognize_path("/admin/users/1", method: :get))
+ assert_equal({ controller: "admin/users", action: "update", id: "1" }, @routes.recognize_path("/admin/users/1", method: :put))
+ assert_equal({ controller: "admin/users", action: "destroy", id: "1" }, @routes.recognize_path("/admin/users/1", method: :delete))
+ assert_equal({ controller: "admin/users", action: "edit", id: "1" }, @routes.recognize_path("/admin/users/1/edit", method: :get))
+
+ assert_equal({ controller: "admin/posts", action: "index" }, @routes.recognize_path("/admin/posts", method: :get))
+ assert_equal({ controller: "admin/posts", action: "new" }, @routes.recognize_path("/admin/posts/new", method: :get))
+
+ assert_equal({ controller: "api/users", action: "index" }, @routes.recognize_path("/api", method: :get))
+ assert_equal({ controller: "api/users", action: "index" }, @routes.recognize_path("/api/", method: :get))
+
+ assert_equal({ controller: "posts", action: "show_date", year: "2009", month: nil, day: nil }, @routes.recognize_path("/blog/2009", method: :get))
+ assert_equal({ controller: "posts", action: "show_date", year: "2009", month: "01", day: nil }, @routes.recognize_path("/blog/2009/01", method: :get))
+ assert_equal({ controller: "posts", action: "show_date", year: "2009", month: "01", day: "01" }, @routes.recognize_path("/blog/2009/01/01", method: :get))
+
+ assert_equal({ controller: "archive", action: "index", year: "2010" }, @routes.recognize_path("/archive/2010"))
+ assert_equal({ controller: "archive", action: "index" }, @routes.recognize_path("/archive"))
+
+ assert_equal({ controller: "people", action: "index" }, @routes.recognize_path("/people", method: :get))
+ assert_equal({ controller: "people", action: "index", format: "xml" }, @routes.recognize_path("/people.xml", method: :get))
+ assert_equal({ controller: "people", action: "create" }, @routes.recognize_path("/people", method: :post))
+ assert_equal({ controller: "people", action: "new" }, @routes.recognize_path("/people/new", method: :get))
+ assert_equal({ controller: "people", action: "show", id: "1" }, @routes.recognize_path("/people/1", method: :get))
+ assert_equal({ controller: "people", action: "show", id: "1", format: "xml" }, @routes.recognize_path("/people/1.xml", method: :get))
+ assert_equal({ controller: "people", action: "update", id: "1" }, @routes.recognize_path("/people/1", method: :put))
+ assert_equal({ controller: "people", action: "destroy", id: "1" }, @routes.recognize_path("/people/1", method: :delete))
+ assert_equal({ controller: "people", action: "edit", id: "1" }, @routes.recognize_path("/people/1/edit", method: :get))
+ assert_equal({ controller: "people", action: "edit", id: "1", format: "xml" }, @routes.recognize_path("/people/1/edit.xml", method: :get))
+
+ assert_equal({ controller: "symbols", action: "show", name: :as_symbol }, @routes.recognize_path("/symbols"))
+ assert_equal({ controller: "foo", action: "id_default", id: "1" }, @routes.recognize_path("/id_default/1"))
+ assert_equal({ controller: "foo", action: "id_default", id: "2" }, @routes.recognize_path("/id_default/2"))
+ assert_equal({ controller: "foo", action: "id_default", id: 1 }, @routes.recognize_path("/id_default"))
+ assert_equal({ controller: "foo", action: "get_or_post" }, @routes.recognize_path("/get_or_post", method: :get))
+ assert_equal({ controller: "foo", action: "get_or_post" }, @routes.recognize_path("/get_or_post", method: :post))
+ assert_raise(ActionController::RoutingError) { @routes.recognize_path("/get_or_post", method: :put) }
+ assert_raise(ActionController::RoutingError) { @routes.recognize_path("/get_or_post", method: :delete) }
+
+ assert_equal({ controller: "posts", action: "index", optional: "bar" }, @routes.recognize_path("/optional/bar"))
+ assert_raise(ActionController::RoutingError) { @routes.recognize_path("/optional") }
+
+ assert_equal({ controller: "posts", action: "show", id: "1", ws: true }, @routes.recognize_path("/ws/posts/show/1", method: :get))
+ assert_equal({ controller: "posts", action: "list", ws: true }, @routes.recognize_path("/ws/posts/list", method: :get))
+ assert_equal({ controller: "posts", action: "index", ws: true }, @routes.recognize_path("/ws/posts", method: :get))
+
+ assert_equal({ controller: "account", action: "subscription" }, @routes.recognize_path("/account", method: :get))
+ assert_equal({ controller: "account", action: "subscription" }, @routes.recognize_path("/account/subscription", method: :get))
+ assert_equal({ controller: "account", action: "billing" }, @routes.recognize_path("/account/billing", method: :get))
+
+ assert_equal({ page_id: "1", controller: "notes", action: "index" }, @routes.recognize_path("/pages/1/notes", method: :get))
+ assert_equal({ page_id: "1", controller: "notes", action: "list" }, @routes.recognize_path("/pages/1/notes/list", method: :get))
+ assert_equal({ page_id: "1", controller: "notes", action: "show", id: "2" }, @routes.recognize_path("/pages/1/notes/show/2", method: :get))
+
+ assert_equal({ controller: "posts", action: "ping" }, @routes.recognize_path("/posts/ping", method: :get))
+ assert_equal({ controller: "posts", action: "index" }, @routes.recognize_path("/posts", method: :get))
+ assert_equal({ controller: "posts", action: "index" }, @routes.recognize_path("/posts/index", method: :get))
+ assert_equal({ controller: "posts", action: "show" }, @routes.recognize_path("/posts/show", method: :get))
+ assert_equal({ controller: "posts", action: "show", id: "1" }, @routes.recognize_path("/posts/show/1", method: :get))
+ assert_equal({ controller: "posts", action: "create" }, @routes.recognize_path("/posts/create", method: :post))
+
+ assert_equal({ controller: "geocode", action: "show", postalcode: "hx12-1az" }, @routes.recognize_path("/ignorecase/geocode/hx12-1az"))
+ assert_equal({ controller: "geocode", action: "show", postalcode: "hx12-1AZ" }, @routes.recognize_path("/ignorecase/geocode/hx12-1AZ"))
+ assert_equal({ controller: "geocode", action: "show", postalcode: "12345-1234" }, @routes.recognize_path("/extended/geocode/12345-1234"))
+ assert_equal({ controller: "geocode", action: "show", postalcode: "12345" }, @routes.recognize_path("/extended/geocode/12345"))
+
+ assert_equal({ controller: "news", action: "index" }, @routes.recognize_path("/", method: :get))
+ assert_equal({ controller: "news", action: "index", format: "rss" }, @routes.recognize_path("/news.rss", method: :get))
+
+ assert_raise(ActionController::RoutingError) { @routes.recognize_path("/none", method: :get) }
+ end
+
+ def test_generate_extras
+ assert_equal ["/people", []], @routes.generate_extras(controller: "people")
+ assert_equal ["/people", [:foo]], @routes.generate_extras(controller: "people", foo: "bar")
+ assert_equal ["/people", []], @routes.generate_extras(controller: "people", action: "index")
+ assert_equal ["/people", [:foo]], @routes.generate_extras(controller: "people", action: "index", foo: "bar")
+ assert_equal ["/people/new", []], @routes.generate_extras(controller: "people", action: "new")
+ assert_equal ["/people/new", [:foo]], @routes.generate_extras(controller: "people", action: "new", foo: "bar")
+ assert_equal ["/people/1", []], @routes.generate_extras(controller: "people", action: "show", id: "1")
+ assert_equal ["/people/1", [:bar, :foo]], sort_extras!(@routes.generate_extras(controller: "people", action: "show", id: "1", foo: "2", bar: "3"))
+ assert_equal ["/people", [:person]], @routes.generate_extras(controller: "people", action: "create", person: { first_name: "Josh", last_name: "Peek" })
+ assert_equal ["/people", [:people]], @routes.generate_extras(controller: "people", action: "create", people: ["Josh", "Dave"])
+
+ assert_equal ["/posts/show/1", []], @routes.generate_extras(controller: "posts", action: "show", id: "1")
+ assert_equal ["/posts/show/1", [:bar, :foo]], sort_extras!(@routes.generate_extras(controller: "posts", action: "show", id: "1", foo: "2", bar: "3"))
+ assert_equal ["/posts", []], @routes.generate_extras(controller: "posts", action: "index")
+ assert_equal ["/posts", [:foo]], @routes.generate_extras(controller: "posts", action: "index", foo: "bar")
+ end
+
+ def test_extras
+ params = { controller: "people" }
+ assert_equal [], @routes.extra_keys(params)
+ assert_equal({ controller: "people", action: "index" }, params)
+
+ params = { controller: "people", foo: "bar" }
+ assert_equal [:foo], @routes.extra_keys(params)
+ assert_equal({ controller: "people", action: "index", foo: "bar" }, params)
+
+ params = { controller: "people", action: "create", person: { name: "Josh" } }
+ assert_equal [:person], @routes.extra_keys(params)
+ assert_equal({ controller: "people", action: "create", person: { name: "Josh" } }, params)
+ end
+
+ def test_unicode_path
+ assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI.parser.escape("こんにちは/世界"), method: :get))
+ end
+
+ def test_downcased_unicode_path
+ assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI.parser.escape("こんにちは/世界").downcase, method: :get))
+ end
+
+ private
+ def sort_extras!(extras)
+ if extras.length == 2
+ extras[1].sort! { |a, b| a.to_s <=> b.to_s }
+ end
+ extras
+ end
+end