summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-01 23:31:56 +0200
committerPaul Buetow <paul@buetow.org>2026-03-01 23:31:56 +0200
commit1ef38a857c895e5f970d219ba6802b2627801620 (patch)
tree3b79793b68ce090e37b80b762124677808b6ddfd /lib
parent1217524955c7ea891ea85cb17dfd13f2b7b1fc3d (diff)
refactor: replace ObjectSpace scan with inherited-hook registry
ResourceDependencies#initialize iterated every live object in the Ruby heap (ObjectSpace.each_object(Class)) to discover Resource subclasses, which is O(heap), non-deterministic, and load-order-dependent (subclasses loaded after an instance was created were invisible to it). Add Resource.inherited + @@subclass_names so each subclass self-registers at load time. ResourceDependencies#initialize now just assigns the shared (frozen) registry reference instead of scanning ObjectSpace. Benefits: - O(1) constructor path instead of O(heap) - Load-order safe: new subclasses are visible to all instances immediately - Deterministic: set is built in require order, not GC-dependent order Also fix the "recource" typo in the ResourceDependencies comment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/dslkeywords/resource.rb23
1 files changed, 18 insertions, 5 deletions
diff --git a/lib/dslkeywords/resource.rb b/lib/dslkeywords/resource.rb
index 143815f..c1c2554 100644
--- a/lib/dslkeywords/resource.rb
+++ b/lib/dslkeywords/resource.rb
@@ -20,15 +20,14 @@ module RCM
end
end
- # To track recource dependencies
+ # To track resource dependencies
module ResourceDependencies
def initialize(...)
super(...)
@requires = Set.new
- @valid_resources = Set.new
- ObjectSpace.each_object(Class).each do |klass|
- @valid_resources << klass.to_s.sub('RCM::', '').downcase.to_sym if klass < Resource
- end
+ # Use the class-level registry (populated via Resource.inherited) rather
+ # than scanning ObjectSpace — deterministic, load-order-safe, and O(1).
+ @valid_resources = Resource.subclass_names
end
def method_missing(method_name, *args)
@@ -93,6 +92,20 @@ module RCM
class NoSuchResourceObject < StandardError; end
@@resource_find_cache = {}
+ # Class-level registry: every subclass is registered here when it is
+ # first loaded (via the inherited hook), so ResourceDependencies can
+ # look up valid keyword names without scanning ObjectSpace.
+ @@subclass_names = Set.new
+
+ def self.inherited(subclass)
+ super
+ @@subclass_names << subclass.to_s.sub('RCM::', '').downcase.to_sym
+ end
+
+ # Return a frozen snapshot so callers cannot accidentally mutate the
+ # shared registry through the @valid_resources instance variable.
+ def self.subclass_names = @@subclass_names.freeze
+
def self.find(id)
return @@resource_find_cache[id] if @@resource_find_cache.key?(id)