From 11e05defecde965e0eb1929f1b0f1f992be39a6b Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Wed, 14 Dec 2016 11:47:51 +0100 Subject: Fix constantize edge case involving prepend, autoloading and name conflicts In the following situation: ```ruby class Bar end module Baz end class Foo prepend Baz end class Foo::Bar end ``` Running `Inflector.constantize('Foo::Bar')` would blow up with a NameError. What is happening is that `constatize` was written before the introduction of prepend, and wrongly assume that `klass.ancestors.first == klass`. So it uses `klass.ancestors.inject` without arguments, as a result a prepended module is used in place of the actual class. --- activesupport/lib/active_support/inflector/methods.rb | 2 +- activesupport/test/autoloading_fixtures/prepend.rb | 8 ++++++++ .../test/autoloading_fixtures/prepend/sub_class_conflict.rb | 2 ++ activesupport/test/constantize_test_cases.rb | 5 +++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 activesupport/test/autoloading_fixtures/prepend.rb create mode 100644 activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index ef3df1240d..fa063af3f4 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -274,7 +274,7 @@ module ActiveSupport # Go down the ancestors to check if it is owned directly. The check # stops when we reach Object or the end of ancestors tree. - constant = constant.ancestors.inject do |const, ancestor| + constant = constant.ancestors.inject(constant) do |const, ancestor| break const if ancestor == Object break ancestor if ancestor.const_defined?(name, false) const diff --git a/activesupport/test/autoloading_fixtures/prepend.rb b/activesupport/test/autoloading_fixtures/prepend.rb new file mode 100644 index 0000000000..3134d1df2b --- /dev/null +++ b/activesupport/test/autoloading_fixtures/prepend.rb @@ -0,0 +1,8 @@ +class SubClassConflict +end + +class Prepend + module PrependedModule + end + prepend PrependedModule +end diff --git a/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb b/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb new file mode 100644 index 0000000000..090dda3043 --- /dev/null +++ b/activesupport/test/autoloading_fixtures/prepend/sub_class_conflict.rb @@ -0,0 +1,2 @@ +class Prepend::SubClassConflict +end diff --git a/activesupport/test/constantize_test_cases.rb b/activesupport/test/constantize_test_cases.rb index af2db8c991..32b720bcbb 100644 --- a/activesupport/test/constantize_test_cases.rb +++ b/activesupport/test/constantize_test_cases.rb @@ -73,6 +73,11 @@ module ConstantizeTestCases yield("RaisesNoMethodError") end end + + with_autoloading_fixtures do + yield("Prepend::SubClassConflict") + assert_equal "constant", defined?(Prepend::SubClassConflict) + end end def run_safe_constantize_tests_on -- cgit v1.2.3