1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Avoid class_attrs for unused collection callbacks

Previously we would generate 4 class attributes for each collection
association whether or not they had any {before,after}_{add,remove}
callbacks.

Class attributes are relatively expensive as they eval code which then
redefines methods on assignment.

I don't think these attributes were intended to be a public API (docs do
not reference them, and we don't allow reconfiguring other callbacks
after associations have been defined, so I think these should be safe to
skip when empty.
This commit is contained in:
John Hawthorn 2021-09-23 13:07:00 -07:00
parent 6d42731ded
commit bfcac1359e
2 changed files with 14 additions and 3 deletions

View file

@ -30,11 +30,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
def self.define_callback(model, callback_name, name, options)
full_callback_name = "#{callback_name}_for_#{name}"
unless model.method_defined?(full_callback_name)
callback_values = Array(options[callback_name.to_sym])
method_defined = model.respond_to?(full_callback_name)
# If there are no callbacks, we must also check if a superclass had
# previously defined this association
return if callback_values.empty? && !method_defined
unless method_defined
model.class_attribute(full_callback_name, instance_accessor: false, instance_predicate: false)
end
callbacks = Array(options[callback_name.to_sym]).map do |callback|
callbacks = callback_values.map do |callback|
case callback
when Symbol
->(method, owner, record) { owner.send(callback, record) }

View file

@ -480,7 +480,11 @@ module ActiveRecord
def callbacks_for(callback_name)
full_callback_name = "#{callback_name}_for_#{reflection.name}"
owner.class.send(full_callback_name)
if owner.class.respond_to?(full_callback_name)
owner.class.send(full_callback_name)
else
[]
end
end
def include_in_memory?(record)