Skip to content

Commit 08473e3

Browse files
authored
Merge pull request rails#49728 from jhawthorn/callbacks_sharing
Reduce memory used by ActiveSupport::Callbacks
2 parents 5534a22 + 578cdf8 commit 08473e3

File tree

1 file changed

+72
-113
lines changed

1 file changed

+72
-113
lines changed

activesupport/lib/active_support/callbacks.rb

Lines changed: 72 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def run_callbacks(kind, type = nil)
150150
def halted_callback_hook(filter, name)
151151
end
152152

153-
module Conditionals # :nodoc:
153+
module Conditionals # :nodoc: all
154154
class Value
155155
def initialize(&block)
156156
@block = block
@@ -159,128 +159,76 @@ def call(target, value); @block.call(value); end
159159
end
160160
end
161161

162-
module Filters
162+
module Filters # :nodoc: all
163163
Environment = Struct.new(:target, :halted, :value)
164164

165165
class Before
166-
def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
166+
def initialize(user_callback, user_conditions, chain_config, filter, name)
167167
halted_lambda = chain_config[:terminator]
168-
169-
if user_conditions.any?
170-
halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
171-
else
172-
halting(callback_sequence, user_callback, halted_lambda, filter, name)
173-
end
168+
@user_callback, @user_conditions, @halted_lambda, @filter, @name = user_callback, user_conditions, halted_lambda, filter, name
169+
freeze
174170
end
171+
attr_reader :user_callback, :user_conditions, :halted_lambda, :filter, :name
175172

176-
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
177-
callback_sequence.before do |env|
178-
target = env.target
179-
value = env.value
180-
halted = env.halted
173+
def call(env)
174+
target = env.target
175+
value = env.value
176+
halted = env.halted
181177

182-
if !halted && user_conditions.all? { |c| c.call(target, value) }
183-
result_lambda = -> { user_callback.call target, value }
184-
env.halted = halted_lambda.call(target, result_lambda)
185-
if env.halted
186-
target.send :halted_callback_hook, filter, name
187-
end
178+
if !halted && user_conditions.all? { |c| c.call(target, value) }
179+
result_lambda = -> { user_callback.call target, value }
180+
env.halted = halted_lambda.call(target, result_lambda)
181+
if env.halted
182+
target.send :halted_callback_hook, filter, name
188183
end
189-
190-
env
191184
end
192-
end
193-
private_class_method :halting_and_conditional
194-
195-
def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
196-
callback_sequence.before do |env|
197-
target = env.target
198-
value = env.value
199-
halted = env.halted
200185

201-
unless halted
202-
result_lambda = -> { user_callback.call target, value }
203-
env.halted = halted_lambda.call(target, result_lambda)
204-
if env.halted
205-
target.send :halted_callback_hook, filter, name
206-
end
207-
end
186+
env
187+
end
208188

209-
env
210-
end
189+
def apply(callback_sequence)
190+
callback_sequence.before(self)
211191
end
212-
private_class_method :halting
213192
end
214193

215194
class After
216-
def self.build(callback_sequence, user_callback, user_conditions, chain_config)
217-
if chain_config[:skip_after_callbacks_if_terminated]
218-
if user_conditions.any?
219-
halting_and_conditional(callback_sequence, user_callback, user_conditions)
220-
else
221-
halting(callback_sequence, user_callback)
222-
end
223-
else
224-
if user_conditions.any?
225-
conditional callback_sequence, user_callback, user_conditions
226-
else
227-
simple callback_sequence, user_callback
228-
end
229-
end
195+
attr_reader :user_callback, :user_conditions, :halting
196+
def initialize(user_callback, user_conditions, chain_config)
197+
halting = chain_config[:skip_after_callbacks_if_terminated]
198+
@user_callback, @user_conditions, @halting = user_callback, user_conditions, halting
199+
freeze
230200
end
231201

232-
def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
233-
callback_sequence.after do |env|
234-
target = env.target
235-
value = env.value
236-
halted = env.halted
237-
238-
if !halted && user_conditions.all? { |c| c.call(target, value) }
239-
user_callback.call target, value
240-
end
202+
def call(env)
203+
target = env.target
204+
value = env.value
205+
halted = env.halted
241206

