1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

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.
This commit is contained in:
Ufuk Kayserilioglu 2022-09-27 01:19:22 +03:00 committed by Jean Boussier
parent 192bc72529
commit 0378e2f4a8
Notes: git 2022-10-20 15:30:37 +00:00
6 changed files with 112 additions and 0 deletions

16
NEWS.md
View file

@ -115,6 +115,21 @@ Note: We're only listing outstanding class updates.
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.
## 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

27
class.c
View file

@ -1589,6 +1589,33 @@ rb_class_subclasses(VALUE klass)
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)
{

View file

@ -200,6 +200,18 @@ VALUE rb_class_descendants(VALUE klass);
*/
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.

View file

@ -4464,6 +4464,7 @@ InitVM_Object(void)
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");

View file

@ -0,0 +1,31 @@
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

View file

@ -759,6 +759,31 @@ class TestClass < Test::Unit::TestCase
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