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

ruby-changes:74147

From: Ufuk <ko1@a...>
Date: Fri, 21 Oct 2022 00:30:45 +0900 (JST)
Subject: [ruby-changes:74147] 0378e2f4a8 (master): Add Class#attached_object

https://git.ruby-lang.org/ruby.git/commit/?id=0378e2f4a8

From 0378e2f4a8319440dd65c82b16f189161472d237 Mon Sep 17 00:00:00 2001
From: Ufuk Kayserilioglu <ufuk@p...>
Date: Tue, 27 Sep 2022 01:19:22 +0300
Subject: Add Class#attached_object

Implements [Feature #12084]

Returns the object for which the receiver is the singleton class, or
raises TypeError if the receiver is not a singleton class.
---
 NEWS.md                                      | 16 ++++++++++++++
 class.c                                      | 27 ++++++++++++++++++++++++
 include/ruby/internal/intern/class.h         | 12 +++++++++++
 object.c                                     |  1 +
 spec/ruby/core/class/attached_object_spec.rb | 31 ++++++++++++++++++++++++++++
 test/ruby/test_class.rb                      | 25 ++++++++++++++++++++++
 6 files changed, 112 insertions(+)
 create mode 100644 spec/ruby/core/class/attached_object_spec.rb

diff --git a/NEWS.md b/NEWS.md
index 3d618c5abb..9205b6e669 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -115,6 +115,21 @@ Note: We're only listing outstanding class updates. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L115
     STDIN.read # => Blocking operation timed out! (IO::TimeoutError)
     ```
 
+* Class
+    * `Class#attached_object`, which returns the object for which
+      the receiver is the singleton class. Raises `TypeError` if the
+      receiver is not a singleton class.
+      [[Feature #12084]]
+
+      ```ruby
+      class Foo; end
+
+      Foo.singleton_class.attached_object        #=> Foo
+      Foo.new.singleton_class.attached_object    #=> #<Foo:0x000000010491a370>
+      Foo.attached_object                        #=> TypeError: `Foo' is not a singleton class
+      nil.singleton_class.attached_object        #=> TypeError: `NilClass' is not a singleton class
+      ```
+
 * Data
     * New core class to represent simple immutable value object. The class is
       similar to `Struct` and partially shares an implementation, but has more
@@ -323,6 +338,7 @@ The following deprecated APIs are removed. https://github.com/ruby/ruby/blob/trunk/NEWS.md#L338
 ## Miscellaneous changes
 
 [Feature #12005]: https://bugs.ruby-lang.org/issues/12005
+[Feature #12084]: https://bugs.ruby-lang.org/issues/12084
 [Feature #12655]: https://bugs.ruby-lang.org/issues/12655
 [Feature #12737]: https://bugs.ruby-lang.org/issues/12737
 [Feature #13110]: https://bugs.ruby-lang.org/issues/13110
diff --git a/class.c b/class.c
index 7a32951e60..b0d23153de 100644
--- a/class.c
+++ b/class.c
@@ -1589,6 +1589,33 @@ rb_class_subclasses(VALUE klass) https://github.com/ruby/ruby/blob/trunk/class.c#L1589
     return class_descendants(klass, true);
 }
 
+/*
+ *  call-seq:
+ *     attached_object -> object
+ *
+ *  Returns the object for which the receiver is the singleton class.
+ *
+ *  Raises an TypeError if the class is not a singleton class.
+ *
+ *     class Foo; end
+ *
+ *     Foo.singleton_class.attached_object        #=> Foo
+ *     Foo.attached_object                        #=> TypeError: `Foo' is not a singleton class
+ *     Foo.new.singleton_class.attached_object    #=> #<Foo:0x000000010491a370>
+ *     TrueClass.attached_object                  #=> TypeError: `TrueClass' is not a singleton class
+ *     NilClass.attached_object                   #=> TypeError: `NilClass' is not a singleton class
+ */
+
+VALUE
+rb_class_attached_object(VALUE klass)
+{
+    if (!FL_TEST(klass, FL_SINGLETON)) {
+        rb_raise(rb_eTypeError, "`%"PRIsVALUE"' is not a singleton class", klass);
+    }
+
+    return rb_attr_get(klass, id_attached);
+}
+
 static void
 ins_methods_push(st_data_t name, st_data_t ary)
 {
diff --git a/include/ruby/internal/intern/class.h b/include/ruby/internal/intern/class.h
index 2181ab93c7..0fb2d001bc 100644
--- a/include/ruby/internal/intern/class.h
+++ b/include/ruby/internal/intern/class.h
@@ -200,6 +200,18 @@ VALUE rb_class_descendants(VALUE klass); https://github.com/ruby/ruby/blob/trunk/include/ruby/internal/intern/class.h#L200
  */
 VALUE rb_class_subclasses(VALUE klass);
 
+
+/**
+ *  Returns the attached object for a singleton class.
+ *  If the given class is not a singleton class, raises a TypeError.
+ *
+ * @param[in]  klass A class.
+ * @return     The object which has the singleton class `klass`.
+ *
+ * @internal
+ */
+VALUE rb_class_attached_object(VALUE klass);
+
 /**
  * Generates an array of symbols, which are the list of method names defined in
  * the passed class.
diff --git a/object.c b/object.c
index 0d99c6db47..fd07944d14 100644
--- a/object.c
+++ b/object.c
@@ -4464,6 +4464,7 @@ InitVM_Object(void) https://github.com/ruby/ruby/blob/trunk/object.c#L4464
     rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
     rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
     rb_define_method(rb_cClass, "subclasses", rb_class_subclasses, 0); /* in class.c */
+    rb_define_method(rb_cClass, "attached_object", rb_class_attached_object, 0); /* in class.c */
     rb_define_alloc_func(rb_cClass, rb_class_s_alloc);
     rb_undef_method(rb_cClass, "extend_object");
     rb_undef_method(rb_cClass, "append_features");
diff --git a/spec/ruby/core/class/attached_object_spec.rb b/spec/ruby/core/class/attached_object_spec.rb
new file mode 100644
index 0000000000..115d5fa563
--- /dev/null
+++ b/spec/ruby/core/class/attached_object_spec.rb
@@ -0,0 +1,31 @@ https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/class/attached_object_spec.rb#L1
+require_relative '../../spec_helper'
+
+ruby_version_is '3.2' do
+  describe "Class#attached_object" do
+    it "returns the object that is attached to a singleton class" do
+      a = Class.new
+
+      a_obj = a.new
+      a_obj.singleton_class.attached_object.should == a_obj
+    end
+
+    it "returns the class object that is attached to a class's singleton class" do
+      a = Class.new
+      singleton_class = (class << a; self; end)
+
+      singleton_class.attached_object.should == a
+    end
+
+    it "raises TypeError if the class is not a singleton class" do
+      a = Class.new
+
+      -> { a.attached_object }.should raise_error(TypeError)
+    end
+
+    it "raises TypeError for special singleton classes" do
+      -> { nil.singleton_class.attached_object }.should raise_error(TypeError)
+      -> { true.singleton_class.attached_object }.should raise_error(TypeError)
+      -> { false.singleton_class.attached_object }.should raise_error(TypeError)
+    end
+  end
+end
diff --git a/test/ruby/test_class.rb b/test/ruby/test_class.rb
index 07c34ce9d5..8d9fde144e 100644
--- a/test/ruby/test_class.rb
+++ b/test/ruby/test_class.rb
@@ -759,6 +759,31 @@ class TestClass < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_class.rb#L759
     end
   end
 
+  def test_attached_object
+    c = Class.new
+    sc = c.singleton_class
+    obj = c.new
+
+    assert_equal(obj, obj.singleton_class.attached_object)
+    assert_equal(c, sc.attached_object)
+
+    assert_raise_with_message(TypeError, /is not a singleton class/) do
+      c.attached_object
+    end
+
+    assert_raise_with_message(TypeError, /`NilClass' is not a singleton class/) do
+      nil.singleton_class.attached_object
+    end
+
+    assert_raise_with_message(TypeError, /`FalseClass' is not a singleton class/) do
+      false.singleton_class.attached_object
+    end
+
+    assert_raise_with_message(TypeError, /`TrueClass' is not a singleton class/) do
+      true.singleton_class.attached_object
+    end
+  end
+
   def test_subclass_gc
     c = Class.new
     10_000.times do
-- 
cgit v1.2.3


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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