242-
env
207+
if (!halted || !@halting) && user_conditions.all? { |c| c.call(target, value) }
208+
user_callback.call target, value
243209
end
244-
end
245-
private_class_method :halting_and_conditional
246-
247-
def self.halting(callback_sequence, user_callback)
248-
callback_sequence.after do |env|
249-
unless env.halted
250-
user_callback.call env.target, env.value
251-
end
252210

253-
env
254-
end
211+
env
255212
end
256-
private_class_method :halting
257-
258-
def self.conditional(callback_sequence, user_callback, user_conditions)
259-
callback_sequence.after do |env|
260-
target = env.target
261-
value = env.value
262213

263-
if user_conditions.all? { |c| c.call(target, value) }
264-
user_callback.call target, value
265-
end
266-
267-
env
268-
end
214+
def apply(callback_sequence)
215+
callback_sequence.after(self)
269216
end
270-
private_class_method :conditional
217+
end
271218

272-
def self.simple(callback_sequence, user_callback)
273-
callback_sequence.after do |env|
274-
user_callback.call env.target, env.value
219+
class Around
220+
def initialize(user_callback, user_conditions)
221+
@user_callback, @user_conditions = user_callback, user_conditions
222+
freeze
223+
end
275224

276-
env
277-
end
225+
def apply(callback_sequence)
226+
callback_sequence.around(@user_callback, @user_conditions)
278227
end
279-
private_class_method :simple
280228
end
281229
end
282230

283-
class Callback # :nodoc:#
231+
class Callback # :nodoc:
284232
def self.build(chain, filter, kind, options)
285233
if filter.is_a?(String)
286234
raise ArgumentError, <<-MSG.squish
@@ -329,19 +277,26 @@ def duplicates?(other)
329277
end
330278
end
331279

280+
def compiled
281+
@compiled ||=
282+
begin
283+
user_conditions = conditions_lambdas
284+
user_callback = CallTemplate.build(@filter, self)
285+
286+
case kind
287+
when :before
288+
Filters::Before.new(user_callback.make_lambda, user_conditions, chain_config, @filter, name)
289+
when :after
290+
Filters::After.new(user_callback.make_lambda, user_conditions, chain_config)
291+
when :around
292+
Filters::Around.new(user_callback, user_conditions)
293+
end
294+
end
295+
end
296+
332297
# Wraps code with filter
333298
def apply(callback_sequence)
334-
user_conditions = conditions_lambdas
335-
user_callback = CallTemplate.build(@filter, self)
336-
337-
case kind
338-
when :before
339-
Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name)
340-
when :after
341-
Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
342-
when :around
343-
callback_sequence.around(user_callback, user_conditions)
344-
end
299+
compiled.apply(callback_sequence)
345300
end
346301

347302
def current_scopes
@@ -368,14 +323,16 @@ def check_conditionals(conditionals)
368323
end
369324

370325
def conditions_lambdas
371-
@if.map { |c| CallTemplate.build(c, self).make_lambda } +
326+
conditions =
327+
@if.map { |c| CallTemplate.build(c, self).make_lambda } +
372328
@unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
329+
conditions.empty? ? EMPTY_ARRAY : conditions
373330
end
374331
end
375332

376333
# A future invocation of user-supplied code (either as a callback,
377334
# or a condition filter).
378-
module CallTemplate # :nodoc:
335+
module CallTemplate # :nodoc: all
379336
class MethodCall
380337
def initialize(method)
381338
@method_name = method
@@ -562,16 +519,18 @@ def initialize(nested = nil, call_template = nil, user_conditions = nil)
562519
@call_template = call_template
563520
@user_conditions = user_conditions
564521

565-
@before = []
566-
@after = []
522+
@before = nil
523+
@after = nil
567524
end
568525

569-
def before(&before)
526+
def before(before)
527+
@before ||= []
570528
@before.unshift(before)
571529
self
572530
end
573531

574-
def after(&after)
532+
def after(after)
533+
@after ||= []
575534
@after.push(after)
576535
self
577536
end
@@ -595,11 +554,11 @@ def expand_call_template(arg, block)
595554
end
596555

597556
def invoke_before(arg)
598-
@before.each { |b| b.call(arg) }
557+
@before&.each { |b| b.call(arg) }
599558
end
600559

601560
def invoke_after(arg)
602-
@after.each { |a| a.call(arg) }
561+
@after&.each { |a| a.call(arg) }
603562
end
604563
end
605564

0 commit comments

Comments
 (0)