Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a hook for when anonymous classes are assigned to a constant?

I've been practicing some Ruby meta-programming recently, and was wondering about assigning anonymous classes to constants.

In Ruby, it is possible to create an anonymous class as follows:

anonymous_class = Class.new  # => #<Class:0x007f9c5afb21d0>

New instances of this class can be created:

an_instance = anonymous_class.new # => #<#<Class:0x007f9c5afb21d0>:0x007f9c5afb0330>

Now, when the anonymous class is assigned to a constant, the class now has a proper name:

Foo = anonymous_class # => Foo

And the previously created instance is now also an instance of that class:

an_instance # => #<Foo:0x007f9c5afb0330>

My question: Is there a hook method for the moment when an anonymous class is assigned to a constant?

There are many hooks methods in Ruby, but I couldn't find this one.

like image 427
rdvdijk Avatar asked Jan 02 '12 20:01

rdvdijk


People also ask

When can you use anonymous classes?

Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.

Can Anonymous classes be implemented an interface?

A normal class can implement any number of interfaces but the anonymous inner class can implement only one interface at a time.

How do you call an anonymous class in Java?

Object = new Example() { public void display() { System. out. println("Anonymous class overrides the method display()."); } }; Here, an object of the anonymous class is created dynamically when we need to override the display() method.

What is an anonymous class in Android?

In simple words, the anonymous inner class is a class without names and only one object is created. Anonymous class is useful when we have to create an instance of the object with overloading methods of a class or interface without creating a subclass of class.


1 Answers

Let's take a look at how constant assignment works internally. The code that follows is extracted from a source tarball of ruby-1.9.3-p0. First we look at the definition of the VM instruction setconstant (which is used to assign constants):

# /insns.def, line 239
DEFINE_INSN
setconstant
(ID id)
(VALUE val, VALUE cbase)
()
{
  vm_check_if_namespace(cbase);
  rb_const_set(cbase, id, val);
  INC_VM_STATE_VERSION();
}

No chance to place a hook in vm_check_if_namespace or INC_VM_STATE_VERSION here. So we look at rb_const_set (variable.c:1886), the function that is called everytime a constant is assigned:

# /variable.c, line 1886
void
rb_const_set(VALUE klass, ID id, VALUE val)
{
    rb_const_entry_t *ce;
    VALUE visibility = CONST_PUBLIC;

    # ...

    check_before_mod_set(klass, id, val, "constant");
    if (!RCLASS_CONST_TBL(klass)) {
      RCLASS_CONST_TBL(klass) = st_init_numtable();
    }
    else {
      # [snip], won't be called on first assignment
    }

    rb_vm_change_state();

    ce = ALLOC(rb_const_entry_t);
    ce->flag = (rb_const_flag_t)visibility;
    ce->value = val;

    st_insert(RCLASS_CONST_TBL(klass), (st_data_t)id, (st_data_t)ce);
}

I removed all the code that was not even called the first time a constant was assigned inside a module. I then looked into all the functions called by this one and didn't find a single point where we could place a hook from Ruby code. This means the hard truth is, unless I missed something, that there is no way to hook a constant assignment (at least in MRI).

Update

To clarify: The anonymous class does not magically get a new name as soon as it is assigned (as noted correctly in Andrew's answer). Rather, the constant name along with the object ID of the class is stored in Ruby's internal constant lookup table. If, after that, the name of the class is requested, it can now be resolved to a proper name (and not just Class:0xXXXXXXXX...).

So the best you can do to react to this assignment is to check the name of the class in a loop of a background worker thread until it is non-nil (which is a huge waste of resources, IMHO).

like image 126
Niklas B. Avatar answered Sep 28 '22 11:09

Niklas B.