Skip to content

Commit 2b1b75e

Browse files
authored
Merge pull request rails#41901 from Shopify/cache-base-class
Precompute Inheritance.base_class
2 parents dcf66b3 + 8c580d7 commit 2b1b75e

File tree

2 files changed

+38
-13
lines changed

2 files changed

+38
-13
lines changed

activerecord/lib/active_record/inheritance.rb

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ module Inheritance
4343
# Determines whether to store the full constant name including namespace when using STI.
4444
# This is true, by default.
4545
class_attribute :store_full_sti_class, instance_writer: false, default: true
46+
47+
set_base_class
4648
end
4749

4850
module ClassMethods
@@ -98,17 +100,7 @@ def finder_needs_type_condition? #:nodoc:
98100
#
99101
# If B < A and C < B and if A is an abstract_class then both B.base_class
100102
# and C.base_class would return B as the answer since A is an abstract_class.
101-
def base_class
102-
unless self < Base
103-
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
104-
end
105-
106-
if superclass == Base || superclass.abstract_class?
107-
self
108-
else
109-
superclass.base_class
110-
end
111-
end
103+
attr_reader :base_class
112104

113105
# Returns whether the class is a base class.
114106
# See #base_class for more information.
@@ -218,10 +210,24 @@ def polymorphic_class_for(name)
218210
end
219211

220212
def inherited(subclass)
213+
subclass.set_base_class
221214
subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
222215
super
223216
end
224217

218+
def dup # :nodoc:
219+
# `initialize_dup` / `initialize_copy` don't work when defined
220+
# in the `singleton_class`.
221+
other = super
222+
other.set_base_class
223+
other
224+
end
225+
226+
def initialize_clone(other) # :nodoc:
227+
super
228+
set_base_class
229+
end
230+
225231
protected
226232
# Returns the class type of the record using the current module as a prefix. So descendants of
227233
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
@@ -253,6 +259,24 @@ def compute_type(type_name)
253259
end
254260
end
255261

262+
def set_base_class # :nodoc:
263+
@base_class = begin
264+
if self == Base
265+
self
266+
else
267+
unless self < Base
268+
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
269+
end
270+
271+
if superclass == Base || superclass.abstract_class?
272+
self
273+
else
274+
superclass.base_class
275+
end
276+
end
277+
end
278+
end
279+
256280
private
257281
# Called by +instantiate+ to decide which class to use for a new
258282
# record instance. For single-table inheritance, we check the record

activerecord/test/cases/inheritance_test.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,9 @@ def test_abstract_inheritance_base_class
190190
end
191191

192192
def test_base_class_activerecord_error
193-
klass = Class.new { include ActiveRecord::Inheritance }
194-
assert_raise(ActiveRecord::ActiveRecordError) { klass.base_class }
193+
assert_raise(ActiveRecord::ActiveRecordError) do
194+
Class.new { include ActiveRecord::Inheritance }
195+
end
195196
end
196197

197198
def test_a_bad_type_column

0 commit comments

Comments
 (0)