Access the name of the anonymous class in the superclass "self.inherited

I would like to access the class name in superclass MySuperclass' method self.inherited. It works great for specific classes, as defined class Foo < MySuperclass; end, but it doesn't work when using anonymous classes. I try to avoid creating (class-) constants in tests; I would like it to work with anonymous classes.

Given the following code:

class MySuperclass
  def self.inherited(subclass)
    super
    # work with subclass' name
  end
end

klass = Class.new(MySuperclass) do
  def self.name
    'FooBar'
  end
end

klass#nameit will still be nilwhen it MySuperclass.inheritedis called as it will before it Class.newgives its block and defines its methods.

I understand that a class gets its own namewhen it is assigned to a constant, but is there a way to set Class#name"earlier" without creating a constant?

, .

+3
3

, #yield ::inherited, . , ::klass singleton ::inherited.

def self.klass
   @klass ||= (self.name || self.to_s).gsub(/Builder\z/, '')
end
+1

, , . , , , , , -:

class MySuperclass
  def self.inherited(subclass)
    # Create a class method for the subclass
    subclass.instance_eval do
      def sub_class() puts "sub_class here" end
    end
    # Create an instance method for the subclass
    subclass.class_eval do
      def sub_instance() puts "sub_instance here" end
    end  
  end
end

klass = Class.new(MySuperclass) do
  def self.name=(name)
    @name = Object.const_set(name, self)
  end
  def self.name
    @name
  end
end

klass.sub_class        #=> "sub_class here"
klass.new.sub_instance #=> "sub_instance here"

klass.name = 'Fido'    #=> "Fido"
kn = klass.name        #=> Fido

kn.sub_class           #=> "sub_class here"
kn.new.sub_instance    #=> "sub_instance here"

klass.name = 'Woof'    #=> "Woof"
kn = klass.name        #=> Fido (cannot change)
+1

Ruby , .

C, :

VALUE
force_class_name (VALUE klass, VALUE symbol_name)
{
    rb_name_class(klass, SYM2ID(symbol_name));
    return klass;
}

void
Init_my_extension ()
{
    rb_define_method(rb_cClass, "force_class_name", force_class_name, 1);
}

This is a very difficult approach to the problem. Even if it works, it will not be guaranteed to work in different versions of ruby, since it relies on a non-API C function rb_name_class. I'm also not sure what behavior will be after Ruby starts using its own name hooks.

The code snippet for your use case will look like this:

require 'my_extension'

class MySuperclass
  def self.inherited(subclass)
    super
    subclass.force_class_name(:FooBar)
    # work with subclass' name
  end
end
+1
source

All Articles