[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]