Project

General

Profile

Feature #14394

Class.descendants

Added by ridiculous (Ryan Buckley) 25 days ago. Updated 24 days ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:85073]

Description

There have been numerous implementations of the method Class.descendants by various gems. However, I can't help but think that this ability should be included in the Ruby language itself. Especially since Ruby already offers the counterpart method Class.ancestors.

Would it possible to add a descendants class method?

History

#1 [ruby-core:85077] Updated by shevegen (Robert A. Heiler) 25 days ago

I do not know if it was suggested before, but it could be discussed
at the ruby developer meeting perhaps (unless it was already rejected).

I think it may be symmetrical to .ancestors too.

To complete your suggestion, could you describe at the least one
use case when this functionality may be useful? The ruby core team
said in the past that they prefer solving real problems - not that
I am saying that you do not have any real problem, mind you; just
so that it can be included here (you mentioned gems that do so but
in the above suggestion there are not yet any specific names; may
also help so that others can have a look as well). But anyway,
these are just my suggestions - feel free to ignore them if you
want to. :)

#2 [ruby-core:85083] Updated by ridiculous (Ryan Buckley) 25 days ago

Thanks for the reply, shevegen, those are helpful questions :)

The gems I've seen implement this are active-support and dry-rb, with other people checking ObjectSpace to get the list (which can be very slow for large apps).

The most common use case for this that I've seen, is implementing the Chain of Responsibility pattern. In this case, we want to find a "handler" class for a certain type of input, from a list of registered classes. Instead of configuring the handlers as a static array, it's easier and more flexible to be able to lookup the list dynamically at runtime. The lookup is done by finding all subclasses of a certain base class. Because there is no fast and out-of-the-box way to do this, I've historically opted for the configured list approach.

#3 [ruby-core:85112] Updated by Eregon (Benoit Daloze) 24 days ago

Could that work with just the Class#inherited hook?
What's the advantage of asking all subclasses/descendents of a class instead?

#4 [ruby-core:85117] Updated by Hanmac (Hans Mackowiak) 24 days ago

"Class#inherited hook" works not for core classes because they are defined before you can define the hook

also should that show only named classes or anonymous somehow too?

ridiculous (Ryan Buckley): i often see a register method where you register your new class onto a name/key to that handler service

#5 Updated by ridiculous (Ryan Buckley) 24 days ago

@Hanmac yeah, registering with a method or a static list is common practice. But I feel like Ruby can do better.

For many cases, it's possible to track them with the inherited hook, @Erogon. But for something so fundamental, why not include it in the language?

I'm interested in the history, as I see it was presented and even tentatively scheduled for version 2.2 (https://bugs.ruby-lang.org/issues/9779), wondering what happened?

#6 [ruby-core:85125] Updated by Eregon (Benoit Daloze) 24 days ago

I think one part of the discussion was that this features requires classes to explicitly track their subclasses (which is a memory overhead, and it must be a list of weak references to avoid leaking subclasses).
I think MRI now tracks subclasses but didn't use to.
FWIW TruffleRuby currently doesn't need to track subclasses (But doing it would probably not be a very big overhead, we already need to track constants, class vars and methods in each Class so Class objects are anyway not so lightweight).

Also available in: Atom PDF