Skip to content

Skip ActiveSupport deprecation proxies when gathering DSL constants - #2701

Merged
KaanOzkan merged 5 commits into
Shopify:mainfrom
Hashim1999164:fix/skip-deprecation-proxies-in-dsl-discovery
Aug 26, 2026
Merged

Skip ActiveSupport deprecation proxies when gathering DSL constants#2701
KaanOzkan merged 5 commits into
Shopify:mainfrom
Hashim1999164:fix/skip-deprecation-proxies-in-dsl-discovery

Conversation

@Hashim1999164

@Hashim1999164 Hashim1999164 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #2463

Rails 8.1 replaced LoadInterlockAwareMonitor with a DeprecatedConstantProxy. DSL compilers walk every loaded module and call methods such as singleton_class on each one. Those calls go through method_missing on the proxy and print deprecation warnings even though Tapioca is only enumerating ObjectSpace.

This change drops DeprecatedConstantProxy modules from all_modules so every DSL compiler skips them. Kernel.class is used to recognize the proxy without triggering the warning.

The ActiveSupportConcern gather_constants spec covers the Rails 8.1 LoadInterlockAwareMonitor proxy.

@Hashim1999164
Hashim1999164 requested a review from a team as a code owner August 18, 2026 19:51

@KaanOzkan KaanOzkan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the fix. Minor suggestions.

ActiveSupport.deprecator.behavior = previous_behavior
end

refute_includes(constants, "ActiveSupport::Concurrency::LoadInterlockAwareMonitor")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't dive into it but agent suggested this diff instead and claimed that current refute_includes would pass even without this change because the name is delegated and becomes Monitor, it might be better to do the diff below. Also can you ensure the final test fails without your change?

- constants = gathered_constants
+ proxy = ActiveSupport::Concurrency.const_get(:LoadInterlockAwareMonitor, false)
+ gathered_constants
+ all_modules = Tapioca::Dsl::Compilers::ActiveSupportConcern.send(:all_modules)
...
- refute_includes(constants, "ActiveSupport::Concurrency::LoadInterlockAwareMonitor")
+ refute(all_modules.any? { |mod| Tapioca::Runtime::Reflection.are_equal?(mod, proxy) })
  assert_empty(warnings.grep(/LoadInterlockAwareMonitor/))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. The spec now loads the proxy with inherit false and checks identity against all_modules so the assertion fails if the skip is missing.

Comment thread lib/tapioca/dsl/compiler.rb Outdated
else
ObjectSpace.each_object(Module).to_a
end.freeze #: Enumerable[Module[top]]?
end.reject { |mod| deprecated_constant_proxy?(mod) }.freeze #: Enumerable[Module[top]]?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to abide by the requested constants even if they result in warnings. Can you move this reject above?

ObjectSpace.each_object(Module).reject { |mod| deprecated_constant_proxy?(mod) }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved. Requested constants stay in the set even if they warn. ObjectSpace discovery is the only path that skips the proxies.

Requested constants stay in the set even if they warn. The spec now checks the proxy by identity so it fails without the skip.
@Hashim1999164

Copy link
Copy Markdown
Contributor Author

Moved the deprecation proxy skip onto the ObjectSpace path only, so requested constants still make it through. The spec now looks up the proxy by identity instead of the delegated name.


refute(all_modules.any? { |mod| Tapioca::Runtime::Reflection.are_equal?(mod, proxy) })
assert_empty(warnings.grep(/FakeDeprecatedConstant/))
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this test to register a fake constant as deprecated since these things might change in future Rails versions.

@KaanOzkan
KaanOzkan force-pushed the fix/skip-deprecation-proxies-in-dsl-discovery branch from e5da488 to 9156fa1 Compare August 26, 2026 19:07
@Hashim1999164

Copy link
Copy Markdown
Contributor Author

Linters failed on RSpec/RemoveConst in the deprecation proxy spec. Added the same rubocop disable the other specs use for that cleanup.

@KaanOzkan
KaanOzkan enabled auto-merge August 26, 2026 19:30
@KaanOzkan
KaanOzkan disabled auto-merge August 26, 2026 19:37
The nested Minitest `describe` block uses a dynamic class that is not registered in the Ruby constant tree. Assigning the proxy through `self.class` leaves `Module#name` unset, so discovery skips it before invoking warning-producing methods.

Assign the proxy through `ActiveSupportConcernSpec` so the test covers the warning path. `DslSpec` runs each example in an isolated process, so the temporary constant does not need manual cleanup.
@KaanOzkan
KaanOzkan enabled auto-merge August 26, 2026 19:45
@KaanOzkan
KaanOzkan merged commit 994d158 into Shopify:main Aug 26, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rails 8.1 deprecation warnings for LoadInterlockAwareMonitor during DSL generation

2 participants