Only compute :only and :except callback conditions once

Ref: https://github.com/rails/rails/issues/38679

`_normalize_callback_options` mutates the options hash, but
doesn't remove the `:only` and `:except` conditions.

So if you call `before_action` & al with the same option hash
you end up with multiple instance of identical `:if` / `:unless` procs
This commit is contained in:
Vitalii Khustochka 2020-03-09 18:24:44 -05:00 committed by Jean Boussier
parent f89e18a3e0
commit edcccf4990
2 changed files with 43 additions and 1 deletions

View File

@ -69,7 +69,7 @@ module AbstractController
end
def _normalize_callback_option(options, from, to) # :nodoc:
if from = options[from]
if from = options.delete(from)
_from = Array(from).map(&:to_s).to_set
from = proc { |c| _from.include? c.action_name }
options[to] = Array(options[to]).unshift(from)

View File

@ -158,6 +158,48 @@ module AbstractController
end
end
class CallbacksWithReusedConditions < ControllerWithCallbacks
options = { only: :index }
before_action :list, options
before_action :authenticate, options
def index
self.response_body = @list.join(", ")
end
def public_data
@authenticated = "false"
self.response_body = @authenticated
end
private
def list
@list = ["Hello", "World"]
end
def authenticate
@list ||= []
@authenticated = "true"
end
end
class TestCallbacksWithReusedConditions < ActiveSupport::TestCase
def setup
@controller = CallbacksWithReusedConditions.new
end
test "when :only is specified, both actions triggered on that action" do
@controller.process(:index)
assert_equal "Hello, World", @controller.response_body
assert_equal "true", @controller.instance_variable_get("@authenticated")
end
test "when :only is specified, both actions are not triggered on other actions" do
@controller.process(:public_data)
assert_equal "false", @controller.response_body
end
end
class CallbacksWithArrayConditions < ControllerWithCallbacks
before_action :list, only: [:index, :listy]
before_action :authenticate, except: [:index, :listy]