Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/tapioca/runtime/reflection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ def constant_defined?(constant)
# @without_runtime
#: (String symbol, ?inherit: bool, ?namespace: Module[top]) -> BasicObject
def constantize(symbol, inherit: false, namespace: Object)
# Rails deliberately avoids eager loading Configurable because loading it emits a deprecation.
# Leave it available for explicit use, and reflect on it normally if the application loaded it.
if (namespace == Object || symbol.start_with?("::")) &&
symbol.match?(/\A(?:::)?ActiveSupport::Configurable(?:::|\z)/) &&
defined?(::ActiveSupport) && ::ActiveSupport.autoload?(:Configurable) &&
::ActiveSupport.gem_version >= ::Gem::Version.new("8.1")
return UNDEFINED_CONSTANT
end

namespace.const_get(symbol, inherit)
rescue NameError, LoadError, RuntimeError, ArgumentError, TypeError
UNDEFINED_CONSTANT
Expand Down
46 changes: 46 additions & 0 deletions spec/tapioca/cli/gem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,52 @@ class FakeRake::TaskLib; end
RBI
end

it "does not load unused ActiveSupport::Configurable when generating gem RBIs" do
skip("Configurable is not deprecated before Rails 8.1") if ActiveSupport.gem_version < ::Gem::Version.new("8.1")

@project.require_real_gem("activesupport", ActiveSupport.gem_version.to_s, require: "active_support")
@project.write!("sorbet/tapioca/require.rb", <<~RB)
ActiveSupport.deprecator.behavior = :raise
configurable_autoload = ActiveSupport.autoload?(:Configurable)
at_exit do
raise "Configurable autoload changed" unless ActiveSupport.autoload?(:Configurable) == configurable_autoload
end
RB
@project.bundle_install!

result = @project.tapioca("gem activesupport")

assert_empty_stderr(result)
assert_success_status(result)
assert_project_file_match("sorbet/rbi/gems/activesupport@*.rbi")
assert_project_file_includes(
"sorbet/rbi/gems/activesupport@#{ActiveSupport.gem_version}.rbi",
"class ActiveSupport::BacktraceCleaner",
)
end

it "still warns and generates RBIs when the application uses ActiveSupport::Configurable" do
skip("ActiveSupport::Configurable was removed") unless ActiveSupport.const_defined?(:Configurable, false)

@project.require_real_gem("activesupport", ActiveSupport.gem_version.to_s, require: "active_support")
@project.write!("sorbet/tapioca/require.rb", <<~RB)
ActiveSupport.deprecator.behavior = :stderr
ActiveSupport::Configurable
RB
@project.bundle_install!

result = @project.tapioca("gem activesupport")

assert_success_status(result)
if ActiveSupport.gem_version >= ::Gem::Version.new("8.1")
assert_stderr_includes(result, "ActiveSupport::Configurable is deprecated without replacement")
end
assert_project_file_includes(
"sorbet/rbi/gems/activesupport@#{ActiveSupport.gem_version}.rbi",
"module ActiveSupport::Configurable",
)
end

it "uses the correct autoload, even when a gem redefines it via alias-method-chain" do
foo = mock_gem("foo", "0.0.1") do
write!("lib/foo.rb", <<~RB)
Expand Down
Loading