Issue #17881 has been updated by fxn (Xavier Noria).


Another gotcha I see is that we cannot assume `const_added` is exclusive to Zeitwerk. Users could have their own. So we would need to prepend our own, to make sure it is not rewritten. And on reload, we would need to keep track of which non-reloadable class/module already has the module prepended, not to prepend again. And would need to document that if you prepend, make sure to call `super` in case there's ours.

What I had in mind in the conversation in the Zeitwerk ticket was not `const_added`. It was a callback that gets called when a new class/module is created and after it gets its name set if defined with the `class`/`module` keywords. Analogous to what we use today. That is, some sort of global chainable callback "on_new_module(&block)", hence all these remarks :).

So, as I see it, these are some things to think about:

1. If there's an autoload for `Foo`, `Object.constants` has `Foo`, `Object.const_defined?(`Foo`)` is true, etc. but you did not get a `const_added` call for it. That does not seem coherent. On the other hand, if we make it coherent, it is not usable by Zeitwerk. This is an important one, semantically.

2. Zeitwerk cannot assume it controls `const_added`, needs cooperation from the user (this is doable via documentation).

3. There is no performance issue to address that I know. I believe this patch should be discussed on a generic Ruby API context, independently of Zeitwerk.

4. What would really work for Zeitwerk if we want a "cleaner" API is a chainable `on_new_module(&block)`.

I am not exactly proposing `on_new_module(&block)` because the `:class` event of TP already does that (plus reopening, it is less precise), and because does no seem like existing Ruby hooks. I am mentioning this to highlight the difference between what I thought Zeitwerk could benefit from, from what we are discussing.

----------------------------------------
Feature #17881: Add a Module#const_added callback
https://bugs.ruby-lang.org/issues/17881#change-92127

* Author: byroot (Jean Boussier)
* Status: Open
* Priority: Normal
----------------------------------------
### Use case

Autoloaders like `zeitwerk` need a callback when a new class or module is registered in the constant table.

Currently this is implemented with TracePoint's `:class` event. It works, but it is a bit unfortunate to have to use an API intended for debugging to implement production features. It doesn't feel "conceptually clean". 

It also [doesn't play well with MJIT](https://k0kubun.medium.com/ruby-3-jit-can-make-rails-faster-756310f235a), even though it's more of an MJIT limitation.

Additionally this usage of TracePoint cause [some incompatibilities with some debuggers like `byebug`](https://github.com/deivid-rodriguez/byebug/issues/564) (even though others don't have this issue). 

### Proposal

I believe that if Ruby was to call `Module#const_added` when a constant is registered, Zeitwerk could get rid of TracePoint.

For now I implemented it as: `const_added(const_name)` for similarity with `method_added`. But maybe it could make sense to have the signature be `const_added(const_name, const_value)`.

Also since `method_removed` exists, maybe `const_removed` would need to be added for consistency.

### Links

Patch: https://github.com/ruby/ruby/pull/4521
Zeitwerk side discussion: https://github.com/fxn/zeitwerk/issues/135

cc @k0kubun



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>