Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

dependencies no longer trigger Kernel#autoload in remove_const [fixes #…

  • Loading branch information...
commit bff4d8d165486797227c5933e93a62e7f2c15d98 1 parent 77edb7c
Xavier Noria fxn authored
2  activesupport/CHANGELOG.md
View
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* Dependencies no longer trigger Kernel#autoload in remove_const [fixes #8213]. *Xavier Noria*
+
* Simplify mocha integration and remove monkey-patches, bumping mocha to 0.13.0. *James Mead*
* `#as_json` isolates options when encoding a hash.
44 activesupport/lib/active_support/dependencies.rb
View
@@ -572,7 +572,6 @@ def safe_constantize(name)
# Determine if the given constant has been automatically loaded.
def autoloaded?(desc)
- # No name => anonymous module.
return false if desc.is_a?(Module) && desc.anonymous?
name = to_constant_name desc
return false unless qualified_const_defined? name
@@ -641,19 +640,38 @@ def to_constant_name(desc) #:nodoc:
end
def remove_constant(const) #:nodoc:
- return false unless qualified_const_defined? const
+ # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
+ normalized = const.to_s.sub(/\A::/, '')
+ normalized.sub!(/\A(Object::)+/, '')
+
+ constants = normalized.split('::')
+ to_remove = constants.pop
+ parent_name = constants.empty? ? 'Object' : constants.join('::')
+
+ if parent = safe_constantize(parent_name)
+ # In an autoloaded user.rb like this
+ #
+ # autoload :Foo, 'foo'
+ #
+ # class User < ActiveRecord::Base
+ # end
+ #
+ # we correctly register "Foo" as being autoloaded. But if the app
+ # does not use the "Foo" constant we need to be careful not to
+ # trigger loading "foo". If the autoload has not been triggered
+ # we already know there is nothing to remove so just return.
+ return if parent.autoload?(to_remove)
- # Normalize ::Foo, Foo, Object::Foo, and ::Object::Foo to Object::Foo
- names = const.to_s.sub(/^::(Object)?/, 'Object::').split("::")
- to_remove = names.pop
- parent = Inflector.constantize(names * '::')
-
- log "removing constant #{const}"
- constantized = constantize(const)
- constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
- parent.instance_eval { remove_const to_remove }
-
- true
+ begin
+ log "removing constant #{const}"
+ constantized = parent.const_get(to_remove, false)
+ rescue NameError
+ log "the constant #{const} is not reachable anymore, skipping"
+ else
+ constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
+ parent.instance_eval { remove_const to_remove }
+ end
+ end
end
protected
1  activesupport/test/autoloading_fixtures/should_not_be_required.rb
View
@@ -0,0 +1 @@
+ShouldNotBeAutoloaded = 0
10 activesupport/test/dependencies_test.rb
View
@@ -928,6 +928,16 @@ def test_remove_constant_handles_double_colon_at_start
assert ! defined?(DeleteMe)
end
+ def test_remove_constant_does_not_trigger_loading_autoloads
+ constant = 'ShouldNotBeAutoloaded'
+ Object.class_eval do
+ autoload constant, File.expand_path('../autoloading_fixtures/should_not_be_required', __FILE__)
+ end
+ ActiveSupport::Dependencies.remove_constant(constant)
+
+ assert Object.autoload?(constant), "Kernel#autoload of #{constant} has been triggered by remove_const"
+ end
+
def test_load_once_constants_should_not_be_unloaded
with_autoloading_fixtures do
ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths
Please sign in to comment.
Something went wrong with that request. Please try again.