Feature #9779 ยป 0001-Add-Module-descendents.patch
class.c | ||
---|---|---|
return ary;
|
||
}
|
||
static void
|
||
add_descendents(VALUE klass, VALUE hash)
|
||
{
|
||
rb_subclass_entry_t *e;
|
||
if (CLASS_OF(klass) &&
|
||
BUILTIN_TYPE(klass) != T_ICLASS) {
|
||
rb_hash_aset(hash, klass, Qnil);
|
||
}
|
||
for (e = RCLASS_EXT(klass)->subclasses; e; e = e->next) {
|
||
add_descendents(e->klass, hash);
|
||
}
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* mod.descendents -> array
|
||
*
|
||
* Returns a list of modules including <i>mod</i> or a subclass of
|
||
* <i>mod</i> (including <i>mod</i> itself).
|
||
*
|
||
* module A
|
||
* end
|
||
*
|
||
* module B
|
||
* include A
|
||
* end
|
||
*
|
||
* module C
|
||
* include A
|
||
* end
|
||
*
|
||
* A.descendents #=> [A, C, B]
|
||
*/
|
||
VALUE
|
||
rb_mod_descendents(VALUE mod)
|
||
{
|
||
VALUE hash = rb_hash_new();
|
||
rb_funcall(hash, rb_intern("compare_by_identity"), 0);
|
||
add_descendents(mod, hash);
|
||
return rb_funcall(hash, rb_intern("keys"), 0);
|
||
}
|
||
#define VISI(x) ((x)&NOEX_MASK)
|
||
#define VISI_CHECK(x,f) (VISI(x) == (f))
|
||
include/ruby/intern.h | ||
---|---|---|
VALUE rb_mod_included_modules(VALUE);
|
||
VALUE rb_mod_include_p(VALUE, VALUE);
|
||
VALUE rb_mod_ancestors(VALUE);
|
||
VALUE rb_mod_descendents(VALUE);
|
||
VALUE rb_class_instance_methods(int, VALUE*, VALUE);
|
||
VALUE rb_class_public_instance_methods(int, VALUE*, VALUE);
|
||
VALUE rb_class_protected_instance_methods(int, VALUE*, VALUE);
|
object.c | ||
---|---|---|
rb_define_method(rb_cModule, "include?", rb_mod_include_p, 1); /* in class.c */
|
||
rb_define_method(rb_cModule, "name", rb_mod_name, 0); /* in variable.c */
|
||
rb_define_method(rb_cModule, "ancestors", rb_mod_ancestors, 0); /* in class.c */
|
||
rb_define_method(rb_cModule, "descendents", rb_mod_descendents, 0); /* in class.c */
|
||
rb_define_private_method(rb_cModule, "attr", rb_mod_attr, -1);
|
||
rb_define_private_method(rb_cModule, "attr_reader", rb_mod_attr_reader, -1);
|
test/ruby/test_module.rb | ||
---|---|---|
assert_equal([String, Comparable, Object, Kernel, BasicObject], String.ancestors - mixins)
|
||
end
|
||
private def assert_same_set(expected, actual)
|
||
assert_equal(expected.sort_by(&:object_id),
|
||
actual.sort_by(&:object_id))
|
||
end
|
||
def test_descendents
|
||
assert_same_set([Mixin, User], Mixin.descendents)
|
||
assert_same_set([User], User.descendents)
|
||
m1 = Module.new
|
||
m2 = Module.new { include m1 }
|
||
m3 = Module.new { include m2 }
|
||
assert_same_set([m1, m2, m3], m1.descendents)
|
||
assert_same_set([m2, m3], m2.descendents)
|
||
assert_same_set([m3], m3.descendents)
|
||
c1 = Class.new
|
||
c2 = Class.new(c1)
|
||
c3 = Class.new(c2)
|
||
assert_same_set([c1, c2, c3], c1.descendents)
|
||
assert_same_set([c2, c3], c2.descendents)
|
||
assert_same_set([c3], c3.descendents)
|
||
end
|
||
CLASS_EVAL = 2
|
||
@@class_eval = 'b'
|
||