summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--NEWS2
-rw-r--r--object.c97
-rw-r--r--test/ruby/test_module.rb30
4 files changed, 118 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 67ebd79fa2..27242dd085 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Sat Oct 27 06:28:33 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * object.c (rb_mod_const_get): const_get accepts qualified constant
+ strings. e.g. Object.const_get("Foo::Bar::Baz") [ruby-core:41404]
+
+ * test/ruby/test_module.rb: tests for new behavior
+
Fri Oct 26 13:24:20 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
* parse.y (literal_concat_gen): merge fixed strings across
diff --git a/NEWS b/NEWS
index 0e4508e27d..967196dcd8 100644
--- a/NEWS
+++ b/NEWS
@@ -66,6 +66,8 @@ with all sufficient information, see the ChangeLog file.
corresponding method in the prepending module.
* extended method:
* Module#define_method accepts a UnboundMethod from a Module.
+ * Module#const_get accepts a qualified constant string, e.g.
+ Object.const_get("Foo::Bar::Baz")
* NilClass
* added method:
diff --git a/object.c b/object.c
index eec42bf4d1..a7eccb8ced 100644
--- a/object.c
+++ b/object.c
@@ -1826,9 +1826,38 @@ rb_mod_attr_accessor(int argc, VALUE *argv, VALUE klass)
return Qnil;
}
+static VALUE
+rb_mod_single_const_get(VALUE mod, VALUE name, VALUE recur)
+{
+ ID id;
+
+ id = rb_check_id(&name);
+ if (!id) {
+ if (!rb_is_const_name(name)) {
+ rb_name_error_str(name, "wrong constant name %s", RSTRING_PTR(name));
+ }
+ else if (!rb_method_basic_definition_p(CLASS_OF(mod), id_const_missing)) {
+ id = rb_to_id(name);
+ }
+ else if (mod && rb_class_real(mod) != rb_cObject) {
+ rb_name_error_str(name, "uninitialized constant %s::%s",
+ rb_class2name(mod),
+ RSTRING_PTR(name));
+ }
+ else {
+ rb_name_error_str(name, "uninitialized constant %s", RSTRING_PTR(name));
+ }
+ }
+ if (!rb_is_const_id(id)) {
+ rb_name_error(id, "wrong constant name %s", rb_id2name(id));
+ }
+ return RTEST(recur) ? rb_const_get(mod, id) : rb_const_get_at(mod, id);
+}
+
/*
* call-seq:
* mod.const_get(sym, inherit=true) -> obj
+ * mod.const_get(str, inherit=true) -> obj
*
* Checks for a constant with the given name in <i>mod</i>
* If +inherit+ is set, the lookup will also search
@@ -1838,12 +1867,33 @@ rb_mod_attr_accessor(int argc, VALUE *argv, VALUE klass)
* otherwise a +NameError+ is raised.
*
* Math.const_get(:PI) #=> 3.14159265358979
+ *
+ * This method will recursively look up constant names if a namespaced
+ * class name is provided. For example:
+ *
+ * module Foo; class Bar; end end
+ * Object.const_get 'Foo::Bar'
+ *
+ * The +inherit+ flag is respected on each lookup. For example:
+ *
+ * module Foo
+ * class Bar
+ * VAL = 10
+ * end
+ *
+ * class Baz < Bar; end
+ * end
+ *
+ * Object.const_get 'Foo::Baz::VAL' # => 10
+ * Object.const_get 'Foo::Baz::VAL', false # => NameError
*/
static VALUE
rb_mod_const_get(int argc, VALUE *argv, VALUE mod)
{
VALUE name, recur;
+ rb_encoding *enc;
+ const char *pbeg, *p, *path;
ID id;
if (argc == 1) {
@@ -1853,27 +1903,38 @@ rb_mod_const_get(int argc, VALUE *argv, VALUE mod)
else {
rb_scan_args(argc, argv, "11", &name, &recur);
}
- id = rb_check_id(&name);
- if (!id) {
- if (!rb_is_const_name(name)) {
- rb_name_error_str(name, "wrong constant name %s", RSTRING_PTR(name));
- }
- else if (!rb_method_basic_definition_p(CLASS_OF(mod), id_const_missing)) {
- id = rb_to_id(name);
- }
- else if (mod && rb_class_real(mod) != rb_cObject) {
- rb_name_error_str(name, "uninitialized constant %s::%s",
- rb_class2name(mod),
- RSTRING_PTR(name));
+
+ if (SYMBOL_P(name)) {
+ name = rb_sym_to_s(name);
+ }
+
+ enc = rb_enc_get(name);
+ path = RSTRING_PTR(name);
+
+ if (!rb_enc_asciicompat(enc)) {
+ rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)");
+ }
+
+ pbeg = p = path;
+ while (*p) {
+ while (*p && *p != ':') p++;
+ id = rb_intern3(pbeg, p-pbeg, enc);
+ if (p[0] == ':') {
+ if (p[1] != ':') {
+ rb_raise(rb_eArgError, "undefined class/module %.*s", (int)(p-path), path);
+ }
+ p += 2;
+ pbeg = p;
}
- else {
- rb_name_error_str(name, "uninitialized constant %s", RSTRING_PTR(name));
+
+ if (!RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) {
+ rb_raise(rb_eTypeError, "%s does not refer to class/module", path);
}
+
+ mod = rb_mod_single_const_get(mod, ID2SYM(id), recur);
}
- if (!rb_is_const_id(id)) {
- rb_name_error(id, "wrong constant name %s", rb_id2name(id));
- }
- return RTEST(recur) ? rb_const_get(mod, id) : rb_const_get_at(mod, id);
+
+ return mod;
}
/*
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index db3f1fde3c..774139b9bc 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -245,6 +245,36 @@ class TestModule < Test::Unit::TestCase
assert_equal(Math::PI, Math.const_get(:PI))
end
+ def test_nested_get
+ assert_equal Other, Object.const_get([self.class, Other].join('::'))
+ assert_equal User::USER, self.class.const_get([User, 'USER'].join('::'))
+ end
+
+ def test_nested_get_symbol
+ const = [self.class, Other].join('::').to_sym
+
+ assert_equal Other, Object.const_get(const)
+ assert_equal User::USER, self.class.const_get([User, 'USER'].join('::'))
+ end
+
+ def test_nested_get_const_missing
+ classes = []
+ klass = Class.new {
+ define_singleton_method(:const_missing) { |name|
+ classes << name
+ klass
+ }
+ }
+ klass.const_get("Foo::Bar::Baz")
+ assert_equal [:Foo, :Bar, :Baz], classes
+ end
+
+ def test_nested_bad_class
+ assert_raises(TypeError) do
+ self.class.const_get([User, 'USER', 'Foo'].join('::'))
+ end
+ end
+
def test_const_set
assert(!Other.const_defined?(:KOALA))
Other.const_set(:KOALA, 99)