summaryrefslogtreecommitdiff
path: root/spec/ruby/core/module/autoload_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/module/autoload_spec.rb')
-rw-r--r--spec/ruby/core/module/autoload_spec.rb424
1 files changed, 312 insertions, 112 deletions
diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb
index 224d5fd57b..057237a92f 100644
--- a/spec/ruby/core/module/autoload_spec.rb
+++ b/spec/ruby/core/module/autoload_spec.rb
@@ -1,6 +1,6 @@
require_relative '../../spec_helper'
+require_relative '../../fixtures/code_loading'
require_relative 'fixtures/classes'
-require 'thread'
describe "Module#autoload?" do
it "returns the name of the file that will be autoloaded" do
@@ -9,24 +9,43 @@ describe "Module#autoload?" do
end
it "returns nil if no file has been registered for a constant" do
- ModuleSpecs::Autoload.autoload?(:Manualload).should be_nil
+ ModuleSpecs::Autoload.autoload?(:Manualload).should == nil
+ end
+
+ it "returns the name of the file that will be autoloaded if an ancestor defined that autoload" do
+ ModuleSpecs::Autoload::Parent.autoload :AnotherAutoload, "another_autoload.rb"
+ ModuleSpecs::Autoload::Child.autoload?(:AnotherAutoload).should == "another_autoload.rb"
+ end
+
+ it "returns nil if an ancestor defined that autoload but recursion is disabled" do
+ ModuleSpecs::Autoload::Parent.autoload :InheritedAutoload, "inherited_autoload.rb"
+ ModuleSpecs::Autoload::Child.autoload?(:InheritedAutoload, false).should == nil
+ end
+
+ it "returns the name of the file that will be loaded if recursion is disabled but the autoload is defined on the class itself" do
+ ModuleSpecs::Autoload::Child.autoload :ChildAutoload, "child_autoload.rb"
+ ModuleSpecs::Autoload::Child.autoload?(:ChildAutoload, false).should == "child_autoload.rb"
end
end
describe "Module#autoload" do
before :all do
@non_existent = fixture __FILE__, "no_autoload.rb"
+ CodeLoadingSpecs.preload_rubygems
end
before :each do
@loaded_features = $".dup
- @frozen_module = Module.new.freeze
ScratchPad.clear
+ @remove = []
end
after :each do
$".replace @loaded_features
+ @remove.each { |const|
+ ModuleSpecs::Autoload.send :remove_const, const
+ }
end
it "registers a file to load the first time the named constant is accessed" do
@@ -36,21 +55,34 @@ describe "Module#autoload" do
it "sets the autoload constant in the constants table" do
ModuleSpecs::Autoload.autoload :B, @non_existent
- ModuleSpecs::Autoload.should have_constant(:B)
+ ModuleSpecs::Autoload.should.const_defined?(:B, false)
+ end
+
+ it "can be overridden with a second autoload on the same constant" do
+ ModuleSpecs::Autoload.autoload :Overridden, @non_existent
+ @remove << :Overridden
+ ModuleSpecs::Autoload.autoload?(:Overridden).should == @non_existent
+
+ path = fixture(__FILE__, "autoload_overridden.rb")
+ ModuleSpecs::Autoload.autoload :Overridden, path
+ ModuleSpecs::Autoload.autoload?(:Overridden).should == path
+
+ ModuleSpecs::Autoload::Overridden.should == :overridden
end
it "loads the registered constant when it is accessed" do
- ModuleSpecs::Autoload.should_not have_constant(:X)
+ ModuleSpecs::Autoload.should_not.const_defined?(:X)
ModuleSpecs::Autoload.autoload :X, fixture(__FILE__, "autoload_x.rb")
+ @remove << :X
ModuleSpecs::Autoload::X.should == :x
- ModuleSpecs::Autoload.send(:remove_const, :X)
end
it "loads the registered constant into a dynamically created class" do
cls = Class.new { autoload :C, fixture(__FILE__, "autoload_c.rb") }
ModuleSpecs::Autoload::DynClass = cls
+ @remove << :DynClass
- ScratchPad.recorded.should be_nil
+ ScratchPad.recorded.should == nil
ModuleSpecs::Autoload::DynClass::C.new.loaded.should == :dynclass_c
ScratchPad.recorded.should == :loaded
end
@@ -58,8 +90,9 @@ describe "Module#autoload" do
it "loads the registered constant into a dynamically created module" do
mod = Module.new { autoload :D, fixture(__FILE__, "autoload_d.rb") }
ModuleSpecs::Autoload::DynModule = mod
+ @remove << :DynModule
- ScratchPad.recorded.should be_nil
+ ScratchPad.recorded.should == nil
ModuleSpecs::Autoload::DynModule::D.new.loaded.should == :dynmodule_d
ScratchPad.recorded.should == :loaded
end
@@ -95,9 +128,10 @@ describe "Module#autoload" do
it "does not load the file when the constant is already set" do
ModuleSpecs::Autoload.autoload :I, fixture(__FILE__, "autoload_i.rb")
+ @remove << :I
ModuleSpecs::Autoload.const_set :I, 3
ModuleSpecs::Autoload::I.should == 3
- ScratchPad.recorded.should be_nil
+ ScratchPad.recorded.should == nil
end
it "loads a file with .rb extension when passed the name without the extension" do
@@ -110,20 +144,21 @@ describe "Module#autoload" do
main = TOPLEVEL_BINDING.eval("self")
main.should_receive(:require).with("module_autoload_not_exist.rb")
# The constant won't be defined since require is mocked to do nothing
- -> { ModuleSpecs::Autoload::ModuleAutoloadCallsRequire }.should raise_error(NameError)
+ -> { ModuleSpecs::Autoload::ModuleAutoloadCallsRequire }.should.raise(NameError)
end
it "does not load the file if the file is manually required" do
filename = fixture(__FILE__, "autoload_k.rb")
ModuleSpecs::Autoload.autoload :KHash, filename
+ @remove << :KHash
require filename
ScratchPad.recorded.should == :loaded
ScratchPad.clear
- ModuleSpecs::Autoload::KHash.should be_kind_of(Class)
+ ModuleSpecs::Autoload::KHash.should.is_a?(Class)
ModuleSpecs::Autoload::KHash::K.should == :autoload_k
- ScratchPad.recorded.should be_nil
+ ScratchPad.recorded.should == nil
end
it "ignores the autoload request if the file is already loaded" do
@@ -135,8 +170,8 @@ describe "Module#autoload" do
ScratchPad.clear
ModuleSpecs::Autoload.autoload :S, filename
- ModuleSpecs::Autoload.autoload?(:S).should be_nil
- ModuleSpecs::Autoload.send(:remove_const, :S)
+ @remove << :S
+ ModuleSpecs::Autoload.autoload?(:S).should == nil
end
it "retains the autoload even if the request to require fails" do
@@ -145,9 +180,9 @@ describe "Module#autoload" do
ModuleSpecs::Autoload.autoload :NotThere, filename
ModuleSpecs::Autoload.autoload?(:NotThere).should == filename
- lambda {
+ -> {
require filename
- }.should raise_error(LoadError)
+ }.should.raise(LoadError)
ModuleSpecs::Autoload.autoload?(:NotThere).should == filename
end
@@ -166,6 +201,13 @@ describe "Module#autoload" do
ModuleSpecs::Autoload.use_ex1.should == :good
end
+ it "considers an autoload constant as loaded when autoload is called for/from the current file" do
+ filename = fixture(__FILE__, "autoload_during_require_current_file.rb")
+ require filename
+
+ ScratchPad.recorded.should == nil
+ end
+
describe "interacting with defined?" do
it "does not load the file when referring to the constant in defined?" do
module ModuleSpecs::Autoload::Dog
@@ -173,20 +215,19 @@ describe "Module#autoload" do
end
defined?(ModuleSpecs::Autoload::Dog::R).should == "constant"
- ScratchPad.recorded.should be_nil
+ ScratchPad.recorded.should == nil
- ModuleSpecs::Autoload::Dog.should have_constant(:R)
+ ModuleSpecs::Autoload::Dog.should.const_defined?(:R, false)
end
it "loads an autoloaded parent when referencing a nested constant" do
module ModuleSpecs::Autoload
autoload :GoodParent, fixture(__FILE__, "autoload_nested.rb")
end
+ @remove << :GoodParent
defined?(ModuleSpecs::Autoload::GoodParent::Nested).should == 'constant'
ScratchPad.recorded.should == :loaded
-
- ModuleSpecs::Autoload.send(:remove_const, :GoodParent)
end
it "returns nil when it fails to load an autoloaded parent when referencing a nested constant" do
@@ -194,16 +235,17 @@ describe "Module#autoload" do
autoload :BadParent, fixture(__FILE__, "autoload_exception.rb")
end
- defined?(ModuleSpecs::Autoload::BadParent::Nested).should be_nil
+ defined?(ModuleSpecs::Autoload::BadParent::Nested).should == nil
ScratchPad.recorded.should == :exception
end
end
- describe "the autoload is removed when the same file is required directly without autoload" do
+ describe "the autoload is triggered when the same file is required directly" do
before :each do
module ModuleSpecs::Autoload
autoload :RequiredDirectly, fixture(__FILE__, "autoload_required_directly.rb")
end
+ @remove << :RequiredDirectly
@path = fixture(__FILE__, "autoload_required_directly.rb")
@check = -> {
[
@@ -214,10 +256,6 @@ describe "Module#autoload" do
ScratchPad.record @check
end
- after :each do
- ModuleSpecs::Autoload.send(:remove_const, :RequiredDirectly)
- end
-
it "with a full path" do
@check.call.should == ["constant", @path]
require @path
@@ -242,7 +280,7 @@ describe "Module#autoload" do
nested_require = -> {
result = nil
ScratchPad.record -> {
- result = [@check.call, Thread.new { @check.call }.value]
+ result = @check.call
}
require nested
result
@@ -251,90 +289,181 @@ describe "Module#autoload" do
@check.call.should == ["constant", @path]
require @path
- cur, other = ScratchPad.recorded
- cur.should == [nil, nil]
- other.should == [nil, nil]
+ ScratchPad.recorded.should == [nil, nil]
@check.call.should == ["constant", nil]
end
+
+ it "does not raise an error if the autoload constant was not defined" do
+ module ModuleSpecs::Autoload
+ autoload :RequiredDirectlyNoConstant, fixture(__FILE__, "autoload_required_directly_no_constant.rb")
+ end
+ @path = fixture(__FILE__, "autoload_required_directly_no_constant.rb")
+ @remove << :RequiredDirectlyNoConstant
+ @check = -> {
+ [
+ defined?(ModuleSpecs::Autoload::RequiredDirectlyNoConstant),
+ ModuleSpecs::Autoload.constants(false).include?(:RequiredDirectlyNoConstant),
+ ModuleSpecs::Autoload.const_defined?(:RequiredDirectlyNoConstant),
+ ModuleSpecs::Autoload.autoload?(:RequiredDirectlyNoConstant)
+ ]
+ }
+ ScratchPad.record @check
+ @check.call.should == ["constant", true, true, @path]
+ $:.push File.dirname(@path)
+ begin
+ require "autoload_required_directly_no_constant.rb"
+ ensure
+ $:.pop
+ end
+ ScratchPad.recorded.should == [nil, true, false, nil]
+ @check.call.should == [nil, true, false, nil]
+ end
end
- describe "during the autoload before the constant is assigned" do
+ describe "after the autoload is triggered by require" do
before :each do
- @path = fixture(__FILE__, "autoload_during_autoload.rb")
- ModuleSpecs::Autoload.autoload :DuringAutoload, @path
- raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoload) == @path
+ @path = tmp("autoload.rb")
end
after :each do
- ModuleSpecs::Autoload.send(:remove_const, :DuringAutoload)
+ rm_r @path
end
- def check_before_during_thread_after(&check)
- before = check.call
- to_autoload_thread, from_autoload_thread = Queue.new, Queue.new
- ScratchPad.record -> {
- from_autoload_thread.push check.call
- to_autoload_thread.pop
- }
- t = Thread.new {
- in_loading_thread = from_autoload_thread.pop
- in_other_thread = check.call
- to_autoload_thread.push :done
- [in_loading_thread, in_other_thread]
- }
- in_loading_thread, in_other_thread = nil
- begin
- ModuleSpecs::Autoload::DuringAutoload
- ensure
- in_loading_thread, in_other_thread = t.value
- end
- after = check.call
- [before, in_loading_thread, in_other_thread, after]
+ it "the mapping feature to autoload is removed, and a new autoload with the same path is considered" do
+ ModuleSpecs::Autoload.autoload :RequireMapping1, @path
+ touch(@path) { |f| f.puts "ModuleSpecs::Autoload::RequireMapping1 = 1" }
+ ModuleSpecs::Autoload::RequireMapping1.should == 1
+
+ $LOADED_FEATURES.delete(@path)
+ ModuleSpecs::Autoload.autoload :RequireMapping2, @path[0...-3]
+ @remove << :RequireMapping2
+ touch(@path) { |f| f.puts "ModuleSpecs::Autoload::RequireMapping2 = 2" }
+ ModuleSpecs::Autoload::RequireMapping2.should == 2
+ end
+ end
+
+ def check_before_during_thread_after(const, &check)
+ before = check.call
+ to_autoload_thread, from_autoload_thread = Queue.new, Queue.new
+ ScratchPad.record -> {
+ from_autoload_thread.push check.call
+ to_autoload_thread.pop
+ }
+ t = Thread.new {
+ in_loading_thread = from_autoload_thread.pop
+ in_other_thread = check.call
+ to_autoload_thread.push :done
+ [in_loading_thread, in_other_thread]
+ }
+ in_loading_thread, in_other_thread = nil
+ begin
+ ModuleSpecs::Autoload.const_get(const)
+ ensure
+ in_loading_thread, in_other_thread = t.value
+ end
+ after = check.call
+ [before, in_loading_thread, in_other_thread, after]
+ end
+
+ describe "during the autoload before the constant is assigned" do
+ before :each do
+ @path = fixture(__FILE__, "autoload_during_autoload.rb")
+ ModuleSpecs::Autoload.autoload :DuringAutoload, @path
+ @remove << :DuringAutoload
+ raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoload) == @path
end
it "returns nil in autoload thread and 'constant' otherwise for defined?" do
- results = check_before_during_thread_after {
+ results = check_before_during_thread_after(:DuringAutoload) {
defined?(ModuleSpecs::Autoload::DuringAutoload)
}
results.should == ['constant', nil, 'constant', 'constant']
end
it "keeps the constant in Module#constants" do
- results = check_before_during_thread_after {
+ results = check_before_during_thread_after(:DuringAutoload) {
ModuleSpecs::Autoload.constants(false).include?(:DuringAutoload)
}
results.should == [true, true, true, true]
end
it "returns false in autoload thread and true otherwise for Module#const_defined?" do
- results = check_before_during_thread_after {
+ results = check_before_during_thread_after(:DuringAutoload) {
ModuleSpecs::Autoload.const_defined?(:DuringAutoload, false)
}
results.should == [true, false, true, true]
end
it "returns nil in autoload thread and returns the path in other threads for Module#autoload?" do
- results = check_before_during_thread_after {
+ results = check_before_during_thread_after(:DuringAutoload) {
ModuleSpecs::Autoload.autoload?(:DuringAutoload)
}
results.should == [@path, nil, @path, nil]
end
end
+ describe "during the autoload after the constant is assigned" do
+ before :each do
+ @path = fixture(__FILE__, "autoload_during_autoload_after_define.rb")
+ ModuleSpecs::Autoload.autoload :DuringAutoloadAfterDefine, @path
+ @autoload_location = [__FILE__, __LINE__ - 1]
+ @const_location = [@path, 2]
+ @remove << :DuringAutoloadAfterDefine
+ raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoloadAfterDefine) == @path
+ end
+
+ it "returns 'constant' in both threads" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ defined?(ModuleSpecs::Autoload::DuringAutoloadAfterDefine)
+ }
+ results.should == ['constant', 'constant', 'constant', 'constant']
+ end
+
+ it "Module#constants include the autoloaded in both threads" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ ModuleSpecs::Autoload.constants(false).include?(:DuringAutoloadAfterDefine)
+ }
+ results.should == [true, true, true, true]
+ end
+
+ it "Module#const_defined? returns true in both threads" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ ModuleSpecs::Autoload.const_defined?(:DuringAutoloadAfterDefine, false)
+ }
+ results.should == [true, true, true, true]
+ end
+
+ it "returns nil in autoload thread and returns the path in other threads for Module#autoload?" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ ModuleSpecs::Autoload.autoload?(:DuringAutoloadAfterDefine)
+ }
+ results.should == [@path, nil, @path, nil]
+ end
+
+ ruby_bug("#20188", ""..."3.4") do
+ it "returns the real constant location in autoload thread and returns the autoload location in other threads for Module#const_source_location" do
+ results = check_before_during_thread_after(:DuringAutoloadAfterDefine) {
+ ModuleSpecs::Autoload.const_source_location(:DuringAutoloadAfterDefine)
+ }
+ results.should == [@autoload_location, @const_location, @autoload_location, @const_location]
+ end
+ end
+ end
+
it "does not remove the constant from Module#constants if load fails and keeps it as an autoload" do
ModuleSpecs::Autoload.autoload :Fail, @non_existent
ModuleSpecs::Autoload.const_defined?(:Fail).should == true
- ModuleSpecs::Autoload.should have_constant(:Fail)
+ ModuleSpecs::Autoload.should.const_defined?(:Fail, false)
ModuleSpecs::Autoload.autoload?(:Fail).should == @non_existent
- lambda { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
+ -> { ModuleSpecs::Autoload::Fail }.should.raise(LoadError)
- ModuleSpecs::Autoload.should have_constant(:Fail)
+ ModuleSpecs::Autoload.should.const_defined?(:Fail, false)
ModuleSpecs::Autoload.const_defined?(:Fail).should == true
ModuleSpecs::Autoload.autoload?(:Fail).should == @non_existent
- lambda { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
+ -> { ModuleSpecs::Autoload::Fail }.should.raise(LoadError)
end
it "does not remove the constant from Module#constants if load raises a RuntimeError and keeps it as an autoload" do
@@ -343,35 +472,35 @@ describe "Module#autoload" do
ModuleSpecs::Autoload.autoload :Raise, path
ModuleSpecs::Autoload.const_defined?(:Raise).should == true
- ModuleSpecs::Autoload.should have_constant(:Raise)
+ ModuleSpecs::Autoload.should.const_defined?(:Raise, false)
ModuleSpecs::Autoload.autoload?(:Raise).should == path
- lambda { ModuleSpecs::Autoload::Raise }.should raise_error(RuntimeError)
+ -> { ModuleSpecs::Autoload::Raise }.should.raise(RuntimeError)
ScratchPad.recorded.should == [:raise]
- ModuleSpecs::Autoload.should have_constant(:Raise)
+ ModuleSpecs::Autoload.should.const_defined?(:Raise, false)
ModuleSpecs::Autoload.const_defined?(:Raise).should == true
ModuleSpecs::Autoload.autoload?(:Raise).should == path
- lambda { ModuleSpecs::Autoload::Raise }.should raise_error(RuntimeError)
+ -> { ModuleSpecs::Autoload::Raise }.should.raise(RuntimeError)
ScratchPad.recorded.should == [:raise, :raise]
end
- it "does not remove the constant from Module#constants if the loaded file does not define it, but leaves it as 'undefined'" do
+ it "removes the constant from Module#constants if the loaded file does not define it" do
path = fixture(__FILE__, "autoload_o.rb")
ScratchPad.record []
ModuleSpecs::Autoload.autoload :O, path
ModuleSpecs::Autoload.const_defined?(:O).should == true
- ModuleSpecs::Autoload.should have_constant(:O)
+ ModuleSpecs::Autoload.should.const_defined?(:O, false)
ModuleSpecs::Autoload.autoload?(:O).should == path
- lambda { ModuleSpecs::Autoload::O }.should raise_error(NameError)
+ -> { ModuleSpecs::Autoload::O }.should.raise(NameError)
- ModuleSpecs::Autoload.should have_constant(:O)
ModuleSpecs::Autoload.const_defined?(:O).should == false
+ ModuleSpecs::Autoload.should_not.const_defined?(:O)
ModuleSpecs::Autoload.autoload?(:O).should == nil
- -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError)
+ -> { ModuleSpecs::Autoload.const_get(:O) }.should.raise(NameError)
end
it "does not try to load the file again if the loaded file did not define the constant" do
@@ -379,13 +508,13 @@ describe "Module#autoload" do
ScratchPad.record []
ModuleSpecs::Autoload.autoload :NotDefinedByFile, path
- -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should.raise(NameError)
ScratchPad.recorded.should == [:loaded]
- -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should.raise(NameError)
ScratchPad.recorded.should == [:loaded]
Thread.new {
- -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should.raise(NameError)
}.join
ScratchPad.recorded.should == [:loaded]
end
@@ -395,7 +524,7 @@ describe "Module#autoload" do
autoload :R, fixture(__FILE__, "autoload.rb")
defined?(R).should == 'constant'
end
- ModuleSpecs::Autoload::Q.should have_constant(:R)
+ ModuleSpecs::Autoload::Q.should.const_defined?(:R, false)
end
it "does not load the file when removing an autoload constant" do
@@ -403,12 +532,13 @@ describe "Module#autoload" do
autoload :R, fixture(__FILE__, "autoload.rb")
remove_const :R
end
- ModuleSpecs::Autoload::Q.should_not have_constant(:R)
+ ModuleSpecs::Autoload::Q.should_not.const_defined?(:R)
end
it "does not load the file when accessing the constants table of the module" do
ModuleSpecs::Autoload.autoload :P, @non_existent
- ModuleSpecs::Autoload.const_defined?(:P).should be_true
+ ModuleSpecs::Autoload.const_defined?(:P).should == true
+ ModuleSpecs::Autoload.const_defined?("P").should == true
end
it "loads the file when opening a module that is the autoloaded constant" do
@@ -419,6 +549,7 @@ describe "Module#autoload" do
X = get_value
end
end
+ @remove << :U
ModuleSpecs::Autoload::U::V::X.should == :autoload_uvx
end
@@ -442,8 +573,8 @@ describe "Module#autoload" do
DeclaredAndDefinedInParent.should == :declared_and_defined_in_parent
# The constant is really in Autoload, not Autoload::LexicalScope
- self.should_not have_constant(:DeclaredAndDefinedInParent)
- -> { const_get(:DeclaredAndDefinedInParent) }.should raise_error(NameError)
+ self.should_not.const_defined?(:DeclaredAndDefinedInParent)
+ -> { const_get(:DeclaredAndDefinedInParent) }.should.raise(NameError)
end
DeclaredAndDefinedInParent.should == :declared_and_defined_in_parent
end
@@ -466,14 +597,42 @@ describe "Module#autoload" do
# Basically, the parent autoload constant remains in a "undefined" state
self.autoload?(:DeclaredInParentDefinedInCurrent).should == nil
const_defined?(:DeclaredInParentDefinedInCurrent).should == false
- self.should have_constant(:DeclaredInParentDefinedInCurrent)
- -> { DeclaredInParentDefinedInCurrent }.should raise_error(NameError)
+ -> { DeclaredInParentDefinedInCurrent }.should.raise(NameError)
ModuleSpecs::Autoload::LexicalScope.send(:remove_const, :DeclaredInParentDefinedInCurrent)
end
end
- it "and fails when finding the undefined autoload constant in the the current scope when declared in current and defined in parent" do
+ it "warns once in verbose mode if the constant was defined in a parent scope" do
+ ScratchPad.record -> {
+ ModuleSpecs::DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent
+ }
+
+ module ModuleSpecs
+ module Autoload
+ autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
+ self.autoload?(:DeclaredInCurrentDefinedInParent).should == fixture(__FILE__, "autoload_callback.rb")
+ const_defined?(:DeclaredInCurrentDefinedInParent).should == true
+
+ -> {
+ DeclaredInCurrentDefinedInParent
+ }.should complain(
+ /Expected .*autoload_callback.rb to define ModuleSpecs::Autoload::DeclaredInCurrentDefinedInParent but it didn't/,
+ verbose: true,
+ )
+
+ -> {
+ DeclaredInCurrentDefinedInParent
+ }.should_not complain(/.*/, verbose: true)
+ self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil
+ const_defined?(:DeclaredInCurrentDefinedInParent).should == false
+ ModuleSpecs.const_defined?(:DeclaredInCurrentDefinedInParent).should == true
+ end
+ end
+ end
+
+ it "looks up in parent scope after failed autoload" do
+ @remove << :DeclaredInCurrentDefinedInParent
module ModuleSpecs::Autoload
ScratchPad.record -> {
DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent
@@ -481,12 +640,11 @@ describe "Module#autoload" do
class LexicalScope
autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
- -> { DeclaredInCurrentDefinedInParent }.should raise_error(NameError)
+ -> { DeclaredInCurrentDefinedInParent }.should_not.raise(NameError)
# Basically, the autoload constant remains in a "undefined" state
self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil
const_defined?(:DeclaredInCurrentDefinedInParent).should == false
- self.should have_constant(:DeclaredInCurrentDefinedInParent)
- -> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError)
+ -> { const_get(:DeclaredInCurrentDefinedInParent) }.should.raise(NameError)
end
DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent
@@ -494,6 +652,7 @@ describe "Module#autoload" do
end
it "in the included modules" do
+ @remove << :DefinedInIncludedModule
module ModuleSpecs::Autoload
ScratchPad.record -> {
module DefinedInIncludedModule
@@ -507,6 +666,7 @@ describe "Module#autoload" do
end
it "in the included modules of the superclass" do
+ @remove << :DefinedInSuperclassIncludedModule
module ModuleSpecs::Autoload
class LookupAfterAutoloadSuper
end
@@ -528,6 +688,7 @@ describe "Module#autoload" do
end
it "in the prepended modules" do
+ @remove << :DefinedInPrependedModule
module ModuleSpecs::Autoload
ScratchPad.record -> {
module DefinedInPrependedModule
@@ -553,8 +714,23 @@ describe "Module#autoload" do
end
end
end
- ModuleSpecs::Autoload.r.should be_kind_of(ModuleSpecs::Autoload::MetaScope)
+ ModuleSpecs::Autoload.r.should.is_a?(ModuleSpecs::Autoload::MetaScope)
+ end
+ end
+
+ it "should trigger the autoload when using `private_constant`" do
+ @remove << :DynClass
+ module ModuleSpecs::Autoload
+ autoload :DynClass, fixture(__FILE__, "autoload_c.rb")
+ private_constant :DynClass
+
+ ScratchPad.recorded.should == nil
+
+ DynClass::C.new.loaded.should == :dynclass_c
+ ScratchPad.recorded.should == :loaded
end
+
+ -> { ModuleSpecs::Autoload::DynClass }.should.raise(NameError, /private constant/)
end
# [ruby-core:19127] [ruby-core:29941]
@@ -567,10 +743,10 @@ describe "Module#autoload" do
end
end
end
+ @remove << :W
- ModuleSpecs::Autoload::W::Y.should be_kind_of(Class)
+ ModuleSpecs::Autoload::W::Y.should.is_a?(Class)
ScratchPad.recorded.should == :loaded
- ModuleSpecs::Autoload::W.send(:remove_const, :Y)
end
it "does not call #require a second time and does not warn if already loading the same feature with #require" do
@@ -584,7 +760,28 @@ describe "Module#autoload" do
-> {
Kernel.require fixture(__FILE__, "autoload_during_require.rb")
}.should_not complain(verbose: true)
- ModuleSpecs::Autoload::AutoloadDuringRequire.should be_kind_of(Class)
+ ModuleSpecs::Autoload::AutoloadDuringRequire.should.is_a?(Class)
+ end
+
+ it "does not call #require a second time and does not warn if feature sets and trigger autoload on itself" do
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_not_receive(:require)
+
+ -> {
+ Kernel.require fixture(__FILE__, "autoload_self_during_require.rb")
+ }.should_not complain(verbose: true)
+ ModuleSpecs::Autoload::AutoloadSelfDuringRequire.should.is_a?(Class)
+ end
+
+ it "handles multiple autoloads in the same file" do
+ $LOAD_PATH.unshift(File.expand_path('../fixtures/multi', __FILE__))
+ begin
+ require 'foo/bar_baz'
+ ModuleSpecs::Autoload::Foo::Bar.should.is_a?(Class)
+ ModuleSpecs::Autoload::Foo::Baz.should.is_a?(Class)
+ ensure
+ $LOAD_PATH.shift
+ end
end
it "calls #to_path on non-string filenames" do
@@ -594,26 +791,27 @@ describe "Module#autoload" do
end
it "raises an ArgumentError when an empty filename is given" do
- lambda { ModuleSpecs.autoload :A, "" }.should raise_error(ArgumentError)
+ -> { ModuleSpecs.autoload :A, "" }.should.raise(ArgumentError)
end
it "raises a NameError when the constant name starts with a lower case letter" do
- lambda { ModuleSpecs.autoload "a", @non_existent }.should raise_error(NameError)
+ -> { ModuleSpecs.autoload "a", @non_existent }.should.raise(NameError)
end
it "raises a NameError when the constant name starts with a number" do
- lambda { ModuleSpecs.autoload "1two", @non_existent }.should raise_error(NameError)
+ -> { ModuleSpecs.autoload "1two", @non_existent }.should.raise(NameError)
end
it "raises a NameError when the constant name has a space in it" do
- lambda { ModuleSpecs.autoload "a name", @non_existent }.should raise_error(NameError)
+ -> { ModuleSpecs.autoload "a name", @non_existent }.should.raise(NameError)
end
it "shares the autoload request across dup'ed copies of modules" do
require fixture(__FILE__, "autoload_s.rb")
+ @remove << :S
filename = fixture(__FILE__, "autoload_t.rb")
mod1 = Module.new { autoload :T, filename }
- lambda {
+ -> {
ModuleSpecs::Autoload::S = mod1
}.should complain(/already initialized constant/)
mod2 = mod1.dup
@@ -622,7 +820,7 @@ describe "Module#autoload" do
mod2.autoload?(:T).should == filename
mod1::T.should == :autoload_t
- lambda { mod2::T }.should raise_error(NameError)
+ -> { mod2::T }.should.raise(NameError)
end
it "raises a TypeError if opening a class with a different superclass than the class defined in the autoload file" do
@@ -630,29 +828,30 @@ describe "Module#autoload" do
class ModuleSpecs::Autoload::ZZ
end
- lambda do
+ -> do
class ModuleSpecs::Autoload::Z < ModuleSpecs::Autoload::ZZ
end
- end.should raise_error(TypeError)
+ end.should.raise(TypeError)
end
it "raises a TypeError if not passed a String or object responding to #to_path for the filename" do
name = mock("autoload_name.rb")
- lambda { ModuleSpecs::Autoload.autoload :Str, name }.should raise_error(TypeError)
+ -> { ModuleSpecs::Autoload.autoload :Str, name }.should.raise(TypeError)
end
it "calls #to_path on non-String filename arguments" do
name = mock("autoload_name.rb")
name.should_receive(:to_path).and_return("autoload_name.rb")
- lambda { ModuleSpecs::Autoload.autoload :Str, name }.should_not raise_error
+ -> { ModuleSpecs::Autoload.autoload :Str, name }.should_not.raise
end
describe "on a frozen module" do
- it "raises a #{frozen_error_class} before setting the name" do
- lambda { @frozen_module.autoload :Foo, @non_existent }.should raise_error(frozen_error_class)
- @frozen_module.should_not have_constant(:Foo)
+ it "raises a FrozenError before setting the name" do
+ frozen_module = Module.new.freeze
+ -> { frozen_module.autoload :Foo, @non_existent }.should.raise(FrozenError)
+ frozen_module.should_not.const_defined?(:Foo)
end
end
@@ -675,6 +874,7 @@ describe "Module#autoload" do
describe "(concurrently)" do
it "blocks a second thread while a first is doing the autoload" do
ModuleSpecs::Autoload.autoload :Concur, fixture(__FILE__, "autoload_concur.rb")
+ @remove << :Concur
start = false
@@ -716,9 +916,7 @@ describe "Module#autoload" do
t1_val.should == 1
t2_val.should == t1_val
- t2_exc.should be_nil
-
- ModuleSpecs::Autoload.send(:remove_const, :Concur)
+ t2_exc.should == nil
end
# https://bugs.ruby-lang.org/issues/10892
@@ -750,7 +948,7 @@ describe "Module#autoload" do
begin
Object.const_get(mod_name).foo
- rescue NoMethodError
+ rescue NameError, NoMethodError # rubocop:disable Lint/ShadowedException
barrier.disable!
break false
end
@@ -758,8 +956,8 @@ describe "Module#autoload" do
end
end
- # check that no thread got a NoMethodError because of partially loaded module
- threads.all? {|t| t.value}.should be_true
+ # check that no thread got a NameError or NoMethodError because of partially loaded module
+ threads.all? {|t| t.value}.should == true
# check that the autoloaded file was evaled exactly once
ScratchPad.recorded.get.should == mod_count
@@ -767,6 +965,8 @@ describe "Module#autoload" do
mod_names.each do |mod_name|
Object.send(:remove_const, mod_name)
end
+ ensure
+ threads.each(&:join) if threads
end
it "raises a NameError in each thread if the constant is not set" do
@@ -790,7 +990,7 @@ describe "Module#autoload" do
start = true
threads.each { |t|
- t.value.should be_an_instance_of(NameError)
+ t.value.should.instance_of?(NameError)
}
end
@@ -815,7 +1015,7 @@ describe "Module#autoload" do
start = true
threads.each { |t|
- t.value.should be_an_instance_of(LoadError)
+ t.value.should.instance_of?(LoadError)
}
end
end