Skip to content

Define Class#singleton_instance #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ with all sufficient information, see the ChangeLog file.
* extended method:
* Module#define_method accepts a UnboundMethod from a Module.

* Class
* added method:
* added Class#singleton_instance returning the attached instance from a
singleton Class. This is the reversed version of the
Kernel#singleton_class getter method.

* NilClass
* added method:
* added nil.to_h which returns {}
Expand Down
15 changes: 15 additions & 0 deletions class.c
Original file line number Diff line number Diff line change
Expand Up @@ -1294,12 +1294,27 @@ special_singleton_class_of(VALUE obj)
return Qnil;
}

static inline VALUE
special_singleton_instance_of(VALUE obj)
{
SPECIAL_SINGLETON(rb_cNilClass, Qnil);
SPECIAL_SINGLETON(rb_cFalseClass, Qfalse);
SPECIAL_SINGLETON(rb_cTrueClass, Qtrue);
rb_raise(rb_eTypeError, "not singleton class");
}

VALUE
rb_special_singleton_class(VALUE obj)
{
return special_singleton_class_of(obj);
}

VALUE
rb_special_singleton_instance(VALUE obj)
{
return special_singleton_instance_of(obj);
}

/*!
* \internal
* Returns the singleton class of \a obj. Creates it if necessary.
Expand Down
1 change: 1 addition & 0 deletions internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ VALUE rb_obj_private_methods(int argc, VALUE *argv, VALUE obj);
VALUE rb_obj_public_methods(int argc, VALUE *argv, VALUE obj);
int rb_obj_basic_to_s_p(VALUE);
VALUE rb_special_singleton_class(VALUE);
VALUE rb_special_singleton_instance(VALUE);
void Init_class_hierarchy(void);

/* compile.c */
Expand Down
24 changes: 24 additions & 0 deletions object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,29 @@ rb_class_superclass(VALUE klass)
return super;
}

/*
* call-seq:
* class.singleton_instance -> an_object
*
* Returns the attached singleton instance of <i>class</i>.
*
* klass = "Hello".singleton_class
* klass.singleton_instance => "Hello"
*
* If the given class isn't a singleton class, it raises a TypeError.
*
*/

VALUE
rb_class_singleton_instance(VALUE klass)
{
if(FL_TEST(klass, FL_SINGLETON)) {
return rb_iv_get(klass, "__attached__");
} else {
return rb_special_singleton_instance(klass);
}
}

VALUE
rb_class_get_superclass(VALUE klass)
{
Expand Down Expand Up @@ -2989,6 +3012,7 @@ Init_Object(void)
rb_define_method(rb_cClass, "initialize", rb_class_initialize, -1);
rb_define_method(rb_cClass, "initialize_copy", rb_class_init_copy, 1); /* in class.c */
rb_define_method(rb_cClass, "superclass", rb_class_superclass, 0);
rb_define_method(rb_cClass, "singleton_instance", rb_class_singleton_instance, 0);
rb_define_alloc_func(rb_cClass, rb_class_s_alloc);
rb_undef_method(rb_cClass, "extend_object");
rb_undef_method(rb_cClass, "append_features");
Expand Down
21 changes: 21 additions & 0 deletions test/ruby/test_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,27 @@ module Foo; def foo; :foo; end; end
INPUT
end

def test_singleton_instance
o = Object.new
c = o.singleton_class
assert_equal(o, c.singleton_instance)
end

def test_no_singleton_instance
assert_raise(TypeError) { String.singleton_instance }
end

def test_class_singleton_instance
c = Object.singleton_class
assert_equal(Object, c.singleton_instance)
end

def test_immediate_singleton_instance
assert_equal(true, TrueClass.singleton_instance)
assert_equal(false, FalseClass.singleton_instance)
assert_equal(nil, NilClass.singleton_instance)
end

def test_uninitialized
assert_raise(TypeError) { Class.allocate.new }
assert_raise(TypeError) { Class.allocate.superclass }
Expand Down