ruby-changes:25283
From: tenderlove <ko1@a...>
Date: Sat, 27 Oct 2012 06:31:45 +0900 (JST)
Subject: [ruby-changes:25283] tenderlove:r37335 (trunk): * object.c (rb_mod_const_get): const_get accepts qualified constant
tenderlove 2012-10-27 06:31:23 +0900 (Sat, 27 Oct 2012) New Revision: 37335 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=37335 Log: * 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 Modified files: trunk/ChangeLog trunk/NEWS trunk/object.c trunk/test/ruby/test_module.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 37334) +++ ChangeLog (revision 37335) @@ -1,3 +1,10 @@ +Sat Oct 27 06:28:33 2012 Aaron Patterson <aaron@t...> + + * 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@r...> * parse.y (literal_concat_gen): merge fixed strings across Index: object.c =================================================================== --- object.c (revision 37334) +++ object.c (revision 37335) @@ -1826,9 +1826,38 @@ 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 @@ * 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 @@ 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)); + + 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 if (!rb_method_basic_definition_p(CLASS_OF(mod), id_const_missing)) { - id = rb_to_id(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); } - 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)); - } + + 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; } /* Index: NEWS =================================================================== --- NEWS (revision 37334) +++ NEWS (revision 37335) @@ -66,6 +66,8 @@ 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: Index: test/ruby/test_module.rb =================================================================== --- test/ruby/test_module.rb (revision 37334) +++ test/ruby/test_module.rb (revision 37335) @@ -245,6 +245,36 @@ 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) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/