Module: ActiveSupport::DescendantsTracker

Included in:
AbstractController::Base, ActionMailer::Preview, ActiveRecord::Base, Rails::Railtie
Defined in:
activesupport/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:


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

def clear(classes) # :nodoc:
  raise "DescendantsTracker.clear was disabled because config.enable_reloading is false" 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


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

def descendants(klass)
  klass.descendants
end

.direct_descendants(klass) ⇒ Object


11
12
13
14
15
16
17
# File 'activesupport/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
# File 'activesupport/lib/active_support/descendants_tracker.rb', line 50

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

.native?Boolean

:nodoc:

Returns:

  • (Boolean)

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

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.


138
139
140
# File 'activesupport/lib/active_support/descendants_tracker.rb', line 138

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

.subclasses(klass) ⇒ Object


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

def subclasses(klass)
  klass.subclasses
end

Instance Method Details

#descendantsObject


88
89
90
# File 'activesupport/lib/active_support/descendants_tracker.rb', line 88

def descendants
  subclasses.concat(subclasses.flat_map(&:descendants))
end

#direct_descendantsObject


92
93
94
95
96
97
98
# File 'activesupport/lib/active_support/descendants_tracker.rb', line 92

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


153
154
155
156
# File 'activesupport/lib/active_support/descendants_tracker.rb', line 153

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

#subclassesObject


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

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