Project

General

Profile

Actions

Feature #6478

closed

BasicObject#__class__

Added by trans (Thomas Sawyer) over 12 years ago. Updated almost 7 years ago.

Status:
Feedback
Target version:
-
[ruby-core:45180]

Description

How else is one supposed to get the class of a subclass of BasicObject?

Updated by mame (Yusuke Endoh) over 12 years ago

  • Status changed from Open to Feedback

I don't understand you.

--
Yusuke Endoh

Updated by trans (Thomas Sawyer) over 12 years ago

=begin
Sorry, I'll be more specific via example:

class Foo < BasicObject
end

foo = Foo.new

foo.class #=> raises NoMethodError

How to get class?

I suggest adding #class feature if there is no current means.
=end

Updated by mame (Yusuke Endoh) over 12 years ago

  • Status changed from Feedback to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

Okay, thanks. I assign this to matz.

--
Yusuke Endoh

Updated by nobu (Nobuyoshi Nakada) over 12 years ago

  • Status changed from Assigned to Feedback
  • Target version changed from 1.9.3 to 2.0.0

=begin
((Why)) do you need it?

BTW, it's possible with pure-ruby.

class Foo < BasicObject
include ::Kernel.dup.module_eval {
alias_method(:class, :class)
undef_method *(instance_methods - [:class, :object_id])
self
}
end

p Foo.new.class
=end

Updated by trans (Thomas Sawyer) over 12 years ago

=begin
To ensure proper functionality when creating new instances from subclasses.

class Foo < BasicObject
def initialize(stuff)
@stuff = stuff
end
def dup
class.new(@stuff)
end
end

class Bar < Foo
end

We can't use (({Foo})) in dup, otherwise Bar would not be right.
=end

Updated by trans (Thomas Sawyer) over 12 years ago

"BTW, it's possible with pure-ruby."

That's a rather nasty implementation. Is there no better way than that? I tried binding Kernel method but that didn't work, obviously, b/c BasicObject isn't "an instance of Kernel".

Updated by nobu (Nobuyoshi Nakada) over 12 years ago

=begin
Seems what you want is (({dup})), not (({class})).

class Foo < BasicObject
mix ::Kernel, dup: :dup, clone: :clone
end
=end

Updated by trans (Thomas Sawyer) over 12 years ago

That was just one example. Here, you can look at this for more cases:

https://github.com/rubyworks/ostruct2/blob/master/lib/ostruct2.rb

Just ctrl-f for class.

But what's this about "mix"? What Ruby are you running!? This is interesting, b/c I was thinking that I could use #respond_to? and I don't see anyway to add it to my BasicObject subclass except the "nasty" approach you demonstrated earlier.

Updated by nobu (Nobuyoshi Nakada) over 12 years ago

=begin
(({Module#mix})) is a feature introduced last year, but may be removed from 2.0.
=end

Updated by Eregon (Benoit Daloze) over 12 years ago

nobu (Nobuyoshi Nakada) wrote:

Seems what you want is (({dup})), not (({class})).

class Foo < BasicObject
mix ::Kernel, dup: :dup, clone: :clone
end

But that would include all methods from Kernel with the current behavior of #mix, as mix ::Kernel would do.
So you need to opt-out all methods:

class Foo < BasicObject
  meths = (::Kernel.instance_methods - [:dup])
  mix ::Kernel, meths.each_with_object(dup: :dup) { |m,h| h[m] = nil }
end

(And Foo.new.dup fails with "undefined method `initialize_dup'")

That behavior of #mix is not very intuitive I think, what do you think about:

diff --git a/class.c b/class.c
index 8e637c0..e9d7a7e 100644
--- a/class.c
+++ b/class.c
@@ -769,8 +769,9 @@ do_mix_method_i(st_data_t key, st_data_t value, st_data_t arg)
st_table *aliasing = argp->aliasing;
st_data_t old, alias;

  • if (aliasing && st_lookup(aliasing, ID2SYM(id), &alias)) {
  •   if (NIL_P(alias)) return ST_CONTINUE;
    
  • if (aliasing) {
  •   if (!st_lookup(aliasing, ID2SYM(id), &alias) || NIL_P(alias))
    
  •       return ST_CONTINUE;
      id = rb_to_id(alias);
    
    }
    if (st_lookup(argp->mtbl, id, &old)) {

(and corresponding changes for the three other functions).
That is, if a Hash of methods is given, only import these methods.

Updated by yhara (Yutaka HARA) about 12 years ago

  • Target version changed from 2.0.0 to 2.6

Updated by nobu (Nobuyoshi Nakada) about 12 years ago

=begin
"Method transplanting" is introduced into 2.0, so you can write:
class Foo < BasicObject
include ::Module.new {
[:dup, :initialize_dup, :initialize_copy].each {|m|
define_method(m, ::Kernel.instance_method(m))
}
}
end

I expect someone would make such method in (({Module})) as an external library.
=end

Updated by alexeymuranov (Alexey Muranov) about 12 years ago

Maybe BasicObject is not intended to be subclassed directly? Why not to subclass Object instead? I do not think it is wrong that basic objects do not know who their class is, after all they are basic.

Actions #14

Updated by naruse (Yui NARUSE) almost 7 years ago

  • Target version deleted (2.6)
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0