https://redmine.ruby-lang.org/https://redmine.ruby-lang.org/favicon.ico?17113305112018-01-24T21:01:03ZRuby Issue Tracking SystemRuby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=697912018-01-24T21:01:03Zshevegen (Robert A. Heiler)shevegen@gmail.com
<ul></ul><p>I do not know if it was suggested before, but it could be discussed<br>
at the ruby developer meeting perhaps (unless it was already rejected).</p>
<p>I think it may be symmetrical to .ancestors too.</p>
<p>To complete your suggestion, could you describe at the least one<br>
use case when this functionality may be useful? The ruby core team<br>
said in the past that they prefer solving real problems - not that<br>
I am saying that you do not have any real problem, mind you; just<br>
so that it can be included here (you mentioned gems that do so but<br>
in the above suggestion there are not yet any specific names; may<br>
also help so that others can have a look as well). But anyway,<br>
these are just my suggestions - feel free to ignore them if you<br>
want to. :)</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=697982018-01-24T22:41:47Zridiculous (Ryan Buckley)arebuckley@gmail.com
<ul></ul><p>Thanks for the reply, shevegen, those are helpful questions :)</p>
<p>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).</p>
<p>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.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=698252018-01-25T12:18:00ZEregon (Benoit Daloze)
<ul></ul><p>Could that work with just the Class#inherited hook?<br>
What's the advantage of asking all subclasses/descendents of a class instead?</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=698332018-01-25T14:34:25ZHanmac (Hans Mackowiak)hanmac@gmx.de
<ul></ul><p>"Class#inherited hook" works not for core classes because they are defined before you can define the hook</p>
<p>also should that show only named classes or anonymous somehow too?</p>
<p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/8618">@ridiculous (Ryan Buckley)</a>: i often see a register method where you register your new class onto a name/key to that handler service</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=698382018-01-25T21:29:38Zridiculous (Ryan Buckley)arebuckley@gmail.com
<ul></ul><p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/3055">@Hanmac (Hans Mackowiak)</a> yeah, registering with a method or a static list is common practice. But I feel like Ruby can do better.</p>
<p>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?</p>
<p>I'm interested in the history, as I see it was presented and even tentatively scheduled for version 2.2 (<a href="https://bugs.ruby-lang.org/issues/9779" class="external">https://bugs.ruby-lang.org/issues/9779</a>), wondering what happened?</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=698392018-01-25T22:25:52ZEregon (Benoit Daloze)
<ul></ul><p>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).<br>
I think MRI now tracks subclasses but didn't use to.<br>
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).</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=872272020-08-27T15:37:53Zfatkodima (Dima Fatko)
<ul></ul><p>I would like the ruby team to reconsider this feature.</p>
<p>There is a high demand for this and a lot of implementations in the ruby world. Just some of them:</p>
<ul>
<li>rails' ActiveSupport - <a href="https://github.com/rails/rails/blob/master/activesupport/lib/active_support/descendants_tracker.rb" class="external">https://github.com/rails/rails/blob/master/activesupport/lib/active_support/descendants_tracker.rb</a>
</li>
<li>rails monkeypatch for <code>Class</code> (implementation uses ObjectSpace) - <a href="https://api.rubyonrails.org/classes/Class.html#method-i-descendants" class="external">https://api.rubyonrails.org/classes/Class.html#method-i-descendants</a>
</li>
<li>rubocop (3-4 places like this) - <a href="https://github.com/rubocop-hq/rubocop/blob/e7197677e919a34ca1587dc1b519c96360249bdc/lib/rubocop/cli/command/base.rb#L15-L18" class="external">https://github.com/rubocop-hq/rubocop/blob/e7197677e919a34ca1587dc1b519c96360249bdc/lib/rubocop/cli/command/base.rb#L15-L18</a>
</li>
<li>rack-attack gem - <a href="https://github.com/rack/rack-attack/blob/129e970d42f86c5c46978988c5c09b0eddaec35a/lib/rack/attack/base_proxy.rb#L9-L15" class="external">https://github.com/rack/rack-attack/blob/129e970d42f86c5c46978988c5c09b0eddaec35a/lib/rack/attack/base_proxy.rb#L9-L15</a>
</li>
</ul>
<p>I have implemented something like this multiple times, personally. And would like to have a standard way to get this.</p>
<p>Are you open for a patch?</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=872392020-08-28T00:51:31Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>Implementation wise MRI already have <code>rb_class_foreach_subclass()</code>. It can be rather trivial to wrap that C function.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=872722020-08-28T23:07:34Zfatkodima (Dima Fatko)
<ul></ul><p>I opened a PR - <a href="https://github.com/ruby/ruby/pull/3471" class="external">https://github.com/ruby/ruby/pull/3471</a></p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=873122020-08-31T09:02:03ZHanmac (Hans Mackowiak)hanmac@gmx.de
<ul></ul><p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/43351">@fatkodima (Dima Fatko)</a></p>
<p>how does <code>rb_class_foreach_subclass</code> handle anonymous classes?</p>
<p>if it would add them, how about adding a parameter to this function exclude/include them? (exclude them as default?)</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=873162020-08-31T10:46:41ZEregon (Benoit Daloze)
<ul></ul><p>Hanmac (Hans Mackowiak) wrote in <a href="#note-10">#note-10</a>:</p>
<blockquote>
<p>if it would add them, how about adding a parameter to this function exclude/include them? (exclude them as default?)</p>
</blockquote>
<p>I think the user can filter easily based on whatever condition they want, so this method shouldn't bother with that.</p>
<p>This will force all Ruby implementations to keep a weak list of subclasses, which is some memory footprint overhead.<br>
TruffleRuby for instance currently does not track subclasses.</p>
<p>But I'm not against it, and it seems easier to use than tracking with <code>inherited</code>, and so much cleaner than <code>ObjectSpace.each_object</code>.<br>
Seeing the usage in Rails I have often thought we might define such a method in TruffleRuby, because iterating the heap just for this is very inefficient.<br>
So +1 from me.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=873202020-08-31T18:50:34Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul></ul><p>+1 for this feature from me.</p>
<p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/3055">@Hanmac (Hans Mackowiak)</a>: if you are referring to singleton classes, they should be excluded:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">s</span> <span class="o">=</span> <span class="o">+</span><span class="s1">'hello'</span>
<span class="k">def</span> <span class="nc">s</span><span class="o">.</span><span class="nf">force_singleton_class</span>
<span class="mi">42</span>
<span class="k">end</span>
<span class="n">s</span><span class="p">.</span><span class="nf">singleton_class</span> <span class="o"><</span> <span class="no">String</span> <span class="c1"># => true</span>
<span class="no">String</span><span class="p">.</span><span class="nf">descendants</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="nf">singleton_class</span><span class="p">)</span> <span class="c1"># => should be false</span>
</code></pre>
<p>Note that the base class' <code>inherited</code> method is not called when a singleton class is created.</p>
<p>If not, I agree with <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/772">@Eregon (Benoit Daloze)</a>, no extra filter necessary.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=873232020-08-31T19:43:05ZHanmac (Hans Mackowiak)hanmac@gmx.de
<ul></ul><p>More like:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">A</span>
<span class="k">end</span>
<span class="n">x</span> <span class="o">=</span> <span class="no">Class</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="no">A</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="nf">new</span>
</code></pre>
<p><del>How does the GC handle such classes?<br>
Will they get GC'd when there isn't any reference to them anymore?</del></p>
<p>I checked such classes will get GC'd when there isn't any reference, (i used ObjectSpace for counting)<br>
So this function wouldn't list them anymore when they are gone</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=876922020-09-25T06:07:13Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Feedback</i></li></ul><p>Accepted.</p>
<p>Although on some implementation, <code>#descendants</code> can be slow since it may be implemented by scanning whole object heaps (as ActiveSupport currently does).</p>
<p>Matz.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=876932020-09-25T06:12:34Zko1 (Koichi Sasada)
<ul></ul><p>let's clear:</p>
<ul>
<li>
<code>self</code> should be is contained or not? AS's method doesn't contain.</li>
<li>singleton classes should be excluded.</li>
<li>order is random (not specified).</li>
<li>performance of this method is not important, or important (calls it many times)?</li>
</ul> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=877042020-09-25T10:08:01Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>self should be is contained or not?</p>
</blockquote>
<p>I don't think it should no. <code>self < self # => self</code>.</p>
<blockquote>
<p>singleton classes should be excluded.</p>
</blockquote>
<p>Absolutely.</p>
<blockquote>
<p>order is random</p>
</blockquote>
<p>Agreed.</p>
<blockquote>
<p>performance of this method is not important, or important</p>
</blockquote>
<p>As long as it has reasonable performance, and that it's not affected by the size of the heap, it will be fine.</p>
<p>It's used in a few semi-hotspot in Rails, hence why there are two versions of it. A slow one that use <code>ObjectSpace.each_objects</code> and one keeping an array of <code>WeakRef</code> populated by <code>inherited</code>.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=877062020-09-25T10:22:08ZEregon (Benoit Daloze)
<ul></ul><p>byroot (Jean Boussier) wrote in <a href="#note-16">#note-16</a>:</p>
<blockquote>
<blockquote>
<p>self should be is contained or not?</p>
</blockquote>
<p>I don't think it should no. <code>self < self # => self</code>.</p>
</blockquote>
<p><code>Module#ancestors</code> includes <code>self</code>.<br>
<code>#descendants</code> is kind of the opposite/complement of <code>mod.ancestors</code> (finds all modules for which <code>mod</code> is in the <code>ancestors</code>, excluding singleton classes).<br>
So unsure if it should or not include <code>self</code>.</p>
<blockquote>
<p>As long as it has reasonable performance</p>
</blockquote>
<p>I expect Object.descendants to be quite slow, so it depends on how many descendants there are (including singleton classes which might be tracked internally).<br>
Also we probably need to avoid recursion for modules as I think it's not a DAG there.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=877072020-09-25T10:27:47Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p><code>self < self # => self</code></p>
</blockquote>
<p>I made a mistake meant <code>=> false</code>.</p>
<blockquote>
<p>I expect Object.descendants to be quite slow</p>
</blockquote>
<p>There isn't much use case for it though. But yes, that one just can't be fast no matter the implementation.</p>
<blockquote>
<p>Also we probably need to avoid recursion for modules as I think it's not a DAG there.</p>
</blockquote>
<p>I don't think modules are a concern. At least in the Active Support implementation only <code>Class</code> instances are considered. It's true that if <code>descendants</code> is seen as the mirror of <code>ancestors</code> then it should include modules, but I'm not so sure about that.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=877082020-09-25T10:41:10ZEregon (Benoit Daloze)
<ul></ul><p>byroot (Jean Boussier) wrote in <a href="#note-18">#note-18</a>:</p>
<blockquote>
<p>I don't think modules are a concern. At least in the Active Support implementation only <code>Class</code> instances are considered. It's true that if <code>descendants</code> is seen as the mirror of <code>ancestors</code> then it should include modules, but I'm not so sure about that.</p>
</blockquote>
<p>The current implementation on the PR includes <code>self</code>, which seems a bit more natural to me, but not a strong opinion.</p>
<p>One can do some_module.descendants but I think indeed some_class.descendants should not include modules (the PR doesn't).</p>
<p>With the current PR:</p>
<pre><code>$ ruby -e 'p Object.descendants'
[Object, DidYouMean::PlainFormatter, DidYouMean::RequirePathChecker, DidYouMean::TreeSpellChecker, DidYouMean::NullChecker, DidYouMean::KeyErrorChecker, DidYouMean::MethodNameChecker, DidYouMean::VariableNameChecker, DidYouMean::ClassNameChecker, DidYouMean::SpellChecker, Gem::PathSupport, MonitorMixin::ConditionVariable, Monitor, Gem::Dependency, Gem::Version, Gem::Requirement, Gem::Platform, Gem::List, Gem::SpecificationPolicy, Gem::StreamUI::ThreadedDownloadReporter, Gem::StreamUI::SilentDownloadReporter, Gem::StreamUI::VerboseProgressReporter, Gem::StreamUI::SimpleProgressReporter, Gem::StreamUI::SilentProgressReporter, Gem::StreamUI, Gem::SilentUI, Gem::ConsoleUI, Gem::StubSpecification::StubLine, Gem::BasicSpecification, Gem::Specification, Gem::StubSpecification, Gem::ErrorReason, Gem::SourceFetchProblem, Gem::PlatformMismatch, RubyVM::AbstractSyntaxTree::Node, TracePoint, Complex::compatible, Rational::compatible, Fiber, Process::Status, Thread::ConditionVariable, Thread::Queue, Thread::SizedQueue, Thread::Mutex, ThreadGroup, RubyVM::InstructionSequence, Thread::Backtrace::Location, Thread::Backtrace, Thread, Process::Waiter, RubyVM, Ractor, Enumerator::Producer, Enumerator::Yielder, Enumerator::Generator, Enumerator, Enumerator::ArithmeticSequence, Enumerator::Chain, Enumerator::Lazy, ObjectSpace::WeakMap, Binding, UnboundMethod, Method, Proc, Random::Base, Random, Time::tm, Time, Dir, File::Stat, ARGF.class, IO, File, Range, MatchData, Regexp, Struct, #<Class:0x00005641f0f7a228>, #<Class:0x00005641f0f5e3e8>, Process::Tms, Hash, Array, Numeric, Complex, Rational, Float, Integer, Exception, SystemStackError, NoMemoryError, SecurityError, ScriptError, LoadError, Gem::LoadError, Gem::ConflictError, Gem::MissingSpecError, Gem::MissingSpecVersionError, NotImplementedError, SyntaxError, StandardError, NameError, NoMethodError, FiberError, ThreadError, Math::DomainError, LocalJumpError, IOError, EOFError, RegexpError, ZeroDivisionError, SystemCallError, Errno::EXFULL, Errno::EXDEV, Errno::EUSERS, Errno::EUNATCH, Errno::EUCLEAN, Errno::ETXTBSY, Errno::ETOOMANYREFS, Errno::ETIMEDOUT, Errno::ETIME, Errno::ESTRPIPE, Errno::ESTALE, Errno::ESRMNT, Errno::ESRCH, Errno::ESPIPE, Errno::ESOCKTNOSUPPORT, Errno::ESHUTDOWN, Errno::EROFS, Errno::ERFKILL, Errno::ERESTART, Errno::EREMOTEIO, Errno::EREMOTE, Errno::EREMCHG, Errno::ERANGE, Errno::EPROTOTYPE, Errno::EPROTONOSUPPORT, Errno::EPROTO, Errno::EPIPE, Errno::EPFNOSUPPORT, Errno::EPERM, Errno::EOWNERDEAD, Errno::EOVERFLOW, Errno::ENXIO, Errno::ENOTUNIQ, Errno::ENOTTY, Errno::ENOTSUP, Errno::ENOTSOCK, Errno::ENOTRECOVERABLE, Errno::ENOTNAM, Errno::ENOTEMPTY, Errno::ENOTDIR, Errno::ENOTCONN, Errno::ENOTBLK, Errno::ENOSYS, Errno::ENOSTR, Errno::ENOSR, Errno::ENOSPC, Errno::ENOPROTOOPT, Errno::ENOPKG, Errno::ENONET, Errno::ENOMSG, Errno::ENOMEM, Errno::ENOMEDIUM, Errno::ENOLINK, Errno::ENOLCK, Errno::ENOKEY, Errno::ENOEXEC, Errno::ENOENT, Errno::ENODEV, Errno::ENODATA, Errno::ENOCSI, Errno::ENOBUFS, Errno::ENOANO, Errno::ENFILE, Errno::ENETUNREACH, Errno::ENETRESET, Errno::ENETDOWN, Errno::ENAVAIL, Errno::ENAMETOOLONG, Errno::EMULTIHOP, Errno::EMSGSIZE, Errno::EMLINK, Errno::EMFILE, Errno::EMEDIUMTYPE, Errno::ELOOP, Errno::ELNRNG, Errno::ELIBSCN, Errno::ELIBMAX, Errno::ELIBEXEC, Errno::ELIBBAD, Errno::ELIBACC, Errno::EL3RST, Errno::EL3HLT, Errno::EL2NSYNC, Errno::EL2HLT, Errno::EKEYREVOKED, Errno::EKEYREJECTED, Errno::EKEYEXPIRED, Errno::EISNAM, Errno::EISDIR, Errno::EISCONN, Errno::EIO, Errno::EINVAL, Errno::EINTR, Errno::EINPROGRESS, IO::EINPROGRESSWaitWritable, IO::EINPROGRESSWaitReadable, Errno::EILSEQ, Errno::EIDRM, Errno::EHWPOISON, Errno::EHOSTUNREACH, Errno::EHOSTDOWN, Errno::EFBIG, Errno::EFAULT, Errno::EEXIST, Errno::EDQUOT, Errno::EDOTDOT, Errno::EDOM, Errno::EDESTADDRREQ, Errno::EDEADLK, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ECONNABORTED, Errno::ECOMM, Errno::ECHRNG, Errno::ECHILD, Errno::ECANCELED, Errno::EBUSY, Errno::EBFONT, Errno::EBADSLT, Errno::EBADRQC, Errno::EBADR, Errno::EBADMSG, Errno::EBADFD, Errno::EBADF, Errno::EBADE, Errno::EALREADY, Errno::EAGAIN, IO::EAGAINWaitWritable, IO::EAGAINWaitReadable, Errno::EAFNOSUPPORT, Errno::EADV, Errno::EADDRNOTAVAIL, Errno::EADDRINUSE, Errno::EACCES, Errno::E2BIG, Errno::NOERROR, EncodingError, Encoding::ConverterNotFoundError, Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError, Encoding::CompatibilityError, RuntimeError, Gem::Exception, Gem::VerificationError, Gem::RubyVersionMismatch, Gem::RemoteSourceException, Gem::RemoteInstallationSkipped, Gem::RemoteInstallationCancelled, Gem::RemoteError, Gem::OperationNotSupportedError, Gem::InvalidSpecificationException, Gem::InstallError, Gem::RuntimeRequirementNotMetError, Gem::ImpossibleDependenciesError, Gem::GemNotFoundException, Gem::SpecificGemNotFoundException, Gem::FormatException, Gem::FilePermissionError, Gem::EndOfYAMLException, Gem::DocumentError, Gem::UninstallError, Gem::GemNotInHomeException, Gem::DependencyRemovalException, Gem::DependencyError, Gem::UnsatisfiableDependencyError, Gem::DependencyResolutionError, Gem::CommandLineError, Ractor::Error, Ractor::MovedError, Ractor::RemoteError, NoMatchingPatternError, FrozenError, RangeError, FloatDomainError, IndexError, KeyError, StopIteration, ClosedQueueError, Ractor::ClosedError, ArgumentError, Gem::Requirement::BadRequirementError, UncaughtThrowError, TypeError, SignalException, Interrupt, fatal, SystemExit, Gem::SystemExitException, Symbol, String, DidYouMean::ClassNameChecker::ClassName, Warning::buffer, Encoding, FalseClass, TrueClass, Data, Encoding::Converter, NameError::message, NilClass, Module, Class]
</code></pre>
<p>(and <code>Kernel.descendants</code> is the same but with <code>Kernel</code> extra).</p>
<p>Unsure if <code>fatal</code>, <code>NameError::message</code>, <code>Warning::buffer</code>, etc should be included. Those would normally be inaccessible in Ruby except via ObjectSpace.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=877102020-09-25T11:19:30Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>(and Kernel.descendants is the same but with Kernel extra).</p>
</blockquote>
<p>I'm not sure <code>Module#descendants</code> is really useful, but I don't see any reason not to have it either.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=877222020-09-25T14:01:26ZEregon (Benoit Daloze)
<ul></ul><p>byroot (Jean Boussier) wrote in <a href="#note-20">#note-20</a>:</p>
<blockquote>
<p>I'm not sure <code>Module#descendants</code> is really useful, but I don't see any reason not to have it either.</p>
</blockquote>
<p>I think tracking subclasses (of classes) is quite simpler than also having to track "which modules include a given module".</p>
<p>Is there a use-case for <code>mod.descendants</code> where <code>mod</code> is a module and not a class?</p>
<p>If not I'd suggest to restrict it to Class#descendants for now.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=877272020-09-25T16:57:44Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>Is there a use-case for mod.descendants where mod is a module and not a class?</p>
</blockquote>
<p>Not that I know of. Active Support implementations only list <code>Class</code>, not <code>Module</code>.</p>
<blockquote>
<p>If not I'd suggest to restrict it to Class#descendants for now.</p>
</blockquote>
<p>Agreed.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=877282020-09-25T18:13:41Zfatkodima (Dima Fatko)
<ul></ul><p>While I needed <code>Class#descendants</code> many times, I never had a need for <code>Module#descendants</code>. But I can think of its usefulness.<br>
For example, when inheritance is implemented through modules, but not classes, for example -</p>
<p>a quick search guided me to this: <a href="https://github.com/mongodb/mongoid/blob/55e4f8367f3878fbf1aafeef6b6e40b39567f917/lib/mongoid/document.rb#L34-L36" class="external">https://github.com/mongodb/mongoid/blob/55e4f8367f3878fbf1aafeef6b6e40b39567f917/lib/mongoid/document.rb#L34-L36</a><br>
In this case, this implementation would benefit from <code>Module#descendants</code>.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=904242021-02-16T07:32:25Zko1 (Koichi Sasada)
<ul></ul><p>Sorry I forget.<br>
I'll implement <code>Class#descendants</code>, but not Module because the spec is not clear yet.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=904252021-02-16T07:32:43Zko1 (Koichi Sasada)
<ul><li><strong>Status</strong> changed from <i>Feedback</i> to <i>Assigned</i></li><li><strong>Assignee</strong> set to <i>ko1 (Koichi Sasada)</i></li></ul> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=926472021-06-25T12:37:41ZEregon (Benoit Daloze)
<ul></ul><p>Would be good to add this for 3.1, as it will avoid some pretty expensive heap walks (ObjectSpace.each_object) just to get a list of subclasses.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=941392021-10-15T17:41:46Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-1 priority-4 priority-default" href="/issues/9779">Feature #9779</a>: Add Module#descendents</i> added</li></ul> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=941402021-10-15T18:34:30Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul></ul><p>I'd like this feature to make 3.1, so I submitted a pull request for it, borrowing somewhat from @fatkodima's pull request: <a href="https://github.com/ruby/ruby/pull/4974" class="external">https://github.com/ruby/ruby/pull/4974</a></p>
<p>As <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/17">@ko1 (Koichi Sasada)</a> specified, the receiver is not included in the array, nor are singleton classes. The order is unspecified, but based on the implementation, a subclass will appear before descendants of that subclasses.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=943402021-10-26T19:35:50Zjeremyevans (Jeremy Evans)code@jeremyevans.net
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Closed</i></li></ul><p>Applied in changeset <a class="changeset" title="Add Class#descendants Doesn't include receiver or singleton classes. Implements [Feature #14394..." href="https://redmine.ruby-lang.org/projects/ruby-master/repository/git/revisions/717ab0bb2ee63dfe76076e0c9f91fbac3a0de4fd">git|717ab0bb2ee63dfe76076e0c9f91fbac3a0de4fd</a>.</p>
<hr>
<p>Add Class#descendants</p>
<p>Doesn't include receiver or singleton classes.</p>
<p>Implements [Feature <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Class.descendants (Open)" href="https://redmine.ruby-lang.org/issues/14394">#14394</a>]</p>
<p>Co-authored-by: fatkodima <a href="mailto:fatkodima123@gmail.com" class="email">fatkodima123@gmail.com</a><br>
Co-authored-by: Benoit Daloze <a href="mailto:eregontp@gmail.com" class="email">eregontp@gmail.com</a></p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=943532021-10-27T14:40:42Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/18273">Feature #18273</a>: Class#subclasses</i> added</li></ul> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=944452021-11-02T09:10:37Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul><li><strong>Related to</strong> <i><a class="issue tracker-1 status-5 priority-4 priority-default closed" href="/issues/18282">Bug #18282</a>: Rails CI raises Segmentation fault with ruby 3.1.0dev supporting `Class#descendants`</i> added</li></ul> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=950762021-12-03T03:13:32Zko1 (Koichi Sasada)
<ul></ul><p>Note:</p>
<p>The internal implementation of subclass iteration seems fragile (difficult to maintain) because it needs to manage with weakref. In fact, our CI shows SEGV in a few times in years (difficult to debug because of its rareness. But we can ignore these bug because it is rare :p).<br>
So personally I don't want to introduce this feature if the feature is not important.</p>
<p>(if this feature is important for Ruby langauge, I think it is better to re-implement with other easy data structures)</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=954322021-12-20T02:39:28Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p><code>Module#descendants</code> has been proposed too in <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Add Module#descendents (Open)" href="https://redmine.ruby-lang.org/issues/9779">#9779</a>.<br>
In addition, upon the existence of newly introduced <code>Class#subclasses</code>, the need for <code>Class#descendants</code> is decreased.</p>
<p>Considering those facts, let up postpone introducing <code>Class#descendats</code> for 3.1.<br>
I feel we need more discussion. Sorry for the last-minute change.</p>
<p>Matz.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=954462021-12-20T19:04:28Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul><li><strong>Status</strong> changed from <i>Closed</i> to <i>Open</i></li></ul><p>matz (Yukihiro Matsumoto) wrote in <a href="#note-33">#note-33</a>:</p>
<blockquote>
<p>Considering those facts, let up postpone introducing <code>Class#descendats</code> for 3.1.</p>
</blockquote>
<p>Class#descendants removed at <a class="changeset" title="Remove Class#descendants" href="https://redmine.ruby-lang.org/projects/ruby-master/repository/git/revisions/3bd5f27f737c7d365b7d01c43d77a958c224ab16">3bd5f27f737c7d365b7d01c43d77a958c224ab16</a>. We can potentially reintroduce after 3.1 after discussion.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=954472021-12-20T19:23:05Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>That's unfortunate. The very recently released Rails 7.0.0 will need to be updated, some code paths won't expect <code>Class#subclasses</code> being present, but not <code>Class#descendants</code>.</p>
<p>I'll update Rails tomorrow.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=954602021-12-21T11:02:27Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>Rails fixed in <a href="https://github.com/rails/rails/pull/43951" class="external">https://github.com/rails/rails/pull/43951</a>, I'll see if we can get a 7.0.1 release before 3.1.0 is out.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=954622021-12-21T11:38:07Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><p>Just curious about the motivation of the revert though. I sin mainly because of the potential <code>Module#descendants</code>?</p>
<p>Because unless I'm missing something, the potential future inclusion of <code>Module#descendants</code> would change the behavior of <code>Class#descendants</code>, as I don't think a <code>Module</code> could ever be the descendant of a <code>Class</code>.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=954632021-12-21T12:12:14Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>Sorry for the last minute change. The biggest reason is that we still have several options, so I didn't want to restrict the future possibility.<br>
For example, <code>Class#descendants</code> can either:</p>
<ul>
<li>behave as it was first introduced. <code>Module#descentands</code> may or may not be introduced.</li>
<li>be undefined for classes (to be reserved for module hierarchy), just like <code>Class#include</code>
</li>
<li>not be introduced along with <code>Module#descendants</code>.</li>
</ul>
<p>I am open to discussion but I don't want to jump on the conclusion.</p>
<p>Matz.</p> Ruby master - Feature #14394: Class.descendantshttps://redmine.ruby-lang.org/issues/14394?journal_id=960752022-01-20T22:46:31Zdgutov (Dmitry Gutov)dgutov@yandex.ru
<ul></ul><p>Shouldn't the <code>#descendants</code> method be the reverse of <code>#ancestors</code>?</p>
<p><code>#ancestors</code> traverses up both class hierarchies and module inclusion chains.</p>
<p>That tells me both <code>Module#descendants</code> and <code>Class#descendants</code> will make sense, and should enumerate both classes and modules that are either derived from the current class, or include the current module (...and descendants of such classes/modules as well).</p>