зеркало из https://github.com/github/ruby.git
Add Class#descendants
Doesn't include receiver or singleton classes. Implements [Feature #14394] Co-authored-by: fatkodima <fatkodima123@gmail.com> Co-authored-by: Benoit Daloze <eregontp@gmail.com>
This commit is contained in:
Родитель
a4d5ee4f31
Коммит
717ab0bb2e
17
NEWS.md
17
NEWS.md
|
@ -96,6 +96,22 @@ Outstanding ones only.
|
|||
|
||||
* Array#intersect? is added. [[Feature #15198]]
|
||||
|
||||
* Class
|
||||
|
||||
* Class#descendants, which returns an array of classes
|
||||
directly or indirectly inheriting from the receiver, not
|
||||
including the receiver or singleton classes.
|
||||
[[Feature #14394]]
|
||||
|
||||
```ruby
|
||||
class A; end
|
||||
class B < A; end
|
||||
class C < B; end
|
||||
A.descendants #=> [B, C]
|
||||
B.descendants #=> [C]
|
||||
C.descendants #=> []
|
||||
```
|
||||
|
||||
* Enumerable
|
||||
|
||||
* Enumerable#compact is added. [[Feature #17312]]
|
||||
|
@ -358,6 +374,7 @@ See [the repository](https://github.com/ruby/error_highlight) in detail.
|
|||
[Bug #4443]: https://bugs.ruby-lang.org/issues/4443
|
||||
[Feature #12194]: https://bugs.ruby-lang.org/issues/12194
|
||||
[Feature #14256]: https://bugs.ruby-lang.org/issues/14256
|
||||
[Feature #14394]: https://bugs.ruby-lang.org/issues/14394
|
||||
[Feature #14579]: https://bugs.ruby-lang.org/issues/14579
|
||||
[Feature #15198]: https://bugs.ruby-lang.org/issues/15198
|
||||
[Feature #15211]: https://bugs.ruby-lang.org/issues/15211
|
||||
|
|
35
class.c
35
class.c
|
@ -1334,6 +1334,41 @@ rb_mod_ancestors(VALUE mod)
|
|||
return ary;
|
||||
}
|
||||
|
||||
static void
|
||||
class_descendants_recursive(VALUE klass, VALUE ary)
|
||||
{
|
||||
if (BUILTIN_TYPE(klass) == T_CLASS && !FL_TEST(klass, FL_SINGLETON)) {
|
||||
rb_ary_push(ary, klass);
|
||||
}
|
||||
rb_class_foreach_subclass(klass, class_descendants_recursive, ary);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* descendants -> array
|
||||
*
|
||||
* Returns an array of classes where the receiver is one of
|
||||
* the ancestors of the class, excluding the receiver and
|
||||
* singleton classes. The order of the returned array is not
|
||||
* defined.
|
||||
*
|
||||
* class A; end
|
||||
* class B < A; end
|
||||
* class C < B; end
|
||||
*
|
||||
* A.descendants #=> [B, C]
|
||||
* B.descendants #=> [C]
|
||||
* C.descendants #=> []
|
||||
*/
|
||||
|
||||
VALUE
|
||||
rb_class_descendants(VALUE klass)
|
||||
{
|
||||
VALUE ary = rb_ary_new();
|
||||
rb_class_foreach_subclass(klass, class_descendants_recursive, ary);
|
||||
return ary;
|
||||
}
|
||||
|
||||
static void
|
||||
ins_methods_push(st_data_t name, st_data_t ary)
|
||||
{
|
||||
|
|
|
@ -174,6 +174,19 @@ VALUE rb_mod_include_p(VALUE child, VALUE parent);
|
|||
*/
|
||||
VALUE rb_mod_ancestors(VALUE mod);
|
||||
|
||||
/**
|
||||
* Queries the class's descendants. This routine gathers classes that are
|
||||
* subclasses of the given class (or subclasses of those subclasses, etc.),
|
||||
* returning an array of classes that have the given class as an ancestor.
|
||||
* The returned array does not include the given class or singleton classes.
|
||||
*
|
||||
* @param[in] klass A class.
|
||||
* @return An array of classes where `klass` is an ancestor.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
VALUE rb_class_descendants(VALUE klass);
|
||||
|
||||
/**
|
||||
* Generates an array of symbols, which are the list of method names defined in
|
||||
* the passed class.
|
||||
|
|
1
object.c
1
object.c
|
@ -4653,6 +4653,7 @@ InitVM_Object(void)
|
|||
rb_define_method(rb_cClass, "new", rb_class_new_instance_pass_kw, -1);
|
||||
rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
|
||||
rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
|
||||
rb_define_method(rb_cClass, "descendants", rb_class_descendants, 0); /* in class.c */
|
||||
rb_define_alloc_func(rb_cClass, rb_class_s_alloc);
|
||||
rb_undef_method(rb_cClass, "extend_object");
|
||||
rb_undef_method(rb_cClass, "append_features");
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
require_relative '../../spec_helper'
|
||||
require_relative '../module/fixtures/classes'
|
||||
|
||||
ruby_version_is '3.1' do
|
||||
describe "Class#descendants" do
|
||||
it "returns a list of classes descended from self (excluding self)" do
|
||||
assert_descendants(ModuleSpecs::Parent, [ModuleSpecs::Child, ModuleSpecs::Child2, ModuleSpecs::Grandchild])
|
||||
end
|
||||
|
||||
it "does not return included modules" do
|
||||
parent = Class.new
|
||||
child = Class.new(parent)
|
||||
mod = Module.new
|
||||
parent.include(mod)
|
||||
|
||||
assert_descendants(parent, [child])
|
||||
end
|
||||
|
||||
it "does not return singleton classes" do
|
||||
a = Class.new
|
||||
|
||||
a_obj = a.new
|
||||
def a_obj.force_singleton_class
|
||||
42
|
||||
end
|
||||
|
||||
a.descendants.should_not include(a_obj.singleton_class)
|
||||
end
|
||||
|
||||
it "has 1 entry per module or class" do
|
||||
ModuleSpecs::Parent.descendants.should == ModuleSpecs::Parent.descendants.uniq
|
||||
end
|
||||
|
||||
def assert_descendants(mod, descendants)
|
||||
mod.descendants.sort_by(&:inspect).should == descendants.sort_by(&:inspect)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -737,4 +737,22 @@ class TestClass < Test::Unit::TestCase
|
|||
c = Class.new.freeze
|
||||
assert_same(c, Module.new.const_set(:Foo, c))
|
||||
end
|
||||
|
||||
def test_descendants
|
||||
c = Class.new
|
||||
sc = Class.new(c)
|
||||
ssc = Class.new(sc)
|
||||
[c, sc, ssc].each do |k|
|
||||
k.include Module.new
|
||||
k.new.define_singleton_method(:force_singleton_class){}
|
||||
end
|
||||
assert_equal([sc, ssc], c.descendants)
|
||||
assert_equal([ssc], sc.descendants)
|
||||
assert_equal([], ssc.descendants)
|
||||
|
||||
object_descendants = Object.descendants
|
||||
assert_include(object_descendants, c)
|
||||
assert_include(object_descendants, sc)
|
||||
assert_include(object_descendants, ssc)
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче