Avoid bumping the class serial when invoking executor

This commit is contained in:
Matthew Draper 2016-10-03 01:34:27 +10:30
parent 7b63f56ce0
commit e8b36e7711
5 changed files with 87 additions and 19 deletions

View File

@ -6,6 +6,12 @@ module ActionView
class Digestor
@@digest_mutex = Mutex.new
module PerExecutionDigestCacheExpiry
def self.before(target)
ActionView::LookupContext::DetailsKey.clear
end
end
class << self
# Supported options:
#

View File

@ -40,7 +40,7 @@ module ActionView
initializer "action_view.per_request_digest_cache" do |app|
ActiveSupport.on_load(:action_view) do
if app.config.consider_all_requests_local
app.executor.to_run { ActionView::LookupContext::DetailsKey.clear }
app.executor.to_run ActionView::Digestor::PerExecutionDigestCacheExpiry
end
end
end

View File

@ -34,16 +34,14 @@ module ActiveRecord
def self.complete(enabled)
ActiveRecord::Base.connection.clear_query_cache
ActiveRecord::Base.connection.disable_query_cache! unless enabled
unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open?
ActiveRecord::Base.clear_active_connections!
end
end
def self.install_executor_hooks(executor = ActiveSupport::Executor)
executor.register_hook(self)
executor.to_complete do
unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open?
ActiveRecord::Base.clear_active_connections!
end
end
end
end
end

View File

@ -19,6 +19,23 @@ module ActiveSupport
set_callback(:complete, *args, &block)
end
class RunHook < Struct.new(:hook) # :nodoc:
def before(target)
hook_state = target.send(:hook_state)
hook_state[hook] = hook.run
end
end
class CompleteHook < Struct.new(:hook) # :nodoc:
def before(target)
hook_state = target.send(:hook_state)
if hook_state.key?(hook)
hook.complete hook_state[hook]
end
end
alias after before
end
# Register an object to be invoked during both the +run+ and
# +complete+ steps.
#
@ -29,19 +46,11 @@ module ActiveSupport
# invoked in that situation.)
def self.register_hook(hook, outer: false)
if outer
run_args = [prepend: true]
complete_args = [:after]
to_run RunHook.new(hook), prepend: true
to_complete :after, CompleteHook.new(hook)
else
run_args = complete_args = []
end
to_run(*run_args) do
hook_state[hook] = hook.run
end
to_complete(*complete_args) do
if hook_state.key?(hook)
hook.complete hook_state[hook]
end
to_run RunHook.new(hook)
to_complete CompleteHook.new(hook)
end
end

View File

@ -158,6 +158,61 @@ class ExecutorTest < ActiveSupport::TestCase
assert_equal :some_state, supplied_state
end
def test_hook_insertion_order
invoked = []
supplied_state = []
hook_class = Class.new do
attr_accessor :letter
define_method(:initialize) do |letter|
self.letter = letter
end
define_method(:run) do
invoked << :"run_#{letter}"
:"state_#{letter}"
end
define_method(:complete) do |state|
invoked << :"complete_#{letter}"
supplied_state << state
end
end
executor.register_hook(hook_class.new(:a))
executor.register_hook(hook_class.new(:b))
executor.register_hook(hook_class.new(:c), outer: true)
executor.register_hook(hook_class.new(:d))
executor.wrap {}
assert_equal [:run_c, :run_a, :run_b, :run_d, :complete_a, :complete_b, :complete_d, :complete_c], invoked
assert_equal [:state_a, :state_b, :state_d, :state_c], supplied_state
end
def test_class_serial_is_unaffected
hook = Class.new do
define_method(:run) do
nil
end
define_method(:complete) do |state|
nil
end
end.new
executor.register_hook(hook)
before = RubyVM.stat(:class_serial)
executor.wrap {}
executor.wrap {}
executor.wrap {}
after = RubyVM.stat(:class_serial)
assert_equal before, after
end
def test_separate_classes_can_wrap
other_executor = Class.new(ActiveSupport::Executor)