-
-
Save shugo/1599198 to your computer and use it in GitHub Desktop.
# Module.constants returns constants accessible at the place where the | |
# method is called. | |
class A | |
X = 1 | |
p Module.constants.include?(:X) #=> true | |
end | |
# Module#constants returns constants defined in the receiver. However, | |
# you need a trick to invoke Module#constants on Module itself. | |
p Module.instance_method(:constants).bind(Module).call #=> [] | |
# Another way to invoke Module#constants on Module itself suggested by | |
# Marc-Andre Lafortune in [ruby-core:42091] | |
# It's available only in CRuby 1.9, and I doubt that Matz has accepted the | |
# feature because it has been introduced by nobu secretly. | |
p Module.constants(Module) |
Module.instance_method(:constants).bind(Module).call invokes Module#constants, whose behavior doesn't depend on places where it is called, not Module.constants.
No constants are defined in the class Module itself, so Module.instance_method(:constants).bind(Module).call returns an empty array. If you define a constant in Module, an invocation of Module#constants on Module returns that constant name.
class Module
FOO = 1
end
p Module.instance_method(:constants).bind(Module).call #=> [:FOO]
Do I still miss something?
Yes, you're right, and calling Module.constants
shouldn't be []
,
I am not sure why I wrote that in the first place. I guess I was a bit
confused with the class A
, which is not really used here.
I guess this would make things more clear:
X = 1
# Module.constants returns constants accessible at the place where the
# method is called.
p Module.constants.include?(:X) #=> true
# Module#constants returns constants defined in the receiver. However,
# you need a trick to invoke Module#constants on Module itself.
p Module.instance_method(:constants).bind(Module).call #=> []
And adding below could clarify that the X
is defined in TOPLEVEL_BINDING
which is a context of Object
instead of Module
.
p Module.instance_method(:constants).bind(Object).call.include?(:X) #=> true
My intention was to show that the behavior of Module.constants depends on places where it is called. class A
is used to provide a certain scope there.
The documentation of Module.constants says "Returns an array of the names of all constants defined in the system", but it is incorrect.
After some experiments, I realized that it's the difference between Module#constants
and Module.constants
. They are different methods. The former would only return names in the exact context, so here it is []
. But the latter would return all the names in the TOPLEVEL_BINDING
.
>> Module.instance_method(:constants)
=> #<UnboundMethod: Module#constants>
>> Module.method(:constants).unbind
=> #<UnboundMethod: #<Class:Module>#constants>
I guess the document is saying about the latter, not the former.
sorry, this might be wrong
---And the former is used in something like String.constants
and the latter is used in something like Object.constants
---
Yes, Module#constants and Module.constants are different methods. That's why you need the above trick to invoke Module#constants on Module itself. I'm sure the documentation is saying about the latter, and it's incorrect.
from eval.c:
/*
* call-seq:
* Module.constants -> array
*
* Returns an array of the names of all constants defined in the
* system. This list includes the names of all modules and classes.
*
* p Module.constants.sort[1..5]
*
* <em>produces:</em>
*
* ["ARGV", "ArgumentError", "Array", "Bignum", "Binding"]
*/
You said "the latter would return all the names in the TOPLEVEL_BINDING", but it's wrong.
Module.constants returns all the constant names accessible at the place where Module.constant is called.
Please read my original gist. :X is included in the result of Module.constants even if X is not accessible at top level.
Good point. So I guess the clearest demonstration would be:
class A
X = 1
p Module.constants.include?(:X) #=> true
end
p Module.constants.include?(:X) #=> false
And I think Module.instance_method(:constants).bind(Module).call
is actually a totally different function, which is defined in variable.c:1775 (from ruby-1.9.3-p0)
https://github.com/ruby/ruby/blob/v1_9_3_0/variable.c#L1775-1809 rb_mod_constants (Module#constants)
https://github.com/ruby/ruby/blob/v1_9_3_0/eval.c#L286-328 rb_mod_s_constants (Module.constants)
I guess rb_mod_s_constants
is overriding rb_mod_constants
inside Module
,
and which will also call rb_mod_constants
when argv > 0
.
This is so confusing :o
Thanks for the discussion.
/*
* call-seq:
* mod.constants(inherit=true) -> array
*
* Returns an array of the names of the constants accessible in
* <i>mod</i>. This includes the names of constants in any included
* modules (example at start of section), unless the <i>all</i>
* parameter is set to <code>false</code>.
*
* IO.constants.include?(:SYNC) #=> true
* IO.constants(false).include?(:SYNC) #=> false
*
* Also see <code>Module::const_defined?</code>.
*/
VALUE
rb_mod_constants(int argc, VALUE *argv, VALUE mod)
{
VALUE inherit;
st_table *tbl;
if (argc == 0) {
inherit = Qtrue;
}
else {
rb_scan_args(argc, argv, "01", &inherit);
}
if (RTEST(inherit)) {
tbl = rb_mod_const_of(mod, 0);
}
else {
tbl = rb_mod_const_at(mod, 0);
}
return rb_const_list(tbl);
}
I guess you missed something:
https://gist.github.com/1599287
Otherwise calling
Module.constants
outside A is still[]