Module: ActiveSupport::DescendantsTracker

Defined in:
lib/active_support/descendants_tracker.rb

Overview

This module provides an internal implementation to track descendants which is faster than iterating through ObjectSpace.

Defined Under Namespace

Classes: DescendantsArray

Constant Summary collapse

@@excluded_descendants =
if RUBY_ENGINE == "ruby"
  # On MRI `ObjectSpace::WeakMap` keys are weak references.
  # So we can simply use WeakMap as a `Set`.
  ObjectSpace::WeakMap.new
else
  # On TruffleRuby `ObjectSpace::WeakMap` keys are strong references.
  # So we use `object_id` as a key and the actual object as a value.
  #
  # JRuby for now doesn't have Class#descendant, but when it will, it will likely
  # have the same WeakMap semantic than Truffle so we future proof this as much as possible.
  class WeakSet # :nodoc:
    def initialize
      @map = ObjectSpace::WeakMap.new
    end
     def [](object)
      @map.key?(object.object_id)
    end
     def []=(object, _present)
      @map[object.object_id] = object
    end
  end
  WeakSet.new
end
@@direct_descendants =
{}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.clear(classes) ⇒ Object

:nodoc:



67
68
69
70
71
72
73
74
75
76
# File 'lib/active_support/descendants_tracker.rb', line 67

def clear(classes) # :nodoc:
  raise "DescendantsTracker.clear was disabled because config.cache_classes = true" if @clear_disabled

  classes.each do |klass|
    @@excluded_descendants[klass] = true
    klass.descendants.each do |descendant|
      @@excluded_descendants[descendant] = true
    end
  end
end

.descendants(klass) ⇒ Object



63
64
65
# File 'lib/active_support/descendants_tracker.rb', line 63

def descendants(klass)
  klass.descendants
end

.direct_descendants(klass) ⇒ Object



11
12
13
14
15
16
17
# File 'lib/active_support/descendants_tracker.rb', line 11

def direct_descendants(klass)
  ActiveSupport::Deprecation.warn(<<~MSG)
    ActiveSupport::DescendantsTracker.direct_descendants is deprecated and will be removed in Rails 7.1.
    Use ActiveSupport::DescendantsTracker.subclasses instead.
  MSG
  subclasses(klass)
end

.disable_clear!Object

:nodoc:



50
51
52
53
54
55
56
57
# File 'lib/active_support/descendants_tracker.rb', line 50

def disable_clear! # :nodoc:
  unless @clear_disabled
    @clear_disabled = true
    remove_method(:subclasses)
    remove_method(:descendants)
    @@excluded_descendants = nil
  end
end

.native?Boolean

:nodoc:

Returns:

  • (Boolean)


78
79
80
# File 'lib/active_support/descendants_tracker.rb', line 78

def native? # :nodoc:
  true
end

.store_inherited(klass, descendant) ⇒ Object

This is the only method that is not thread safe, but is only ever called during the eager loading phase.



141
142
143
# File 'lib/active_support/descendants_tracker.rb', line 141

def store_inherited(klass, descendant)
  (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
end

.subclasses(klass) ⇒ Object



59
60
61
# File 'lib/active_support/descendants_tracker.rb', line 59

def subclasses(klass)
  klass.subclasses
end

Instance Method Details

#descendantsObject



89
90
91
92
93
# File 'lib/active_support/descendants_tracker.rb', line 89

def descendants
  descendants = super
  descendants.reject! { |d| @@excluded_descendants[d] }
  descendants
end

#direct_descendantsObject



95
96
97
98
99
100
101
# File 'lib/active_support/descendants_tracker.rb', line 95

def direct_descendants
  ActiveSupport::Deprecation.warn(<<~MSG)
    ActiveSupport::DescendantsTracker#direct_descendants is deprecated and will be removed in Rails 7.1.
    Use #subclasses instead.
  MSG
  subclasses
end

#inherited(base) ⇒ Object



156
157
158
159
# File 'lib/active_support/descendants_tracker.rb', line 156

def inherited(base)
  DescendantsTracker.store_inherited(self, base)
  super
end

#subclassesObject



83
84
85
86
87
# File 'lib/active_support/descendants_tracker.rb', line 83

def subclasses
  subclasses = super
  subclasses.reject! { |d| @@excluded_descendants[d] }
  subclasses
end