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


Let me also add something for context. @byroot knows it and probably @Eregon too, just for anybody else following.

Something the TP on the :class event allows you to do is to enable only if you need it. Allows you to be precise.

If the TP is not needed ever, it is not even enabled. If at some point you needed it, but no longer, Zeitwerk disables it. The tracer is managed by [this module](https://github.com/fxn/zeitwerk/blob/main/lib/zeitwerk/explicit_namespace.rb) where you can see the fine logic in register/deregister.

A TP on the :class event is only needed if some of the loaders in the Ruby process saw both a file "foo.rb" and a directory "foo", and Foo has not been yet loaded. As soon as Foo is loaded, if there are no more constants in a similar situation, the tracer is disabled.

In particular, the tracer becomes normally disabled in a Rails application in production mode because Zeitwerk eager loads not only the application, but any other gem managed by Zeitwerk. That is what Zeitwerk::Loader.eager_load_all does [here](https://github.com/rails/rails/blob/3bd6b688d0d46f3e2c4fb1d2ad8cbd2f61caf7ff/railties/lib/rails/application/finisher.rb#L111).

So, I find a callback to be neat from an implementation standpoint (if one associates TracePoint to debugging use cases), but you wouldn't normally be able to be so precise. Though you could probably at least bail out first thing in the block body via a maintained flag or something.

TP on the :class event, ironically, seems a tad off for me, but it happens to be perfect to solve this problem. There is no performance penalty that I know of, and in the case of JITs, on paper it should be fine (it is in a JIT @byroot checked).

So... no idea, it's core's call :).

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

* 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>