From ef1eff576fcc1541a2bb21fe89e9ad33f3862806 Mon Sep 17 00:00:00 2001 From: Sam Bostock Date: Wed, 24 Jan 2024 23:45:13 -0500 Subject: [PATCH] Add `extenders_of` helper method This method is similar to `descendants_of`, but returns all modules which extend the given module. That is, all modules (including classes), whose singleton class (or parent thereof) includes the given module. --- lib/tapioca/runtime/reflection.rb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/tapioca/runtime/reflection.rb b/lib/tapioca/runtime/reflection.rb index 966cd1ebb..5d9c56f8c 100644 --- a/lib/tapioca/runtime/reflection.rb +++ b/lib/tapioca/runtime/reflection.rb @@ -172,6 +172,33 @@ def descendants_of(klass) T.unsafe(result) end + # Returns an array with all modules which extend the supplied module + # (i.e. all modules whose singleton class, or ancestor thereof, includes the supplied module). + # + # module M; end + # extenders_of(M) # => [] + # + # module E + # extend M + # end + # extenders_of(M) # => [E] + # + # class P + # extend M + # end + # extenders_of(M) # => [E, P] + # + # class C < P; end + # extenders_of(M) # => [E, P, C] + sig { params(mod: Module).returns(T::Array[Module]) } + def extenders_of(mod) + result = ObjectSpace.each_object(Module).select do |m| + T.cast(m, Module).singleton_class.included_modules.include?(mod) + end + + T.cast(result, T::Array[Module]) + end + # Examines the call stack to identify the closest location where a "require" is performed # by searching for the label "". If none is found, it returns the location # labeled "
", which is the original call site.