From d043920eb903d32a1e3540690d3b08d98d1b6d88 Mon Sep 17 00:00:00 2001 From: Simon Courtois Date: Thu, 27 Sep 2018 19:51:57 +0200 Subject: [PATCH] Fixing an edge case when using objects as constraints This PR fixes an issue when the following situation occurs. If you define a class like this class MyConstraint def call(*args) # for some reason this is defined end def matches?(*args) # checking the args end end and try to use it as a constraint get "/", to: "home#show", constraints: MyConstraint.new if its `matches?` method returns `false` there will be an error for the mapper will ask for the constraint arity, thinking it is a proc, lambda or method. This PR checks for the presence of the `arity` method on the constraint calling it only if present, preventing the error while keeping the basic behavior. --- actionpack/lib/action_dispatch/routing/mapper.rb | 14 +++++++++++++- actionpack/test/dispatch/routing_test.rb | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index b618b9c400..8386cb9689 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -50,7 +50,19 @@ module ActionDispatch private def constraint_args(constraint, request) - constraint.arity == 1 ? [request] : [request.path_parameters, request] + arity = if constraint.respond_to?(:arity) + constraint.arity + else + constraint.method(:call).arity + end + + if arity < 1 + [] + elsif arity == 1 + [request] + else + [request.path_parameters, request] + end end end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 5efbe5b553..ee87791538 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -115,6 +115,21 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 301, status end + def test_accepts_a_constraint_object_responding_to_call + constraint = Class.new do + def call(*); true; end + def matches?(*); false; end + end + + draw do + get "/", to: "home#show", constraints: constraint.new + end + + assert_nothing_raised do + get "/" + end + end + def test_namespace_with_controller_segment assert_raise(ArgumentError) do draw do