ruby-changes:32115
From: nobu <ko1@a...>
Date: Sat, 14 Dec 2013 11:26:02 +0900 (JST)
Subject: [ruby-changes:32115] nobu:r44194 (trunk): object.c: nested path const_defined?
nobu 2013-12-14 11:25:58 +0900 (Sat, 14 Dec 2013) New Revision: 44194 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=44194 Log: object.c: nested path const_defined? * object.c (rb_mod_const_defined): support nested class path as well as const_get. [Feature #7414] Modified files: trunk/ChangeLog trunk/object.c trunk/test/ruby/test_module.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 44193) +++ ChangeLog (revision 44194) @@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Sat Dec 14 11:25:56 2013 Nobuyoshi Nakada <nobu@r...> + + * object.c (rb_mod_const_defined): support nested class path as + well as const_get. [Feature #7414] + Sat Dec 14 01:31:52 2013 Nobuyoshi Nakada <nobu@r...> * eval.c (rb_rescue2): reuse tags pushed for body proc to protect Index: object.c =================================================================== --- object.c (revision 44193) +++ object.c (revision 44194) @@ -2213,6 +2213,8 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/object.c#L2213 rb_mod_const_defined(int argc, VALUE *argv, VALUE mod) { VALUE name, recur; + rb_encoding *enc; + const char *pbeg, *p, *path, *pend; ID id; if (argc == 1) { @@ -2222,20 +2224,86 @@ rb_mod_const_defined(int argc, VALUE *ar https://github.com/ruby/ruby/blob/trunk/object.c#L2224 else { rb_scan_args(argc, argv, "11", &name, &recur); } - if (!(id = rb_check_id(&name))) { - if (rb_is_const_name(name)) { - return Qfalse; + + if (SYMBOL_P(name)) { + id = SYM2ID(name); + if (!rb_is_const_id(id)) goto wrong_id; + return RTEST(recur) ? rb_const_defined(mod, id) : rb_const_defined_at(mod, id); + } + + path = StringValuePtr(name); + enc = rb_enc_get(name); + + if (!rb_enc_asciicompat(enc)) { + rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)"); + } + + pbeg = p = path; + pend = path + RSTRING_LEN(name); + + if (p >= pend || !*p) { + wrong_name: + rb_raise(rb_eNameError, "wrong constant name %"PRIsVALUE, + QUOTE(name)); + } + + if (p + 2 < pend && p[0] == ':' && p[1] == ':') { + mod = rb_cObject; + p += 2; + pbeg = p; + } + + while (p < pend) { + VALUE part; + long len, beglen; + + while (p < pend && *p != ':') p++; + + if (pbeg == p) goto wrong_name; + + id = rb_check_id_cstr(pbeg, len = p-pbeg, enc); + beglen = pbeg-path; + + if (p < pend && p[0] == ':') { + if (p + 2 >= pend || p[1] != ':') goto wrong_name; + p += 2; + pbeg = p; + } + + if (!id) { + if (!ISUPPER(*pbeg) || !rb_enc_symname2_p(pbeg, len, enc)) { + part = rb_str_subseq(name, beglen, len); + rb_name_error_str(part, "wrong constant name %"PRIsVALUE, + QUOTE(part)); + } + else { + return Qfalse; + } + } + if (!rb_is_const_id(id)) { + wrong_id: + rb_name_error(id, "wrong constant name %"PRIsVALUE, + QUOTE_ID(id)); + } + if (RTEST(recur)) { + if (!rb_const_defined(mod, id)) + return Qfalse; + mod = rb_const_get(mod, id); } else { - rb_name_error_str(name, "wrong constant name %"PRIsVALUE, - QUOTE(name)); + if (!rb_const_defined_at(mod, id)) + return Qfalse; + mod = rb_const_get_at(mod, id); + } + recur = Qfalse; + + if (p < pend && !RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) { + rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module", + QUOTE(name)); } } - if (!rb_is_const_id(id)) { - rb_name_error(id, "wrong constant name %"PRIsVALUE, - QUOTE_ID(id)); - } - return RTEST(recur) ? rb_const_defined(mod, id) : rb_const_defined_at(mod, id); + + return Qtrue; } /* Index: test/ruby/test_module.rb =================================================================== --- test/ruby/test_module.rb (revision 44193) +++ test/ruby/test_module.rb (revision 44194) @@ -303,6 +303,26 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L303 end end + def test_nested_defined + assert_send([Object, :const_defined?, [self.class.name, 'Other'].join('::')]) + assert_send([self.class, :const_defined?, 'User::USER']) + assert_not_send([self.class, :const_defined?, 'User::Foo']) + end + + def test_nested_defined_symbol + const = [self.class, Other].join('::').to_sym + assert_raise(NameError) {Object.const_defined?(const)} + + const = [User, 'USER'].join('::').to_sym + assert_raise(NameError) {self.class.const_defined?(const)} + end + + def test_nested_defined_bad_class + assert_raise(TypeError) do + self.class.const_defined?('User::USER::Foo') + end + end + def test_const_set assert_not_operator(Other, :const_defined?, :KOALA) Other.const_set(:KOALA, 99) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/