mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
1cfeab5314
Cleans up four items I came across in ActiveSupport::Dependencies: - DependenciesTest# test_dependency_which_raises_exception_isnt_added_to_loaded_set: Fixes current implementation which will pass no matter what since the filepath is never added to "loaded" or "history" without being expanded first. - Remove DependenciesTest#test_unhook. Seems leftover from when alias_method_chain was used in Loadable and ModuleConstMissing. The test will always pass since Module never responds to those methods - WatchStack#new_constants documentation: update self to @stack. Looks like self was leftover from when WatchStack inherited from Hash - Remove ActiveSupport namespace from call to Dependencies.constant_watch_stack.watching? since the namespace is not needed, Dependencies is called two other times in the same method without it (even on the same line) and it brings the line to within 80 characters
1051 lines
35 KiB
Ruby
1051 lines
35 KiB
Ruby
require 'abstract_unit'
|
|
require 'pp'
|
|
require 'active_support/dependencies'
|
|
require 'dependencies_test_helpers'
|
|
|
|
module ModuleWithMissing
|
|
mattr_accessor :missing_count
|
|
def self.const_missing(name)
|
|
self.missing_count += 1
|
|
name
|
|
end
|
|
end
|
|
|
|
module ModuleWithConstant
|
|
InheritedConstant = "Hello"
|
|
end
|
|
|
|
class DependenciesTest < ActiveSupport::TestCase
|
|
include DependenciesTestHelpers
|
|
|
|
setup do
|
|
@loaded_features_copy = $LOADED_FEATURES.dup
|
|
end
|
|
|
|
teardown do
|
|
ActiveSupport::Dependencies.clear
|
|
$LOADED_FEATURES.replace(@loaded_features_copy)
|
|
end
|
|
|
|
def test_depend_on_path
|
|
expected = assert_raises(LoadError) do
|
|
Kernel.require 'omgwtfbbq'
|
|
end
|
|
|
|
e = assert_raises(LoadError) do
|
|
ActiveSupport::Dependencies.depend_on 'omgwtfbbq'
|
|
end
|
|
assert_equal expected.path, e.path
|
|
end
|
|
|
|
def test_require_dependency_accepts_an_object_which_implements_to_path
|
|
o = Object.new
|
|
def o.to_path; 'dependencies/service_one'; end
|
|
assert_nothing_raised {
|
|
require_dependency o
|
|
}
|
|
assert defined?(ServiceOne)
|
|
ensure
|
|
remove_constants(:ServiceOne)
|
|
end
|
|
|
|
def test_tracking_loaded_files
|
|
with_loading do
|
|
require_dependency 'dependencies/service_one'
|
|
require_dependency 'dependencies/service_two'
|
|
assert_equal 2, ActiveSupport::Dependencies.loaded.size
|
|
end
|
|
ensure
|
|
remove_constants(:ServiceOne, :ServiceTwo)
|
|
end
|
|
|
|
def test_tracking_identical_loaded_files
|
|
with_loading do
|
|
require_dependency 'dependencies/service_one'
|
|
require_dependency 'dependencies/service_one'
|
|
assert_equal 1, ActiveSupport::Dependencies.loaded.size
|
|
end
|
|
ensure
|
|
remove_constants(:ServiceOne)
|
|
end
|
|
|
|
def test_missing_dependency_raises_missing_source_file
|
|
assert_raise(LoadError) { require_dependency("missing_service") }
|
|
end
|
|
|
|
def test_dependency_which_raises_exception_isnt_added_to_loaded_set
|
|
with_loading do
|
|
filename = 'dependencies/raises_exception'
|
|
expanded = File.expand_path(filename)
|
|
$raises_exception_load_count = 0
|
|
|
|
5.times do |count|
|
|
e = assert_raise Exception, 'should have loaded dependencies/raises_exception which raises an exception' do
|
|
require_dependency filename
|
|
end
|
|
|
|
assert_equal 'Loading me failed, so do not add to loaded or history.', e.message
|
|
assert_equal count + 1, $raises_exception_load_count
|
|
|
|
assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
|
|
assert_not ActiveSupport::Dependencies.history.include?(expanded)
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_dependency_which_raises_doesnt_blindly_call_blame_file!
|
|
with_loading do
|
|
filename = 'dependencies/raises_exception_without_blame_file'
|
|
assert_raises(Exception) { require_dependency filename }
|
|
end
|
|
end
|
|
|
|
def test_warnings_should_be_enabled_on_first_load
|
|
with_loading 'dependencies' do
|
|
old_warnings, ActiveSupport::Dependencies.warnings_on_first_load = ActiveSupport::Dependencies.warnings_on_first_load, true
|
|
filename = "check_warnings"
|
|
expanded = File.expand_path("#{File.dirname(__FILE__)}/dependencies/#{filename}")
|
|
$check_warnings_load_count = 0
|
|
|
|
assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
|
|
assert_not ActiveSupport::Dependencies.history.include?(expanded)
|
|
|
|
silence_warnings { require_dependency filename }
|
|
assert_equal 1, $check_warnings_load_count
|
|
assert_equal true, $checked_verbose, 'On first load warnings should be enabled.'
|
|
|
|
assert ActiveSupport::Dependencies.loaded.include?(expanded)
|
|
ActiveSupport::Dependencies.clear
|
|
assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
|
|
assert ActiveSupport::Dependencies.history.include?(expanded)
|
|
|
|
silence_warnings { require_dependency filename }
|
|
assert_equal 2, $check_warnings_load_count
|
|
assert_equal nil, $checked_verbose, 'After first load warnings should be left alone.'
|
|
|
|
assert ActiveSupport::Dependencies.loaded.include?(expanded)
|
|
ActiveSupport::Dependencies.clear
|
|
assert_not ActiveSupport::Dependencies.loaded.include?(expanded)
|
|
assert ActiveSupport::Dependencies.history.include?(expanded)
|
|
|
|
enable_warnings { require_dependency filename }
|
|
assert_equal 3, $check_warnings_load_count
|
|
assert_equal true, $checked_verbose, 'After first load warnings should be left alone.'
|
|
|
|
assert ActiveSupport::Dependencies.loaded.include?(expanded)
|
|
ActiveSupport::Dependencies.warnings_on_first_load = old_warnings
|
|
end
|
|
end
|
|
|
|
def test_mutual_dependencies_dont_infinite_loop
|
|
with_loading 'dependencies' do
|
|
$mutual_dependencies_count = 0
|
|
assert_nothing_raised { require_dependency 'mutual_one' }
|
|
assert_equal 2, $mutual_dependencies_count
|
|
|
|
ActiveSupport::Dependencies.clear
|
|
|
|
$mutual_dependencies_count = 0
|
|
assert_nothing_raised { require_dependency 'mutual_two' }
|
|
assert_equal 2, $mutual_dependencies_count
|
|
end
|
|
end
|
|
|
|
def test_circular_autoloading_detection
|
|
with_autoloading_fixtures do
|
|
e = assert_raise(RuntimeError) { Circular1 }
|
|
assert_equal "Circular dependency detected while autoloading constant Circular1", e.message
|
|
end
|
|
end
|
|
|
|
def test_ensures_the_expected_constant_is_defined
|
|
with_autoloading_fixtures do
|
|
e = assert_raise(LoadError) { Typo }
|
|
assert_match %r{Unable to autoload constant Typo, expected .*/test/autoloading_fixtures/typo.rb to define it}, e.message
|
|
end
|
|
end
|
|
|
|
def test_require_dependency_does_not_assume_any_particular_constant_is_defined
|
|
with_autoloading_fixtures do
|
|
require_dependency 'typo'
|
|
assert_equal 1, TypO
|
|
end
|
|
end
|
|
|
|
# Regression, see https://github.com/rails/rails/issues/16468.
|
|
def test_require_dependency_interaction_with_autoloading
|
|
with_autoloading_fixtures do
|
|
require_dependency 'typo'
|
|
assert_equal 1, TypO
|
|
|
|
e = assert_raise(LoadError) { Typo }
|
|
assert_match %r{Unable to autoload constant Typo, expected .*/test/autoloading_fixtures/typo.rb to define it}, e.message
|
|
end
|
|
end
|
|
|
|
def test_module_loading
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Module, A
|
|
assert_kind_of Class, A::B
|
|
assert_kind_of Class, A::C::D
|
|
assert_kind_of Class, A::C::EM::F
|
|
end
|
|
end
|
|
|
|
def test_non_existing_const_raises_name_error
|
|
with_autoloading_fixtures do
|
|
assert_raise(NameError) { DoesNotExist }
|
|
assert_raise(NameError) { NoModule::DoesNotExist }
|
|
assert_raise(NameError) { A::DoesNotExist }
|
|
assert_raise(NameError) { A::B::DoesNotExist }
|
|
end
|
|
end
|
|
|
|
def test_directories_manifest_as_modules_unless_const_defined
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Module, ModuleFolder
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleFolder)
|
|
end
|
|
|
|
def test_module_with_nested_class
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Class, ModuleFolder::NestedClass
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleFolder)
|
|
end
|
|
|
|
def test_module_with_nested_inline_class
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Class, ModuleFolder::InlineClass
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleFolder)
|
|
end
|
|
|
|
def test_directories_may_manifest_as_nested_classes
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Class, ClassFolder
|
|
end
|
|
ensure
|
|
remove_constants(:ClassFolder)
|
|
end
|
|
|
|
def test_class_with_nested_class
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Class, ClassFolder::NestedClass
|
|
end
|
|
ensure
|
|
remove_constants(:ClassFolder)
|
|
end
|
|
|
|
def test_class_with_nested_inline_class
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Class, ClassFolder::InlineClass
|
|
end
|
|
ensure
|
|
remove_constants(:ClassFolder)
|
|
end
|
|
|
|
def test_class_with_nested_inline_subclass_of_parent
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Class, ClassFolder::ClassFolderSubclass
|
|
assert_kind_of Class, ClassFolder
|
|
assert_equal 'indeed', ClassFolder::ClassFolderSubclass::ConstantInClassFolder
|
|
end
|
|
ensure
|
|
remove_constants(:ClassFolder)
|
|
end
|
|
|
|
def test_nested_class_can_access_sibling
|
|
with_autoloading_fixtures do
|
|
sibling = ModuleFolder::NestedClass.class_eval "NestedSibling"
|
|
assert defined?(ModuleFolder::NestedSibling)
|
|
assert_equal ModuleFolder::NestedSibling, sibling
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleFolder)
|
|
end
|
|
|
|
def test_doesnt_break_normal_require
|
|
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
|
|
original_path = $:.dup
|
|
$:.push(path)
|
|
with_autoloading_fixtures do
|
|
# The _ = assignments are to prevent warnings
|
|
_ = RequiresConstant
|
|
assert defined?(RequiresConstant)
|
|
assert defined?(LoadedConstant)
|
|
ActiveSupport::Dependencies.clear
|
|
_ = RequiresConstant
|
|
assert defined?(RequiresConstant)
|
|
assert defined?(LoadedConstant)
|
|
end
|
|
ensure
|
|
remove_constants(:RequiresConstant, :LoadedConstant)
|
|
$:.replace(original_path)
|
|
end
|
|
|
|
def test_doesnt_break_normal_require_nested
|
|
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
|
|
original_path = $:.dup
|
|
$:.push(path)
|
|
|
|
with_autoloading_fixtures do
|
|
# The _ = assignments are to prevent warnings
|
|
_ = LoadsConstant
|
|
assert defined?(LoadsConstant)
|
|
assert defined?(LoadedConstant)
|
|
ActiveSupport::Dependencies.clear
|
|
_ = LoadsConstant
|
|
assert defined?(LoadsConstant)
|
|
assert defined?(LoadedConstant)
|
|
end
|
|
ensure
|
|
remove_constants(:RequiresConstant, :LoadedConstant, :LoadsConstant)
|
|
$:.replace(original_path)
|
|
end
|
|
|
|
def test_require_returns_true_when_file_not_yet_required
|
|
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
|
|
original_path = $:.dup
|
|
$:.push(path)
|
|
|
|
with_loading do
|
|
assert_equal true, require('loaded_constant')
|
|
end
|
|
ensure
|
|
remove_constants(:LoadedConstant)
|
|
$:.replace(original_path)
|
|
end
|
|
|
|
def test_require_returns_true_when_file_not_yet_required_even_when_no_new_constants_added
|
|
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
|
|
original_path = $:.dup
|
|
$:.push(path)
|
|
|
|
with_loading do
|
|
Object.module_eval "module LoadedConstant; end"
|
|
assert_equal true, require('loaded_constant')
|
|
end
|
|
ensure
|
|
remove_constants(:LoadedConstant)
|
|
$:.replace(original_path)
|
|
end
|
|
|
|
def test_require_returns_false_when_file_already_required
|
|
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
|
|
original_path = $:.dup
|
|
$:.push(path)
|
|
|
|
with_loading do
|
|
require 'loaded_constant'
|
|
assert_equal false, require('loaded_constant')
|
|
end
|
|
ensure
|
|
remove_constants(:LoadedConstant)
|
|
$:.replace(original_path)
|
|
end
|
|
|
|
def test_require_raises_load_error_when_file_not_found
|
|
with_loading do
|
|
assert_raise(LoadError) { require 'this_file_dont_exist_dude' }
|
|
end
|
|
end
|
|
|
|
def test_load_returns_true_when_file_found
|
|
path = File.expand_path("../autoloading_fixtures/load_path", __FILE__)
|
|
original_path = $:.dup
|
|
$:.push(path)
|
|
|
|
with_loading do
|
|
assert_equal true, load('loaded_constant.rb')
|
|
assert_equal true, load('loaded_constant.rb')
|
|
end
|
|
ensure
|
|
remove_constants(:LoadedConstant)
|
|
$:.replace(original_path)
|
|
end
|
|
|
|
def test_load_raises_load_error_when_file_not_found
|
|
with_loading do
|
|
assert_raise(LoadError) { load 'this_file_dont_exist_dude.rb' }
|
|
end
|
|
end
|
|
|
|
def failing_test_access_thru_and_upwards_fails
|
|
with_autoloading_fixtures do
|
|
assert_not defined?(ModuleFolder)
|
|
assert_raise(NameError) { ModuleFolder::Object }
|
|
assert_raise(NameError) { ModuleFolder::NestedClass::Object }
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleFolder)
|
|
end
|
|
|
|
def test_non_existing_const_raises_name_error_with_fully_qualified_name
|
|
with_autoloading_fixtures do
|
|
e = assert_raise(NameError) { A::DoesNotExist.nil? }
|
|
assert_equal "uninitialized constant A::DoesNotExist", e.message
|
|
assert_equal :DoesNotExist, e.name
|
|
|
|
e = assert_raise(NameError) { A::B::DoesNotExist.nil? }
|
|
assert_equal "uninitialized constant A::B::DoesNotExist", e.message
|
|
assert_equal :DoesNotExist, e.name
|
|
end
|
|
ensure
|
|
remove_constants(:A)
|
|
end
|
|
|
|
def test_smart_name_error_strings
|
|
e = assert_raise NameError do
|
|
Object.module_eval "ImaginaryObject"
|
|
end
|
|
assert_includes "uninitialized constant ImaginaryObject", e.message
|
|
end
|
|
|
|
def test_loadable_constants_for_path_should_handle_empty_autoloads
|
|
assert_equal [], ActiveSupport::Dependencies.loadable_constants_for_path('hello')
|
|
end
|
|
|
|
def test_loadable_constants_for_path_should_handle_relative_paths
|
|
fake_root = 'dependencies'
|
|
relative_root = File.dirname(__FILE__) + '/dependencies'
|
|
['', '/'].each do |suffix|
|
|
with_loading fake_root + suffix do
|
|
assert_equal ["A::B"], ActiveSupport::Dependencies.loadable_constants_for_path(relative_root + '/a/b')
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_loadable_constants_for_path_should_provide_all_results
|
|
fake_root = '/usr/apps/backpack'
|
|
with_loading fake_root, fake_root + '/lib' do
|
|
root = ActiveSupport::Dependencies.autoload_paths.first
|
|
assert_equal ["Lib::A::B", "A::B"], ActiveSupport::Dependencies.loadable_constants_for_path(root + '/lib/a/b')
|
|
end
|
|
end
|
|
|
|
def test_loadable_constants_for_path_should_uniq_results
|
|
fake_root = '/usr/apps/backpack/lib'
|
|
with_loading fake_root, fake_root + '/' do
|
|
root = ActiveSupport::Dependencies.autoload_paths.first
|
|
assert_equal ["A::B"], ActiveSupport::Dependencies.loadable_constants_for_path(root + '/a/b')
|
|
end
|
|
end
|
|
|
|
def test_loadable_constants_with_load_path_without_trailing_slash
|
|
path = File.dirname(__FILE__) + '/autoloading_fixtures/class_folder/inline_class.rb'
|
|
with_loading 'autoloading_fixtures/class/' do
|
|
assert_equal [], ActiveSupport::Dependencies.loadable_constants_for_path(path)
|
|
end
|
|
end
|
|
|
|
def test_qualified_const_defined
|
|
assert ActiveSupport::Dependencies.qualified_const_defined?("Object")
|
|
assert ActiveSupport::Dependencies.qualified_const_defined?("::Object")
|
|
assert ActiveSupport::Dependencies.qualified_const_defined?("::Object::Kernel")
|
|
assert ActiveSupport::Dependencies.qualified_const_defined?("::ActiveSupport::TestCase")
|
|
end
|
|
|
|
def test_qualified_const_defined_should_not_call_const_missing
|
|
ModuleWithMissing.missing_count = 0
|
|
assert ! ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A")
|
|
assert_equal 0, ModuleWithMissing.missing_count
|
|
assert ! ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A::B")
|
|
assert_equal 0, ModuleWithMissing.missing_count
|
|
end
|
|
|
|
def test_qualified_const_defined_explodes_with_invalid_const_name
|
|
assert_raises(NameError) { ActiveSupport::Dependencies.qualified_const_defined?("invalid") }
|
|
end
|
|
|
|
def test_autoloaded?
|
|
with_autoloading_fixtures do
|
|
assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
|
|
assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
|
|
|
|
assert ActiveSupport::Dependencies.autoloaded?(ModuleFolder)
|
|
|
|
assert ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
|
|
assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
|
|
|
|
assert ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass)
|
|
|
|
assert ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
|
|
assert ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
|
|
|
|
assert ActiveSupport::Dependencies.autoloaded?("::ModuleFolder")
|
|
assert ActiveSupport::Dependencies.autoloaded?(:ModuleFolder)
|
|
|
|
# Anonymous modules aren't autoloaded.
|
|
assert !ActiveSupport::Dependencies.autoloaded?(Module.new)
|
|
|
|
nil_name = Module.new
|
|
def nil_name.name() nil end
|
|
assert !ActiveSupport::Dependencies.autoloaded?(nil_name)
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleFolder)
|
|
end
|
|
|
|
def test_qualified_name_for
|
|
assert_equal "A", ActiveSupport::Dependencies.qualified_name_for(Object, :A)
|
|
assert_equal "A", ActiveSupport::Dependencies.qualified_name_for(:Object, :A)
|
|
assert_equal "A", ActiveSupport::Dependencies.qualified_name_for("Object", :A)
|
|
assert_equal "A", ActiveSupport::Dependencies.qualified_name_for("::Object", :A)
|
|
|
|
assert_equal "ActiveSupport::Dependencies::A", ActiveSupport::Dependencies.qualified_name_for(:'ActiveSupport::Dependencies', :A)
|
|
assert_equal "ActiveSupport::Dependencies::A", ActiveSupport::Dependencies.qualified_name_for(ActiveSupport::Dependencies, :A)
|
|
end
|
|
|
|
def test_file_search
|
|
with_loading 'dependencies' do
|
|
root = ActiveSupport::Dependencies.autoload_paths.first
|
|
assert_equal nil, ActiveSupport::Dependencies.search_for_file('service_three')
|
|
assert_equal nil, ActiveSupport::Dependencies.search_for_file('service_three.rb')
|
|
assert_equal root + '/service_one.rb', ActiveSupport::Dependencies.search_for_file('service_one')
|
|
assert_equal root + '/service_one.rb', ActiveSupport::Dependencies.search_for_file('service_one.rb')
|
|
end
|
|
end
|
|
|
|
def test_file_search_uses_first_in_load_path
|
|
with_loading 'dependencies', 'autoloading_fixtures' do
|
|
deps, autoload = ActiveSupport::Dependencies.autoload_paths
|
|
assert_match %r/dependencies/, deps
|
|
assert_match %r/autoloading_fixtures/, autoload
|
|
|
|
assert_equal deps + '/conflict.rb', ActiveSupport::Dependencies.search_for_file('conflict')
|
|
end
|
|
with_loading 'autoloading_fixtures', 'dependencies' do
|
|
autoload, deps = ActiveSupport::Dependencies.autoload_paths
|
|
assert_match %r/dependencies/, deps
|
|
assert_match %r/autoloading_fixtures/, autoload
|
|
|
|
assert_equal autoload + '/conflict.rb', ActiveSupport::Dependencies.search_for_file('conflict')
|
|
end
|
|
|
|
end
|
|
|
|
def test_custom_const_missing_should_work
|
|
Object.module_eval <<-end_eval, __FILE__, __LINE__ + 1
|
|
module ModuleWithCustomConstMissing
|
|
def self.const_missing(name)
|
|
const_set name, name.to_s.hash
|
|
end
|
|
|
|
module A
|
|
end
|
|
end
|
|
end_eval
|
|
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Integer, ::ModuleWithCustomConstMissing::B
|
|
assert_kind_of Module, ::ModuleWithCustomConstMissing::A
|
|
assert_kind_of String, ::ModuleWithCustomConstMissing::A::B
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleWithCustomConstMissing)
|
|
end
|
|
|
|
def test_const_missing_in_anonymous_modules_loads_top_level_constants
|
|
with_autoloading_fixtures do
|
|
# class_eval STRING pushes the class to the nesting of the eval'ed code.
|
|
klass = Class.new.class_eval "EM"
|
|
assert_equal EM, klass
|
|
end
|
|
ensure
|
|
remove_constants(:EM)
|
|
end
|
|
|
|
def test_const_missing_in_anonymous_modules_raises_if_the_constant_belongs_to_Object
|
|
with_autoloading_fixtures do
|
|
require_dependency 'em'
|
|
|
|
mod = Module.new
|
|
e = assert_raise(NameError) { mod::EM }
|
|
assert_equal 'EM cannot be autoloaded from an anonymous class or module', e.message
|
|
assert_equal :EM, e.name
|
|
end
|
|
ensure
|
|
remove_constants(:EM)
|
|
end
|
|
|
|
def test_removal_from_tree_should_be_detected
|
|
with_loading 'dependencies' do
|
|
c = ServiceOne
|
|
ActiveSupport::Dependencies.clear
|
|
assert_not defined?(ServiceOne)
|
|
e = assert_raise ArgumentError do
|
|
ActiveSupport::Dependencies.load_missing_constant(c, :FakeMissing)
|
|
end
|
|
assert_match %r{ServiceOne has been removed from the module tree}i, e.message
|
|
end
|
|
ensure
|
|
remove_constants(:ServiceOne)
|
|
end
|
|
|
|
def test_references_should_work
|
|
with_loading 'dependencies' do
|
|
c = ActiveSupport::Dependencies.reference("ServiceOne")
|
|
service_one_first = ServiceOne
|
|
assert_equal service_one_first, c.get("ServiceOne")
|
|
ActiveSupport::Dependencies.clear
|
|
assert_not defined?(ServiceOne)
|
|
service_one_second = ServiceOne
|
|
assert_not_equal service_one_first, c.get("ServiceOne")
|
|
assert_equal service_one_second, c.get("ServiceOne")
|
|
end
|
|
ensure
|
|
remove_constants(:ServiceOne)
|
|
end
|
|
|
|
def test_constantize_shortcut_for_cached_constant_lookups
|
|
with_loading 'dependencies' do
|
|
assert_equal ServiceOne, ActiveSupport::Dependencies.constantize("ServiceOne")
|
|
end
|
|
ensure
|
|
remove_constants(:ServiceOne)
|
|
end
|
|
|
|
def test_nested_load_error_isnt_rescued
|
|
with_loading 'dependencies' do
|
|
assert_raise(LoadError) do
|
|
RequiresNonexistent1
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_autoload_once_paths_do_not_add_to_autoloaded_constants
|
|
old_path = ActiveSupport::Dependencies.autoload_once_paths
|
|
with_autoloading_fixtures do
|
|
ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths.dup
|
|
|
|
assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
|
|
assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
|
|
assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder)
|
|
|
|
1 if ModuleFolder::NestedClass # 1 if to avoid warning
|
|
assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass)
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleFolder)
|
|
ActiveSupport::Dependencies.autoload_once_paths = old_path
|
|
end
|
|
|
|
def test_autoload_once_pathnames_do_not_add_to_autoloaded_constants
|
|
with_autoloading_fixtures do
|
|
pathnames = ActiveSupport::Dependencies.autoload_paths.collect{|p| Pathname.new(p)}
|
|
ActiveSupport::Dependencies.autoload_paths = pathnames
|
|
ActiveSupport::Dependencies.autoload_once_paths = pathnames
|
|
|
|
assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder")
|
|
assert_not ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass")
|
|
assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder)
|
|
|
|
1 if ModuleFolder::NestedClass # 1 if to avoid warning
|
|
assert_not ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass)
|
|
end
|
|
ensure
|
|
remove_constants(:ModuleFolder)
|
|
ActiveSupport::Dependencies.autoload_once_paths = []
|
|
end
|
|
|
|
def test_application_should_special_case_application_controller
|
|
with_autoloading_fixtures do
|
|
require_dependency 'application'
|
|
assert_equal 10, ApplicationController
|
|
assert ActiveSupport::Dependencies.autoloaded?(:ApplicationController)
|
|
end
|
|
ensure
|
|
remove_constants(:ApplicationController)
|
|
end
|
|
|
|
def test_preexisting_constants_are_not_marked_as_autoloaded
|
|
with_autoloading_fixtures do
|
|
require_dependency 'em'
|
|
assert ActiveSupport::Dependencies.autoloaded?(:EM)
|
|
ActiveSupport::Dependencies.clear
|
|
end
|
|
|
|
Object.const_set :EM, Class.new
|
|
with_autoloading_fixtures do
|
|
require_dependency 'em'
|
|
assert ! ActiveSupport::Dependencies.autoloaded?(:EM), "EM shouldn't be marked autoloaded!"
|
|
ActiveSupport::Dependencies.clear
|
|
end
|
|
ensure
|
|
remove_constants(:EM)
|
|
end
|
|
|
|
def test_constants_in_capitalized_nesting_marked_as_autoloaded
|
|
with_autoloading_fixtures do
|
|
ActiveSupport::Dependencies.load_missing_constant(HTML, "SomeClass")
|
|
|
|
assert ActiveSupport::Dependencies.autoloaded?("HTML::SomeClass")
|
|
end
|
|
ensure
|
|
remove_constants(:HTML)
|
|
end
|
|
|
|
def test_unloadable
|
|
with_autoloading_fixtures do
|
|
Object.const_set :M, Module.new
|
|
M.unloadable
|
|
|
|
ActiveSupport::Dependencies.clear
|
|
assert ! defined?(M)
|
|
|
|
Object.const_set :M, Module.new
|
|
ActiveSupport::Dependencies.clear
|
|
assert ! defined?(M), "Dependencies should unload unloadable constants each time"
|
|
end
|
|
end
|
|
|
|
def test_unloadable_should_fail_with_anonymous_modules
|
|
with_autoloading_fixtures do
|
|
m = Module.new
|
|
assert_raise(ArgumentError) { m.unloadable }
|
|
end
|
|
end
|
|
|
|
def test_unloadable_should_return_change_flag
|
|
with_autoloading_fixtures do
|
|
Object.const_set :M, Module.new
|
|
assert_equal true, M.unloadable
|
|
assert_equal false, M.unloadable
|
|
end
|
|
ensure
|
|
remove_constants(:M)
|
|
end
|
|
|
|
def test_unloadable_constants_should_receive_callback
|
|
Object.const_set :C, Class.new { def self.before_remove_const; end }
|
|
C.unloadable
|
|
assert_called(C, :before_remove_const, times: 1) do
|
|
assert C.respond_to?(:before_remove_const)
|
|
ActiveSupport::Dependencies.clear
|
|
assert !defined?(C)
|
|
end
|
|
ensure
|
|
remove_constants(:C)
|
|
end
|
|
|
|
def test_new_contants_in_without_constants
|
|
assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) { })
|
|
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
|
|
end
|
|
|
|
def test_new_constants_in_with_a_single_constant
|
|
assert_equal ["Hello"], ActiveSupport::Dependencies.new_constants_in(Object) {
|
|
Object.const_set :Hello, 10
|
|
}.map(&:to_s)
|
|
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
|
|
ensure
|
|
remove_constants(:Hello)
|
|
end
|
|
|
|
def test_new_constants_in_with_nesting
|
|
outer = ActiveSupport::Dependencies.new_constants_in(Object) do
|
|
Object.const_set :OuterBefore, 10
|
|
|
|
assert_equal ["Inner"], ActiveSupport::Dependencies.new_constants_in(Object) {
|
|
Object.const_set :Inner, 20
|
|
}.map(&:to_s)
|
|
|
|
Object.const_set :OuterAfter, 30
|
|
end
|
|
|
|
assert_equal ["OuterAfter", "OuterBefore"], outer.sort.map(&:to_s)
|
|
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
|
|
ensure
|
|
remove_constants(:OuterBefore, :Inner, :OuterAfter)
|
|
end
|
|
|
|
def test_new_constants_in_module
|
|
Object.const_set :M, Module.new
|
|
|
|
outer = ActiveSupport::Dependencies.new_constants_in(M) do
|
|
M.const_set :OuterBefore, 10
|
|
|
|
inner = ActiveSupport::Dependencies.new_constants_in(M) do
|
|
M.const_set :Inner, 20
|
|
end
|
|
assert_equal ["M::Inner"], inner
|
|
|
|
M.const_set :OuterAfter, 30
|
|
end
|
|
assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort
|
|
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
|
|
ensure
|
|
remove_constants(:M)
|
|
end
|
|
|
|
def test_new_constants_in_module_using_name
|
|
outer = ActiveSupport::Dependencies.new_constants_in(:M) do
|
|
Object.const_set :M, Module.new
|
|
M.const_set :OuterBefore, 10
|
|
|
|
inner = ActiveSupport::Dependencies.new_constants_in(:M) do
|
|
M.const_set :Inner, 20
|
|
end
|
|
assert_equal ["M::Inner"], inner
|
|
|
|
M.const_set :OuterAfter, 30
|
|
end
|
|
assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort
|
|
assert ActiveSupport::Dependencies.constant_watch_stack.all? {|k,v| v.empty? }
|
|
ensure
|
|
remove_constants(:M)
|
|
end
|
|
|
|
def test_new_constants_in_with_inherited_constants
|
|
m = ActiveSupport::Dependencies.new_constants_in(:Object) do
|
|
Object.class_eval { include ModuleWithConstant }
|
|
end
|
|
assert_equal [], m
|
|
end
|
|
|
|
def test_new_constants_in_with_illegal_module_name_raises_correct_error
|
|
assert_raise(NameError) do
|
|
ActiveSupport::Dependencies.new_constants_in("Illegal-Name") {}
|
|
end
|
|
end
|
|
|
|
def test_file_with_multiple_constants_and_require_dependency
|
|
with_autoloading_fixtures do
|
|
assert_not defined?(MultipleConstantFile)
|
|
assert_not defined?(SiblingConstant)
|
|
|
|
require_dependency 'multiple_constant_file'
|
|
assert defined?(MultipleConstantFile)
|
|
assert defined?(SiblingConstant)
|
|
assert ActiveSupport::Dependencies.autoloaded?(:MultipleConstantFile)
|
|
assert ActiveSupport::Dependencies.autoloaded?(:SiblingConstant)
|
|
ActiveSupport::Dependencies.clear
|
|
|
|
assert_not defined?(MultipleConstantFile)
|
|
assert_not defined?(SiblingConstant)
|
|
end
|
|
ensure
|
|
remove_constants(:MultipleConstantFile, :SiblingConstant)
|
|
end
|
|
|
|
def test_file_with_multiple_constants_and_auto_loading
|
|
with_autoloading_fixtures do
|
|
assert_not defined?(MultipleConstantFile)
|
|
assert_not defined?(SiblingConstant)
|
|
|
|
assert_equal 10, MultipleConstantFile
|
|
|
|
assert defined?(MultipleConstantFile)
|
|
assert defined?(SiblingConstant)
|
|
assert ActiveSupport::Dependencies.autoloaded?(:MultipleConstantFile)
|
|
assert ActiveSupport::Dependencies.autoloaded?(:SiblingConstant)
|
|
|
|
ActiveSupport::Dependencies.clear
|
|
|
|
assert_not defined?(MultipleConstantFile)
|
|
assert_not defined?(SiblingConstant)
|
|
end
|
|
ensure
|
|
remove_constants(:MultipleConstantFile, :SiblingConstant)
|
|
end
|
|
|
|
def test_nested_file_with_multiple_constants_and_require_dependency
|
|
with_autoloading_fixtures do
|
|
assert_not defined?(ClassFolder::NestedClass)
|
|
assert_not defined?(ClassFolder::SiblingClass)
|
|
|
|
require_dependency 'class_folder/nested_class'
|
|
|
|
assert defined?(ClassFolder::NestedClass)
|
|
assert defined?(ClassFolder::SiblingClass)
|
|
assert ActiveSupport::Dependencies.autoloaded?("ClassFolder::NestedClass")
|
|
assert ActiveSupport::Dependencies.autoloaded?("ClassFolder::SiblingClass")
|
|
|
|
ActiveSupport::Dependencies.clear
|
|
|
|
assert_not defined?(ClassFolder::NestedClass)
|
|
assert_not defined?(ClassFolder::SiblingClass)
|
|
end
|
|
ensure
|
|
remove_constants(:ClassFolder)
|
|
end
|
|
|
|
def test_nested_file_with_multiple_constants_and_auto_loading
|
|
with_autoloading_fixtures do
|
|
assert_not defined?(ClassFolder::NestedClass)
|
|
assert_not defined?(ClassFolder::SiblingClass)
|
|
|
|
assert_kind_of Class, ClassFolder::NestedClass
|
|
|
|
assert defined?(ClassFolder::NestedClass)
|
|
assert defined?(ClassFolder::SiblingClass)
|
|
assert ActiveSupport::Dependencies.autoloaded?("ClassFolder::NestedClass")
|
|
assert ActiveSupport::Dependencies.autoloaded?("ClassFolder::SiblingClass")
|
|
|
|
ActiveSupport::Dependencies.clear
|
|
|
|
assert_not defined?(ClassFolder::NestedClass)
|
|
assert_not defined?(ClassFolder::SiblingClass)
|
|
end
|
|
ensure
|
|
remove_constants(:ClassFolder)
|
|
end
|
|
|
|
def test_autoload_doesnt_shadow_no_method_error_with_relative_constant
|
|
with_autoloading_fixtures do
|
|
assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it hasn't been referenced yet!"
|
|
2.times do
|
|
assert_raise(NoMethodError) { RaisesNoMethodError }
|
|
assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!"
|
|
end
|
|
end
|
|
ensure
|
|
remove_constants(:RaisesNoMethodError)
|
|
end
|
|
|
|
def test_autoload_doesnt_shadow_no_method_error_with_absolute_constant
|
|
with_autoloading_fixtures do
|
|
assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it hasn't been referenced yet!"
|
|
2.times do
|
|
assert_raise(NoMethodError) { ::RaisesNoMethodError }
|
|
assert !defined?(::RaisesNoMethodError), "::RaisesNoMethodError is defined but it should have failed!"
|
|
end
|
|
end
|
|
ensure
|
|
remove_constants(:RaisesNoMethodError)
|
|
end
|
|
|
|
def test_autoload_doesnt_shadow_error_when_mechanism_not_set_to_load
|
|
with_autoloading_fixtures do
|
|
ActiveSupport::Dependencies.mechanism = :require
|
|
2.times do
|
|
assert_raise(NameError) { assert_equal 123, ::RaisesNameError::FooBarBaz }
|
|
end
|
|
end
|
|
ensure
|
|
remove_constants(:RaisesNameError)
|
|
end
|
|
|
|
def test_autoload_doesnt_shadow_name_error
|
|
with_autoloading_fixtures do
|
|
2.times do
|
|
e = assert_raise NameError do
|
|
::RaisesNameError::FooBarBaz.object_id
|
|
end
|
|
assert_equal 'uninitialized constant RaisesNameError::FooBarBaz', e.message
|
|
assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
|
|
end
|
|
|
|
assert !defined?(::RaisesNameError)
|
|
2.times do
|
|
assert_raise(NameError) { ::RaisesNameError }
|
|
assert !defined?(::RaisesNameError), "::RaisesNameError is defined but it should have failed!"
|
|
end
|
|
end
|
|
ensure
|
|
remove_constants(:RaisesNameError)
|
|
end
|
|
|
|
def test_remove_constant_handles_double_colon_at_start
|
|
Object.const_set 'DeleteMe', Module.new
|
|
DeleteMe.const_set 'OrMe', Module.new
|
|
ActiveSupport::Dependencies.remove_constant "::DeleteMe::OrMe"
|
|
assert_not defined?(DeleteMe::OrMe)
|
|
assert defined?(DeleteMe)
|
|
ActiveSupport::Dependencies.remove_constant "::DeleteMe"
|
|
assert_not defined?(DeleteMe)
|
|
ensure
|
|
remove_constants(: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
|
|
|
|
assert_nil ActiveSupport::Dependencies.remove_constant(constant), "Kernel#autoload has been triggered by remove_constant"
|
|
assert_not defined?(ShouldNotBeAutoloaded)
|
|
ensure
|
|
remove_constants(constant)
|
|
end
|
|
|
|
def test_remove_constant_does_not_autoload_already_removed_parents_as_a_side_effect
|
|
with_autoloading_fixtures do
|
|
_ = ::A # assignment to silence parse-time warning "possibly useless use of :: in void context"
|
|
_ = ::A::B # assignment to silence parse-time warning "possibly useless use of :: in void context"
|
|
ActiveSupport::Dependencies.remove_constant('A')
|
|
ActiveSupport::Dependencies.remove_constant('A::B')
|
|
assert_not defined?(A)
|
|
end
|
|
ensure
|
|
remove_constants(:A)
|
|
end
|
|
|
|
def test_load_once_constants_should_not_be_unloaded
|
|
old_path = ActiveSupport::Dependencies.autoload_once_paths
|
|
with_autoloading_fixtures do
|
|
ActiveSupport::Dependencies.autoload_once_paths = ActiveSupport::Dependencies.autoload_paths
|
|
_ = ::A # assignment to silence parse-time warning "possibly useless use of :: in void context"
|
|
assert defined?(A)
|
|
ActiveSupport::Dependencies.clear
|
|
assert defined?(A)
|
|
end
|
|
ensure
|
|
ActiveSupport::Dependencies.autoload_once_paths = old_path
|
|
remove_constants(:A)
|
|
end
|
|
|
|
def test_access_unloaded_constants_for_reload
|
|
with_autoloading_fixtures do
|
|
assert_kind_of Module, A
|
|
assert_kind_of Class, A::B # Necessary to load A::B for the test
|
|
ActiveSupport::Dependencies.mark_for_unload(A::B)
|
|
ActiveSupport::Dependencies.remove_unloadable_constants!
|
|
|
|
A::B # Make sure no circular dependency error
|
|
end
|
|
ensure
|
|
remove_constants(:A)
|
|
end
|
|
|
|
|
|
def test_autoload_once_paths_should_behave_when_recursively_loading
|
|
old_path = ActiveSupport::Dependencies.autoload_once_paths
|
|
with_loading 'dependencies', 'autoloading_fixtures' do
|
|
ActiveSupport::Dependencies.autoload_once_paths = [ActiveSupport::Dependencies.autoload_paths.last]
|
|
assert_not defined?(CrossSiteDependency)
|
|
assert_nothing_raised { CrossSiteDepender.nil? }
|
|
assert defined?(CrossSiteDependency)
|
|
assert_not ActiveSupport::Dependencies.autoloaded?(CrossSiteDependency),
|
|
"CrossSiteDependency shouldn't be marked as autoloaded!"
|
|
ActiveSupport::Dependencies.clear
|
|
assert defined?(CrossSiteDependency),
|
|
"CrossSiteDependency shouldn't have been unloaded!"
|
|
end
|
|
ensure
|
|
ActiveSupport::Dependencies.autoload_once_paths = old_path
|
|
remove_constants(:CrossSiteDependency)
|
|
end
|
|
|
|
def test_hook_called_multiple_times
|
|
assert_nothing_raised { ActiveSupport::Dependencies.hook! }
|
|
end
|
|
|
|
def test_load_and_require_stay_private
|
|
assert Object.private_methods.include?(:load)
|
|
assert Object.private_methods.include?(:require)
|
|
|
|
ActiveSupport::Dependencies.unhook!
|
|
|
|
assert Object.private_methods.include?(:load)
|
|
assert Object.private_methods.include?(:require)
|
|
ensure
|
|
ActiveSupport::Dependencies.hook!
|
|
end
|
|
end
|