diff options
author | Xavier Noria <fxn@hashref.com> | 2012-11-28 00:13:14 +0100 |
---|---|---|
committer | Xavier Noria <fxn@hashref.com> | 2012-11-28 00:13:14 +0100 |
commit | 46ebce6b49d8c646241a11821c39e6bbc20d61d5 (patch) | |
tree | 3bfd4cf2985ce2d9638b2b4f8219fc22a49c9fb2 /activesupport/lib | |
parent | 5aade8245a0fe10e80e60fd0d01427406b53e753 (diff) | |
download | rails-46ebce6b49d8c646241a11821c39e6bbc20d61d5.tar.gz rails-46ebce6b49d8c646241a11821c39e6bbc20d61d5.tar.bz2 rails-46ebce6b49d8c646241a11821c39e6bbc20d61d5.zip |
prevent Dependencies#remove_const from autoloading parents [fixes #8301]
Diffstat (limited to 'activesupport/lib')
-rw-r--r-- | activesupport/lib/active_support/dependencies.rb | 76 |
1 files changed, 44 insertions, 32 deletions
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index c75fb46263..b816ecae5a 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -644,46 +644,58 @@ module ActiveSupport #:nodoc: normalized = const.to_s.sub(/\A::/, '') normalized.sub!(/\A(Object::)+/, '') - constants = normalized.split('::') - to_remove = constants.pop - parent_name = constants.empty? ? 'Object' : constants.join('::') + constants = normalized.split('::') + to_remove = constants.pop - if parent = safe_constantize(parent_name) - log "removing constant #{const}" - - # 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.rb" ourselves. While #const_defined? and #const_get? do - # require the file, #autoload? and #remove_const don't. + if constants.empty? + parent = Object + else + # This method is robust to non-reachable constants. # - # We are going to remove the constant nonetheless ---which exists as - # far as Ruby is concerned--- because if the user removes the macro - # call from a class or module that were not autoloaded, as in the - # example above with Object, accessing to that constant must err. - unless parent.autoload?(to_remove) - begin - constantized = parent.const_get(to_remove, false) - rescue NameError - log "the constant #{const} is not reachable anymore, skipping" - return - else - constantized.before_remove_const if constantized.respond_to?(:before_remove_const) - end - end + # Non-reachable constants may be passed if some of the parents were + # autoloaded and already removed. It is easier to do a sanity check + # here than require the caller to be clever. We check the parent + # rather than the very const argument because we do not want to + # trigger Kernel#autoloads, see the comment below. + parent_name = constants.join('::') + return unless qualified_const_defined?(parent_name) + parent = constantize(parent_name) + end + log "removing constant #{const}" + + # 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.rb" ourselves. While #const_defined? and #const_get? do + # require the file, #autoload? and #remove_const don't. + # + # We are going to remove the constant nonetheless ---which exists as + # far as Ruby is concerned--- because if the user removes the macro + # call from a class or module that were not autoloaded, as in the + # example above with Object, accessing to that constant must err. + unless parent.autoload?(to_remove) begin - parent.instance_eval { remove_const to_remove } + constantized = parent.const_get(to_remove, false) rescue NameError log "the constant #{const} is not reachable anymore, skipping" + return + else + constantized.before_remove_const if constantized.respond_to?(:before_remove_const) end end + + begin + parent.instance_eval { remove_const to_remove } + rescue NameError + log "the constant #{const} is not reachable anymore, skipping" + end end protected |