Bug #4971 ยป class_variables.diff
| include/ruby/intern.h | ||
|---|---|---|
|
void rb_cv_set(VALUE, const char*, VALUE);
|
||
|
VALUE rb_cv_get(VALUE, const char*);
|
||
|
void rb_define_class_variable(VALUE, const char*, VALUE);
|
||
|
VALUE rb_mod_class_variables(VALUE);
|
||
|
VALUE rb_mod_class_variables(int, VALUE*, VALUE);
|
||
|
VALUE rb_mod_remove_cvar(VALUE, VALUE);
|
||
|
/* version.c */
|
||
|
void ruby_show_version(void);
|
||
| object.c | ||
|---|---|---|
|
rb_define_method(rb_cModule, "const_missing",
|
||
|
rb_mod_const_missing, 1); /* in variable.c */
|
||
|
rb_define_method(rb_cModule, "class_variables",
|
||
|
rb_mod_class_variables, 0); /* in variable.c */
|
||
|
rb_mod_class_variables, -1); /* in variable.c */
|
||
|
rb_define_method(rb_cModule, "remove_class_variable",
|
||
|
rb_mod_remove_cvar, 1); /* in variable.c */
|
||
|
rb_define_method(rb_cModule, "class_variable_get", rb_mod_cvar_get, 1);
|
||
| test/ruby/test_module.rb | ||
|---|---|---|
|
}
|
||
|
assert_equal(42, bar::D)
|
||
|
end
|
||
|
def test_class_variables
|
||
|
m = Module.new
|
||
|
m.class_variable_set(:@@foo, 1)
|
||
|
m2 = Module.new
|
||
|
m2.send(:include, m)
|
||
|
m2.class_variable_set(:@@bar, 2)
|
||
|
assert_equal([:@@foo], m.class_variables)
|
||
|
assert_equal([:@@bar, :@@foo], m2.class_variables)
|
||
|
assert_equal([:@@bar, :@@foo], m2.class_variables(true))
|
||
|
assert_equal([:@@bar], m2.class_variables(false))
|
||
|
end
|
||
|
end
|
||
| variable.c | ||
|---|---|---|
|
}
|
||
|
static int
|
||
|
cv_i(ID key, VALUE value, VALUE ary)
|
||
|
cv_i(ID key, VALUE value, st_table *tbl)
|
||
|
{
|
||
|
if (rb_is_class_id(key)) {
|
||
|
VALUE kval = ID2SYM(key);
|
||
|
if (!rb_ary_includes(ary, kval)) {
|
||
|
rb_ary_push(ary, kval);
|
||
|
if (!st_lookup(tbl, (st_data_t)key, 0)) {
|
||
|
st_insert(tbl, (st_data_t)key, (st_data_t)0);
|
||
|
}
|
||
|
}
|
||
|
return ST_CONTINUE;
|
||
|
}
|
||
|
static void*
|
||
|
mod_cvar_at(VALUE mod, void *data)
|
||
|
{
|
||
|
st_table *tbl = data;
|
||
|
if (!tbl) {
|
||
|
tbl = st_init_numtable();
|
||
|
}
|
||
|
if (RCLASS_IV_TBL(mod)) {
|
||
|
st_foreach_safe(RCLASS_IV_TBL(mod), cv_i, (st_data_t)tbl);
|
||
|
}
|
||
|
return tbl;
|
||
|
}
|
||
|
static void*
|
||
|
mod_cvar_of(VALUE mod, void *data)
|
||
|
{
|
||
|
VALUE tmp = mod;
|
||
|
for (;;) {
|
||
|
data = mod_cvar_at(tmp, data);
|
||
|
tmp = RCLASS_SUPER(tmp);
|
||
|
if (!tmp) break;
|
||
|
#if 0
|
||
|
if (tmp == rb_cObject && mod != rb_cObject) break;
|
||
|
#endif
|
||
|
}
|
||
|
return data;
|
||
|
}
|
||
|
static int
|
||
|
cv_list_i(st_data_t key, st_data_t value, VALUE ary)
|
||
|
{
|
||
|
ID sym = (ID)key;
|
||
|
rb_const_entry_t *ce = (rb_const_entry_t *)value;
|
||
|
rb_ary_push(ary, ID2SYM(sym));
|
||
|
return ST_CONTINUE;
|
||
|
}
|
||
|
static VALUE
|
||
|
cvar_list(void *data)
|
||
|
{
|
||
|
st_table *tbl = data;
|
||
|
VALUE ary;
|
||
|
if (!tbl) return rb_ary_new2(0);
|
||
|
ary = rb_ary_new2(tbl->num_entries);
|
||
|
st_foreach_safe(tbl, cv_list_i, ary);
|
||
|
st_free_table(tbl);
|
||
|
return ary;
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* mod.class_variables -> array
|
||
| ... | ... | |
|
* @@var2 = 2
|
||
|
* end
|
||
|
* One.class_variables #=> [:@@var1]
|
||
|
* Two.class_variables #=> [:@@var2]
|
||
|
* Two.class_variables #=> [:@@var2, :@@var1]
|
||
|
*/
|
||
|
VALUE
|
||
|
rb_mod_class_variables(VALUE obj)
|
||
|
rb_mod_class_variables(int argc, VALUE *argv, VALUE mod)
|
||
|
{
|
||
|
VALUE ary = rb_ary_new();
|
||
|
VALUE inherit;
|
||
|
st_table *tbl;
|
||
|
if (RCLASS_IV_TBL(obj)) {
|
||
|
st_foreach_safe(RCLASS_IV_TBL(obj), cv_i, ary);
|
||
|
if (argc == 0) {
|
||
|
inherit = Qtrue;
|
||
|
}
|
||
|
return ary;
|
||
|
else {
|
||
|
rb_scan_args(argc, argv, "01", &inherit);
|
||
|
}
|
||
|
if (RTEST(inherit)) {
|
||
|
tbl = mod_cvar_of(mod, 0);
|
||
|
}
|
||
|
else {
|
||
|
tbl = mod_cvar_at(mod, 0);
|
||
|
}
|
||
|
return cvar_list(tbl);
|
||
|
}
|
||
|
/*
|
||