Preview Ruby 4.1 is a preview. It can change before the final release. Switch to Ruby 4.0 →
Module#descendants
VALUE
rb_mod_descendants(VALUE mod)
{
struct descendants_traverse_data data = { Qfalse, 0, -1, NULL };
// estimate the count of descendants
data.visited = st_init_numtable();
st_insert(data.visited, (st_data_t)mod, 1); // exclude the receiver
rb_class_foreach_subclass(mod, module_descendants_recursive, (VALUE)&data);
st_free_table(data.visited);
// the following allocation may cause GC which may change the number of descendants
data.buffer = rb_ary_new_capa(data.count);
data.maxcount = data.count;
data.count = 0;
// pre-sized so that st_insert() does not cause GC during the enumeration
data.visited = st_init_numtable_with_size(data.maxcount + 1);
st_insert(data.visited, (st_data_t)mod, 1); // exclude the receiver
size_t gc_count = rb_gc_count();
rb_class_foreach_subclass(mod, module_descendants_recursive, (VALUE)&data);
if (gc_count != rb_gc_count()) {
rb_bug("GC must not occur during the subclass iteration of Module#descendants");
}
st_free_table(data.visited);
return data.buffer;
}
Returns an array of classes and modules that have the receiver in their ancestors. This is the inverse of Module#ancestors: +x.descendants.include?(y)+ holds if and only if +y.ancestors.include?(x)+ holds, except that the receiver itself, singleton classes, and refinements are never included. The order of the returned array is not defined.
module A; end module B; include A; end class C; include B; end class D < C; end A.descendants #=> [B, C, D] B.descendants #=> [C, D] C.descendants #=> [D]
Note that the receiver does not hold references to its descendants and doesn’t prevent them from being garbage collected. This means that a descendant might disappear from the result when all references to it are dropped, depending on whether garbage collector was run.
Ruby version changes
-
4.1
-
Module#descendantsis added. It returns an array of classes and modules that have the receiver in their ancestors. [Feature #9779]
-