diff options
Diffstat (limited to 'spec/ruby/core/module')
39 files changed, 1009 insertions, 860 deletions
diff --git a/spec/ruby/core/module/ancestors_spec.rb b/spec/ruby/core/module/ancestors_spec.rb index 5e4c196206..90c26941d1 100644 --- a/spec/ruby/core/module/ancestors_spec.rb +++ b/spec/ruby/core/module/ancestors_spec.rb @@ -7,10 +7,17 @@ describe "Module#ancestors" do ModuleSpecs.ancestors.should == [ModuleSpecs] ModuleSpecs::Basic.ancestors.should == [ModuleSpecs::Basic] ModuleSpecs::Super.ancestors.should == [ModuleSpecs::Super, ModuleSpecs::Basic] - ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should == - [ModuleSpecs::Parent, Object, Kernel, BasicObject] - ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should == - [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, Kernel, BasicObject] + if defined?(Ruby::Box) && Ruby::Box.enabled? + ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should == + [ModuleSpecs::Parent, Object, Ruby::Box::Loader, Kernel, BasicObject] + ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should == + [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, Ruby::Box::Loader, Kernel, BasicObject] + else + ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should == + [ModuleSpecs::Parent, Object, Kernel, BasicObject] + ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should == + [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, Kernel, BasicObject] + end end it "returns only modules and classes" do @@ -21,6 +28,17 @@ describe "Module#ancestors" do ModuleSpecs::Parent.ancestors.should == ModuleSpecs::Parent.ancestors.uniq end + it "returns a module that is included later into a nested module as well" do + m1 = Module.new + m2 = Module.new + m3 = Module.new do + include m2 + end + m2.include m1 # should be after m3 includes m2 + + m3.ancestors.should == [m3, m2, m1] + end + describe "when called on a singleton class" do it "includes the singleton classes of ancestors" do parent = Class.new diff --git a/spec/ruby/core/module/attr_accessor_spec.rb b/spec/ruby/core/module/attr_accessor_spec.rb index eea5b4b64b..503dccc61e 100644 --- a/spec/ruby/core/module/attr_accessor_spec.rb +++ b/spec/ruby/core/module/attr_accessor_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/attr_added' describe "Module#attr_accessor" do it "creates a getter and setter for each given attribute name" do @@ -106,4 +107,6 @@ describe "Module#attr_accessor" do 1.foobar.should be_nil end end + + it_behaves_like :module_attr_added, :attr_accessor end diff --git a/spec/ruby/core/module/attr_reader_spec.rb b/spec/ruby/core/module/attr_reader_spec.rb index 0b6d996719..37fd537ff5 100644 --- a/spec/ruby/core/module/attr_reader_spec.rb +++ b/spec/ruby/core/module/attr_reader_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/attr_added' describe "Module#attr_reader" do it "creates a getter for each given attribute name" do @@ -67,4 +68,6 @@ describe "Module#attr_reader" do (attr_reader :foo, 'bar').should == [:foo, :bar] end end + + it_behaves_like :module_attr_added, :attr_reader end diff --git a/spec/ruby/core/module/attr_spec.rb b/spec/ruby/core/module/attr_spec.rb index 72a6646b1e..2f9f4e26dc 100644 --- a/spec/ruby/core/module/attr_spec.rb +++ b/spec/ruby/core/module/attr_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/attr_added' describe "Module#attr" do before :each do @@ -153,4 +154,6 @@ describe "Module#attr" do (attr :qux, true).should == [:qux, :qux=] end end + + it_behaves_like :module_attr_added, :attr end diff --git a/spec/ruby/core/module/attr_writer_spec.rb b/spec/ruby/core/module/attr_writer_spec.rb index aaea0ce43f..5b863ef88c 100644 --- a/spec/ruby/core/module/attr_writer_spec.rb +++ b/spec/ruby/core/module/attr_writer_spec.rb @@ -1,5 +1,6 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'shared/attr_added' describe "Module#attr_writer" do it "creates a setter for each given attribute name" do @@ -77,4 +78,6 @@ describe "Module#attr_writer" do (attr_writer :foo, 'bar').should == [:foo=, :bar=] end end + + it_behaves_like :module_attr_added, :attr_writer end diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb index af04ab26c8..625d945686 100644 --- a/spec/ruby/core/module/autoload_spec.rb +++ b/spec/ruby/core/module/autoload_spec.rb @@ -1,7 +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 @@ -343,6 +342,29 @@ describe "Module#autoload" do 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") @@ -351,58 +373,83 @@ describe "Module#autoload" do raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoload) == @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] - 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 @@ -439,42 +486,21 @@ describe "Module#autoload" do ScratchPad.recorded.should == [:raise, :raise] end - ruby_version_is "3.1" 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.autoload?(:O).should == path - - -> { ModuleSpecs::Autoload::O }.should raise_error(NameError) - - ModuleSpecs::Autoload.const_defined?(:O).should == false - ModuleSpecs::Autoload.should_not have_constant(:O) - ModuleSpecs::Autoload.autoload?(:O).should == nil - -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError) - end - end - - ruby_version_is ""..."3.1" do - it "does not remove the constant from Module#constants if the loaded file does not define it, but leaves it as 'undefined'" do - path = fixture(__FILE__, "autoload_o.rb") - ScratchPad.record [] - ModuleSpecs::Autoload.autoload :O, path + 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.autoload?(:O).should == path + ModuleSpecs::Autoload.const_defined?(:O).should == true + ModuleSpecs::Autoload.should have_constant(:O) + ModuleSpecs::Autoload.autoload?(:O).should == path - -> { ModuleSpecs::Autoload::O }.should raise_error(NameError) + -> { ModuleSpecs::Autoload::O }.should raise_error(NameError) - ModuleSpecs::Autoload.const_defined?(:O).should == false - ModuleSpecs::Autoload.should have_constant(:O) - ModuleSpecs::Autoload.autoload?(:O).should == nil - -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError) - end + ModuleSpecs::Autoload.const_defined?(:O).should == false + ModuleSpecs::Autoload.should_not have_constant(:O) + ModuleSpecs::Autoload.autoload?(:O).should == nil + -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError) end it "does not try to load the file again if the loaded file did not define the constant" do @@ -577,78 +603,51 @@ describe "Module#autoload" do end end - ruby_version_is "3.2" 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 - } + 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 + 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 - ruby_version_is "3.1" do - it "looks up in parent scope after failed autoload" do - @remove << :DeclaredInCurrentDefinedInParent - module ModuleSpecs::Autoload - ScratchPad.record -> { - DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent - } - - class LexicalScope - autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb") - -> { DeclaredInCurrentDefinedInParent }.should_not raise_error(NameError) - # Basically, the autoload constant remains in a "undefined" state - self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil - const_defined?(:DeclaredInCurrentDefinedInParent).should == false - -> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError) - 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 + } - DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent + class LexicalScope + autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb") + -> { DeclaredInCurrentDefinedInParent }.should_not raise_error(NameError) + # Basically, the autoload constant remains in a "undefined" state + self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil + const_defined?(:DeclaredInCurrentDefinedInParent).should == false + -> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError) end - end - end - - ruby_version_is ""..."3.1" do - it "and fails when finding the undefined autoload constant in the current scope when declared in current and defined in parent" do - @remove << :DeclaredInCurrentDefinedInParent - module ModuleSpecs::Autoload - ScratchPad.record -> { - DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent - } - class LexicalScope - autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb") - -> { DeclaredInCurrentDefinedInParent }.should raise_error(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) - end - - DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent - end + DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent end end @@ -719,6 +718,21 @@ describe "Module#autoload" do 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 be_nil + + DynClass::C.new.loaded.should == :dynclass_c + ScratchPad.recorded.should == :loaded + end + + -> { ModuleSpecs::Autoload::DynClass }.should raise_error(NameError, /private constant/) + end + # [ruby-core:19127] [ruby-core:29941] it "does NOT raise a NameError when the autoload file did not define the constant and a module is opened with the same name" do module ModuleSpecs::Autoload diff --git a/spec/ruby/core/module/const_added_spec.rb b/spec/ruby/core/module/const_added_spec.rb index 31ac6eb105..90cd36551a 100644 --- a/spec/ruby/core/module/const_added_spec.rb +++ b/spec/ruby/core/module/const_added_spec.rb @@ -1,125 +1,238 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' +require_relative 'fixtures/const_added' describe "Module#const_added" do - ruby_version_is "3.2" do - it "is a private instance method" do - Module.should have_private_instance_method(:const_added) + it "is a private instance method" do + Module.should have_private_instance_method(:const_added) + end + + it "returns nil in the default implementation" do + Module.new do + const_added(:TEST).should == nil + end + end + + it "for a class defined with the `class` keyword, const_added runs before inherited" do + ScratchPad.record [] + + mod = Module.new do + def self.const_added(_) + ScratchPad << :const_added + end end - it "returns nil in the default implementation" do - Module.new do - const_added(:TEST).should == nil + parent = Class.new do + def self.inherited(_) + ScratchPad << :inherited end end - it "is called when a new constant is assigned on self" do - ScratchPad.record [] + class mod::C < parent; end - mod = Module.new do - def self.const_added(name) - ScratchPad << name - end + ScratchPad.recorded.should == [:const_added, :inherited] + end + + it "the superclass of a class assigned to a constant is set before const_added is called" do + ScratchPad.record [] + + parent = Class.new do + def self.const_added(name) + ScratchPad << name + ScratchPad << const_get(name).superclass end + end - mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) - TEST = 1 - RUBY + class parent::C < parent; end + + ScratchPad.recorded.should == [:C, parent] + end - ScratchPad.recorded.should == [:TEST] + it "is called when a new constant is assigned on self" do + ScratchPad.record [] + + mod = Module.new do + def self.const_added(name) + ScratchPad << name + end end - it "is called when a new constant is assigned on self through const_set" do - ScratchPad.record [] + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + TEST = 1 + RUBY - mod = Module.new do - def self.const_added(name) - ScratchPad << name - end + ScratchPad.recorded.should == [:TEST] + end + + it "is called when a new constant is assigned on self through const_set" do + ScratchPad.record [] + + mod = Module.new do + def self.const_added(name) + ScratchPad << name end + end + + mod.const_set(:TEST, 1) + + ScratchPad.recorded.should == [:TEST] + end - mod.const_set(:TEST, 1) + it "is called when a new module is defined under self" do + ScratchPad.record [] - ScratchPad.recorded.should == [:TEST] + mod = Module.new do + def self.const_added(name) + ScratchPad << name + end end - it "is called when a new module is defined under self" do - ScratchPad.record [] + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + module SubModule + end + + module SubModule + end + RUBY - mod = Module.new do + ScratchPad.recorded.should == [:SubModule] + end + + it "is called when a new module is defined under a named module (assigned to a constant)" do + ScratchPad.record [] + + ModuleSpecs::ConstAddedSpecs::NamedModule = Module.new do + def self.const_added(name) + ScratchPad << name + end + + module self::A def self.const_added(name) ScratchPad << name end - end - mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) - module SubModule + module self::B end + end + end - module SubModule - end - RUBY + ScratchPad.recorded.should == [:A, :B] + ModuleSpecs::ConstAddedSpecs.send :remove_const, :NamedModule + end - ScratchPad.recorded.should == [:SubModule] + it "is called when a new class is defined under self" do + ScratchPad.record [] + + mod = Module.new do + def self.const_added(name) + ScratchPad << name + end end - it "is called when a new class is defined under self" do - ScratchPad.record [] + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + class SubClass + end + + class SubClass + end + RUBY + + ScratchPad.recorded.should == [:SubClass] + end + + it "is called when a new class is defined under a named module (assigned to a constant)" do + ScratchPad.record [] + + ModuleSpecs::ConstAddedSpecs::NamedModuleB = Module.new do + def self.const_added(name) + ScratchPad << name + end - mod = Module.new do + class self::A def self.const_added(name) ScratchPad << name end - end - mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) - class SubClass + class self::B end + end + end - class SubClass - end - RUBY + ScratchPad.recorded.should == [:A, :B] + ModuleSpecs::ConstAddedSpecs.send :remove_const, :NamedModuleB + end + + it "is called when an autoload is defined" do + ScratchPad.record [] - ScratchPad.recorded.should == [:SubClass] + mod = Module.new do + def self.const_added(name) + ScratchPad << name + end end - it "is called when an autoload is defined" do - ScratchPad.record [] + mod.autoload :Autoload, "foo" + ScratchPad.recorded.should == [:Autoload] + end - mod = Module.new do - def self.const_added(name) - ScratchPad << name - end - end + it "is called with a precise caller location with the line of definition" do + ScratchPad.record [] - mod.autoload :Autoload, "foo" - ScratchPad.recorded.should == [:Autoload] + mod = Module.new do + def self.const_added(name) + location = caller_locations(1, 1)[0] + ScratchPad << location.lineno + end end - it "is called with a precise caller location with the line of definition" do - ScratchPad.record [] + line = __LINE__ + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + TEST = 1 - mod = Module.new do - def self.const_added(name) - location = caller_locations(1, 1)[0] - ScratchPad << location.lineno - end + module SubModule end - line = __LINE__ - mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) - TEST = 1 + class SubClass + end + RUBY - module SubModule - end + mod.const_set(:CONST_SET, 1) - class SubClass - end - RUBY + ScratchPad.recorded.should == [line + 2, line + 4, line + 7, line + 11] + end + + it "is called when the constant is already assigned a value" do + ScratchPad.record [] - mod.const_set(:CONST_SET, 1) + mod = Module.new do + def self.const_added(name) + ScratchPad.record const_get(name) + end + end + + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + TEST = 123 + RUBY + + ScratchPad.recorded.should == 123 + end + + it "records re-definition of existing constants" do + ScratchPad.record [] - ScratchPad.recorded.should == [line + 2, line + 4, line + 7, line + 11] + mod = Module.new do + def self.const_added(name) + ScratchPad << const_get(name) + end end + + -> { + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + TEST = 123 + TEST = 456 + RUBY + }.should complain(/warning: already initialized constant .+::TEST/) + + ScratchPad.recorded.should == [123, 456] end end diff --git a/spec/ruby/core/module/const_defined_spec.rb b/spec/ruby/core/module/const_defined_spec.rb index 027cf5a245..8b137cd134 100644 --- a/spec/ruby/core/module/const_defined_spec.rb +++ b/spec/ruby/core/module/const_defined_spec.rb @@ -65,6 +65,8 @@ describe "Module#const_defined?" do str = "CS_CONSTλ".encode("euc-jp") ConstantSpecs.const_set str, 1 ConstantSpecs.const_defined?(str).should be_true + ensure + ConstantSpecs.send(:remove_const, str) end it "returns false if the constant is not defined in the receiver, its superclass, or any included modules" do diff --git a/spec/ruby/core/module/const_get_spec.rb b/spec/ruby/core/module/const_get_spec.rb index 0233118f4b..4b53cbe7b3 100644 --- a/spec/ruby/core/module/const_get_spec.rb +++ b/spec/ruby/core/module/const_get_spec.rb @@ -131,7 +131,7 @@ describe "Module#const_get" do end it "does read private constants" do - ConstantSpecs.const_get(:CS_PRIVATE).should == :cs_private + ConstantSpecs.const_get(:CS_PRIVATE).should == :cs_private end it 'does autoload a constant' do @@ -202,40 +202,60 @@ describe "Module#const_get" do ConstantSpecs::ContainerA::ChildA::CS_CONST301 = :const301_5 ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST301).should == :const301_5 + ensure + ConstantSpecs::ClassA.send(:remove_const, :CS_CONST301) + ConstantSpecs::ModuleA.send(:remove_const, :CS_CONST301) + ConstantSpecs::ParentA.send(:remove_const, :CS_CONST301) + ConstantSpecs::ContainerA::ChildA.send(:remove_const, :CS_CONST301) end it "searches a module included in the immediate class before the superclass" do ConstantSpecs::ParentB::CS_CONST302 = :const302_1 ConstantSpecs::ModuleF::CS_CONST302 = :const302_2 ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST302).should == :const302_2 + ensure + ConstantSpecs::ParentB.send(:remove_const, :CS_CONST302) + ConstantSpecs::ModuleF.send(:remove_const, :CS_CONST302) end it "searches the superclass before a module included in the superclass" do ConstantSpecs::ModuleE::CS_CONST303 = :const303_1 ConstantSpecs::ParentB::CS_CONST303 = :const303_2 ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST303).should == :const303_2 + ensure + ConstantSpecs::ModuleE.send(:remove_const, :CS_CONST303) + ConstantSpecs::ParentB.send(:remove_const, :CS_CONST303) end it "searches a module included in the superclass" do ConstantSpecs::ModuleA::CS_CONST304 = :const304_1 ConstantSpecs::ModuleE::CS_CONST304 = :const304_2 ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST304).should == :const304_2 + ensure + ConstantSpecs::ModuleA.send(:remove_const, :CS_CONST304) + ConstantSpecs::ModuleE.send(:remove_const, :CS_CONST304) end it "searches the superclass chain" do ConstantSpecs::ModuleA::CS_CONST305 = :const305 ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST305).should == :const305 + ensure + ConstantSpecs::ModuleA.send(:remove_const, :CS_CONST305) end it "returns a toplevel constant when the receiver is a Class" do Object::CS_CONST306 = :const306 ConstantSpecs::ContainerB::ChildB.const_get(:CS_CONST306).should == :const306 + ensure + Object.send(:remove_const, :CS_CONST306) end it "returns a toplevel constant when the receiver is a Module" do Object::CS_CONST308 = :const308 ConstantSpecs.const_get(:CS_CONST308).should == :const308 ConstantSpecs::ModuleA.const_get(:CS_CONST308).should == :const308 + ensure + Object.send(:remove_const, :CS_CONST308) end it "returns the updated value of a constant" do @@ -246,6 +266,8 @@ describe "Module#const_get" do ConstantSpecs::ClassB::CS_CONST309 = :const309_2 }.should complain(/already initialized constant/) ConstantSpecs::ClassB.const_get(:CS_CONST309).should == :const309_2 + ensure + ConstantSpecs::ClassB.send(:remove_const, :CS_CONST309) end end end diff --git a/spec/ruby/core/module/const_set_spec.rb b/spec/ruby/core/module/const_set_spec.rb index 5bdfd7b68f..823768b882 100644 --- a/spec/ruby/core/module/const_set_spec.rb +++ b/spec/ruby/core/module/const_set_spec.rb @@ -8,16 +8,23 @@ describe "Module#const_set" do ConstantSpecs.const_set "CS_CONST402", :const402 ConstantSpecs.const_get(:CS_CONST402).should == :const402 + ensure + ConstantSpecs.send(:remove_const, :CS_CONST401) + ConstantSpecs.send(:remove_const, :CS_CONST402) end it "returns the value set" do ConstantSpecs.const_set(:CS_CONST403, :const403).should == :const403 + ensure + ConstantSpecs.send(:remove_const, :CS_CONST403) end it "sets the name of an anonymous module" do m = Module.new ConstantSpecs.const_set(:CS_CONST1000, m) m.name.should == "ConstantSpecs::CS_CONST1000" + ensure + ConstantSpecs.send(:remove_const, :CS_CONST1000) end it "sets the name of a module scoped by an anonymous module" do @@ -38,6 +45,8 @@ describe "Module#const_set" do b.name.should == "ModuleSpecs_CS3::B" c.name.should == "ModuleSpecs_CS3::B::C" d.name.should == "ModuleSpecs_CS3::D" + ensure + Object.send(:remove_const, :ModuleSpecs_CS3) end it "raises a NameError if the name does not start with a capital letter" do @@ -55,6 +64,8 @@ describe "Module#const_set" do ConstantSpecs.const_set("CS_CONST404", :const404).should == :const404 -> { ConstantSpecs.const_set "Name=", 1 }.should raise_error(NameError) -> { ConstantSpecs.const_set "Name?", 1 }.should raise_error(NameError) + ensure + ConstantSpecs.send(:remove_const, :CS_CONST404) end it "calls #to_str to convert the given name to a String" do @@ -62,6 +73,8 @@ describe "Module#const_set" do name.should_receive(:to_str).and_return("CS_CONST405") ConstantSpecs.const_set(name, :const405).should == :const405 ConstantSpecs::CS_CONST405.should == :const405 + ensure + ConstantSpecs.send(:remove_const, :CS_CONST405) end it "raises a TypeError if conversion to a String by calling #to_str fails" do diff --git a/spec/ruby/core/module/const_source_location_spec.rb b/spec/ruby/core/module/const_source_location_spec.rb index ded2aa51d7..96649ea10b 100644 --- a/spec/ruby/core/module/const_source_location_spec.rb +++ b/spec/ruby/core/module/const_source_location_spec.rb @@ -19,40 +19,60 @@ describe "Module#const_source_location" do ConstantSpecs::ContainerA::ChildA::CSL_CONST301 = :const301_5 ConstantSpecs::ContainerA::ChildA.const_source_location(:CSL_CONST301).should == [__FILE__, __LINE__ - 1] + ensure + ConstantSpecs::ClassA.send(:remove_const, :CSL_CONST301) + ConstantSpecs::ModuleA.send(:remove_const, :CSL_CONST301) + ConstantSpecs::ParentA.send(:remove_const, :CSL_CONST301) + ConstantSpecs::ContainerA::ChildA.send(:remove_const, :CSL_CONST301) end it "searches a path in a module included in the immediate class before the superclass" do ConstantSpecs::ParentB::CSL_CONST302 = :const302_1 ConstantSpecs::ModuleF::CSL_CONST302 = :const302_2 ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST302).should == [__FILE__, __LINE__ - 1] + ensure + ConstantSpecs::ParentB.send(:remove_const, :CSL_CONST302) + ConstantSpecs::ModuleF.send(:remove_const, :CSL_CONST302) end it "searches a path in the superclass before a module included in the superclass" do ConstantSpecs::ModuleE::CSL_CONST303 = :const303_1 ConstantSpecs::ParentB::CSL_CONST303 = :const303_2 ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST303).should == [__FILE__, __LINE__ - 1] + ensure + ConstantSpecs::ModuleE.send(:remove_const, :CSL_CONST303) + ConstantSpecs::ParentB.send(:remove_const, :CSL_CONST303) end it "searches a path in a module included in the superclass" do ConstantSpecs::ModuleA::CSL_CONST304 = :const304_1 ConstantSpecs::ModuleE::CSL_CONST304 = :const304_2 ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST304).should == [__FILE__, __LINE__ - 1] + ensure + ConstantSpecs::ModuleA.send(:remove_const, :CSL_CONST304) + ConstantSpecs::ModuleE.send(:remove_const, :CSL_CONST304) end it "searches a path in the superclass chain" do ConstantSpecs::ModuleA::CSL_CONST305 = :const305 ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST305).should == [__FILE__, __LINE__ - 1] + ensure + ConstantSpecs::ModuleA.send(:remove_const, :CSL_CONST305) end it "returns path to a toplevel constant when the receiver is a Class" do Object::CSL_CONST306 = :const306 ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST306).should == [__FILE__, __LINE__ - 1] + ensure + Object.send(:remove_const, :CSL_CONST306) end it "returns path to a toplevel constant when the receiver is a Module" do Object::CSL_CONST308 = :const308 ConstantSpecs.const_source_location(:CSL_CONST308).should == [__FILE__, __LINE__ - 1] ConstantSpecs::ModuleA.const_source_location(:CSL_CONST308).should == [__FILE__, __LINE__ - 2] + ensure + Object.send(:remove_const, :CSL_CONST308) end it "returns path to the updated value of a constant" do @@ -63,6 +83,8 @@ describe "Module#const_source_location" do ConstantSpecs::ClassB::CSL_CONST309 = :const309_2 }.should complain(/already initialized constant/) ConstantSpecs::ClassB.const_source_location(:CSL_CONST309).should == [__FILE__, __LINE__ - 2] + ensure + ConstantSpecs::ClassB.send(:remove_const, :CSL_CONST309) end end @@ -207,7 +229,7 @@ describe "Module#const_source_location" do end it "does search private constants path" do - ConstantSpecs.const_source_location(:CS_PRIVATE).should == [@constants_fixture_path, ConstantSpecs::CS_PRIVATE_LINE] + ConstantSpecs.const_source_location(:CS_PRIVATE).should == [@constants_fixture_path, ConstantSpecs::CS_PRIVATE_LINE] end it "works for eval with a given line" do @@ -223,6 +245,14 @@ describe "Module#const_source_location" do @line = __LINE__ - 1 end + before :each do + @loaded_features = $".dup + end + + after :each do + $".replace @loaded_features + end + it 'returns the autoload location while not resolved' do ConstantSpecs.const_source_location('CSL_CONST1').should == [__FILE__, @line] end @@ -233,5 +263,19 @@ describe "Module#const_source_location" do line = ConstantSpecs::CONST_LOCATION ConstantSpecs.const_source_location('CONST_LOCATION').should == [file, line] end + + ruby_bug("#20188", ""..."3.4") do + it 'returns the real constant location as soon as it is defined' do + file = fixture(__FILE__, 'autoload_const_source_location.rb') + ConstantSpecs.autoload :ConstSource, file + autoload_location = [__FILE__, __LINE__ - 1] + + ConstantSpecs.const_source_location(:ConstSource).should == autoload_location + ConstantSpecs::ConstSource::LOCATION.should == ConstantSpecs.const_source_location(:ConstSource) + ConstantSpecs::BEFORE_DEFINE_LOCATION.should == autoload_location + ConstantSpecs.send :remove_const, :ConstSource + ConstantSpecs.send :remove_const, :BEFORE_DEFINE_LOCATION + end + end end end diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb index 381aacadb7..c5dfc53764 100644 --- a/spec/ruby/core/module/define_method_spec.rb +++ b/spec/ruby/core/module/define_method_spec.rb @@ -476,6 +476,9 @@ describe "Module#define_method" do ChildClass = Class.new(ParentClass) { define_method(:foo) { :baz } } ParentClass.send :define_method, :foo, ChildClass.instance_method(:foo) }.should raise_error(TypeError, /bind argument must be a subclass of ChildClass/) + ensure + Object.send(:remove_const, :ParentClass) + Object.send(:remove_const, :ChildClass) end it "raises a TypeError when an UnboundMethod from one class is defined on an unrelated class" do @@ -499,6 +502,33 @@ describe "Module#define_method" do Class.new { define_method :bar, m } }.should raise_error(TypeError, /can't bind singleton method to a different class/) end + + it "defines a new method with public visibility when a Method passed and the class/module of the context isn't equal to the receiver of #define_method" do + c = Class.new do + private def foo + "public" + end + end + + object = c.new + object.singleton_class.define_method(:bar, object.method(:foo)) + + object.bar.should == "public" + end + + it "defines the new method according to the scope visibility when a Method passed and the class/module of the context is equal to the receiver of #define_method" do + c = Class.new do + def foo; end + end + + object = c.new + object.singleton_class.class_eval do + private + define_method(:bar, c.new.method(:foo)) + end + + -> { object.bar }.should raise_error(NoMethodError) + end end describe "Module#define_method" do @@ -697,7 +727,7 @@ describe "Module#define_method" do end end -describe "Method#define_method when passed a Method object" do +describe "Module#define_method when passed a Method object" do before :each do @klass = Class.new do def m(a, b, *c) @@ -722,7 +752,7 @@ describe "Method#define_method when passed a Method object" do end end -describe "Method#define_method when passed an UnboundMethod object" do +describe "Module#define_method when passed an UnboundMethod object" do before :each do @klass = Class.new do def m(a, b, *c) @@ -747,7 +777,7 @@ describe "Method#define_method when passed an UnboundMethod object" do end end -describe "Method#define_method when passed a Proc object" do +describe "Module#define_method when passed a Proc object" do describe "and a method is defined inside" do it "defines the nested method in the default definee where the Proc was created" do prc = nil @@ -772,7 +802,7 @@ describe "Method#define_method when passed a Proc object" do end end -describe "Method#define_method when passed a block" do +describe "Module#define_method when passed a block" do describe "behaves exactly like a lambda" do it "for return" do Class.new do diff --git a/spec/ruby/core/module/deprecate_constant_spec.rb b/spec/ruby/core/module/deprecate_constant_spec.rb index aabef934c4..ec0de6782f 100644 --- a/spec/ruby/core/module/deprecate_constant_spec.rb +++ b/spec/ruby/core/module/deprecate_constant_spec.rb @@ -44,6 +44,15 @@ describe "Module#deprecate_constant" do end end + ruby_bug '#20900', ''...'3.4' do + describe "when removing the deprecated module" do + it "warns with a message" do + @module.deprecate_constant :PUBLIC1 + -> { @module.module_eval {remove_const :PUBLIC1} }.should complain(/warning: constant .+::PUBLIC1 is deprecated/) + end + end + end + it "accepts multiple symbols and strings as constant names" do @module.deprecate_constant "PUBLIC1", :PUBLIC2 diff --git a/spec/ruby/core/module/fixtures/autoload_const_source_location.rb b/spec/ruby/core/module/fixtures/autoload_const_source_location.rb new file mode 100644 index 0000000000..ee0e5a689f --- /dev/null +++ b/spec/ruby/core/module/fixtures/autoload_const_source_location.rb @@ -0,0 +1,6 @@ +module ConstantSpecs + BEFORE_DEFINE_LOCATION = const_source_location(:ConstSource) + module ConstSource + LOCATION = Object.const_source_location(name) + end +end diff --git a/spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb b/spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb new file mode 100644 index 0000000000..a9d886dfd6 --- /dev/null +++ b/spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb @@ -0,0 +1,6 @@ +module ModuleSpecs::Autoload + class DuringAutoloadAfterDefine + block = ScratchPad.recorded + ScratchPad.record(block.call) + end +end diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb index a434e7b0b8..964f64c593 100644 --- a/spec/ruby/core/module/fixtures/classes.rb +++ b/spec/ruby/core/module/fixtures/classes.rb @@ -1,6 +1,6 @@ module ModuleSpecs def self.without_test_modules(modules) - ignore = %w[MSpecRSpecAdapter PP::ObjectMixin ModuleSpecs::IncludedInObject MainSpecs::Module ConstantSpecs::ModuleA] + ignore = %w[MSpecRSpecAdapter PP::ObjectMixin MainSpecs::Module ConstantSpecs::ModuleA] modules.reject { |k| ignore.include?(k.name) } end diff --git a/spec/ruby/core/module/fixtures/const_added.rb b/spec/ruby/core/module/fixtures/const_added.rb new file mode 100644 index 0000000000..0f5baad65d --- /dev/null +++ b/spec/ruby/core/module/fixtures/const_added.rb @@ -0,0 +1,4 @@ +module ModuleSpecs + module ConstAddedSpecs + end +end diff --git a/spec/ruby/core/module/fixtures/name.rb b/spec/ruby/core/module/fixtures/name.rb index fb9e66c309..25c74d3944 100644 --- a/spec/ruby/core/module/fixtures/name.rb +++ b/spec/ruby/core/module/fixtures/name.rb @@ -7,4 +7,7 @@ module ModuleSpecs Cß.name end end + + module NameSpecs + end end diff --git a/spec/ruby/core/module/fixtures/set_temporary_name.rb b/spec/ruby/core/module/fixtures/set_temporary_name.rb new file mode 100644 index 0000000000..901b3b94d1 --- /dev/null +++ b/spec/ruby/core/module/fixtures/set_temporary_name.rb @@ -0,0 +1,4 @@ +module ModuleSpecs + module SetTemporaryNameSpec + end +end diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb index c073bc31ca..210918b2e7 100644 --- a/spec/ruby/core/module/include_spec.rb +++ b/spec/ruby/core/module/include_spec.rb @@ -44,7 +44,23 @@ describe "Module#include" do end it "does not raise a TypeError when the argument is an instance of a subclass of Module" do - -> { ModuleSpecs::SubclassSpec.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) + class ModuleSpecs::SubclassSpec::AClass + end + -> { ModuleSpecs::SubclassSpec::AClass.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) + ensure + ModuleSpecs::SubclassSpec.send(:remove_const, :AClass) + end + + it "raises a TypeError when the argument is a refinement" do + refinement = nil + + Module.new do + refine String do + refinement = self + end + end + + -> { ModuleSpecs::Basic.include(refinement) }.should raise_error(TypeError, "Cannot include refinement") end it "imports constants to modules and classes" do @@ -399,6 +415,8 @@ describe "Module#include" do M.const_set(:FOO, 'm') B.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdated) end it "updates the constant when a module included after a call is later updated" do @@ -425,6 +443,8 @@ describe "Module#include" do M.const_set(:FOO, 'm') B.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstLaterUpdated) end it "updates the constant when a module included in another module after a call is later updated" do @@ -451,6 +471,8 @@ describe "Module#include" do M.const_set(:FOO, 'm') B.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstModuleLaterUpdated) end it "updates the constant when a nested included module is updated" do @@ -479,6 +501,8 @@ describe "Module#include" do N.const_set(:FOO, 'n') B.foo.should == 'n' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatedNestedIncludeUpdated) end it "updates the constant when a new module is included" do @@ -503,6 +527,8 @@ describe "Module#include" do B.include(M) B.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatedNewInclude) end it "updates the constant when a new module with nested module is included" do @@ -531,6 +557,8 @@ describe "Module#include" do B.include M B.foo.should == 'n' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatedNestedIncluded) end it "overrides a previous super method call" do @@ -553,6 +581,29 @@ describe "Module#include" do c2.include(m) c2.new.foo.should == [:c2, :m1] end + + it "update a module when a nested module is updated and includes a module on its own" do + m1 = Module.new + m2 = Module.new do + def m2; [:m2]; end + end + m3 = Module.new do + def m3; [:m3]; end + end + m4 = Module.new do + def m4; [:m4]; end + end + c = Class.new + + c.include(m1) + m1.include(m2) + m2.include(m3) + m3.include(m4) + + c.new.m2.should == [:m2] + c.new.m3.should == [:m3] + c.new.m4.should == [:m4] + end end describe "Module#include?" do diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb index 8d006e647e..182cdf5c54 100644 --- a/spec/ruby/core/module/instance_method_spec.rb +++ b/spec/ruby/core/module/instance_method_spec.rb @@ -48,11 +48,6 @@ describe "Module#instance_method" do @mod_um.inspect.should =~ /\bbar\b/ @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethMod\b/ - - ruby_version_is ""..."3.2" do - @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/ - @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/ - end end it "raises a TypeError if the given name is not a String/Symbol" do diff --git a/spec/ruby/core/module/module_function_spec.rb b/spec/ruby/core/module/module_function_spec.rb index 1c3ec5471b..51f647142e 100644 --- a/spec/ruby/core/module/module_function_spec.rb +++ b/spec/ruby/core/module/module_function_spec.rb @@ -38,22 +38,11 @@ describe "Module#module_function with specific method names" do m.respond_to?(:test3).should == false end - ruby_version_is ""..."3.1" do - it "returns self" do - Module.new do - def foo; end - module_function(:foo).should equal(self) - end - end - end - - ruby_version_is "3.1" do - it "returns argument or arguments if given" do - Module.new do - def foo; end - module_function(:foo).should equal(:foo) - module_function(:foo, :foo).should == [:foo, :foo] - end + it "returns argument or arguments if given" do + Module.new do + def foo; end + module_function(:foo).should equal(:foo) + module_function(:foo, :foo).should == [:foo, :foo] end end @@ -216,19 +205,9 @@ describe "Module#module_function as a toggle (no arguments) in a Module body" do m.respond_to?(:test2).should == true end - ruby_version_is ""..."3.1" do - it "returns self" do - Module.new do - module_function.should equal(self) - end - end - end - - ruby_version_is "3.1" do - it "returns nil" do - Module.new do - module_function.should equal(nil) - end + it "returns nil" do + Module.new do + module_function.should equal(nil) end end diff --git a/spec/ruby/core/module/name_spec.rb b/spec/ruby/core/module/name_spec.rb index 0d1f4e24d5..d3318e1645 100644 --- a/spec/ruby/core/module/name_spec.rb +++ b/spec/ruby/core/module/name_spec.rb @@ -30,6 +30,8 @@ describe "Module#name" do m::N.name.should =~ /\A#<Module:0x\h+>::N\z/ ModuleSpecs::Anonymous::WasAnnon = m::N m::N.name.should == "ModuleSpecs::Anonymous::WasAnnon" + ensure + ModuleSpecs::Anonymous.send(:remove_const, :WasAnnon) end it "may be the repeated in different module objects" do @@ -76,12 +78,16 @@ describe "Module#name" do m = Module.new ModuleSpecs::Anonymous::A = m m.name.should == "ModuleSpecs::Anonymous::A" + ensure + ModuleSpecs::Anonymous.send(:remove_const, :A) end it "is set when assigning to a constant (constant path does not match outer module name)" do m = Module.new ModuleSpecs::Anonymous::SameChild::A = m m.name.should == "ModuleSpecs::Anonymous::Child::A" + ensure + ModuleSpecs::Anonymous::SameChild.send(:remove_const, :A) end it "is not modified when assigning to a new constant after it has been accessed" do @@ -90,6 +96,9 @@ describe "Module#name" do m.name.should == "ModuleSpecs::Anonymous::B" ModuleSpecs::Anonymous::C = m m.name.should == "ModuleSpecs::Anonymous::B" + ensure + ModuleSpecs::Anonymous.send(:remove_const, :B) + ModuleSpecs::Anonymous.send(:remove_const, :C) end it "is not modified when assigned to a different anonymous module" do @@ -125,6 +134,8 @@ describe "Module#name" do m::N = Module.new ModuleSpecs::Anonymous::E = m m::N.name.should == "ModuleSpecs::Anonymous::E::N" + ensure + ModuleSpecs::Anonymous.send(:remove_const, :E) end # https://bugs.ruby-lang.org/issues/19681 @@ -138,6 +149,48 @@ describe "Module#name" do "ModuleSpecs::Anonymous::StoredInMultiplePlaces::O" ] valid_names.should include(m::N.name) # You get one of the two, but you don't know which one. + ensure + ModuleSpecs::Anonymous.send(:remove_const, :StoredInMultiplePlaces) + end + + it "is set in #const_added callback when a module defined in the top-level scope" do + ruby_exe(<<~RUBY, args: "2>&1").chomp.should == "TEST1\nTEST2" + class Module + def const_added(name) + puts const_get(name).name + end + end + + # module with name + module TEST1 + end + + # anonymous module + TEST2 = Module.new + RUBY + end + + it "is set in #const_added callback for a nested module when an outer module defined in the top-level scope" do + ScratchPad.record [] + + ModuleSpecs::NameSpecs::NamedModule = Module.new do + def self.const_added(name) + ScratchPad << const_get(name).name + end + + module self::A + def self.const_added(name) + ScratchPad << const_get(name).name + end + + module self::B + end + end + end + + ScratchPad.recorded.should.one?(/#<Module.+>::A$/) + ScratchPad.recorded.should.one?(/#<Module.+>::A::B$/) + ModuleSpecs::NameSpecs.send :remove_const, :NamedModule end it "returns a frozen String" do diff --git a/spec/ruby/core/module/new_spec.rb b/spec/ruby/core/module/new_spec.rb index da7f3b8720..ec521360bd 100644 --- a/spec/ruby/core/module/new_spec.rb +++ b/spec/ruby/core/module/new_spec.rb @@ -6,6 +6,10 @@ describe "Module.new" do Module.new.is_a?(Module).should == true end + it "creates a module without a name" do + Module.new.name.should be_nil + end + it "creates a new Module and passes it to the provided block" do test_mod = nil m = Module.new do |mod| diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb index 96598e7209..71e82c513e 100644 --- a/spec/ruby/core/module/prepend_spec.rb +++ b/spec/ruby/core/module/prepend_spec.rb @@ -75,6 +75,26 @@ describe "Module#prepend" do foo.call.should == 'm' end + it "updates the optimized method when a prepended module is updated" do + out = ruby_exe(<<~RUBY) + module M; end + class Integer + prepend M + end + l = -> { 1 + 2 } + p l.call + M.module_eval do + def +(o) + $called = true + super(o) + end + end + p l.call + p $called + RUBY + out.should == "3\n3\ntrue\n" + end + it "updates the method when there is a base included method and the prepended module overrides it" do base_module = Module.new do def foo @@ -241,6 +261,8 @@ describe "Module#prepend" do B.prepend M B.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatePrepended) end it "updates the constant when a prepended module is updated" do @@ -261,6 +283,8 @@ describe "Module#prepend" do M.const_set(:FOO, 'm') B.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstPrependedUpdated) end it "updates the constant when there is a base included constant and the prepended module overrides it" do @@ -282,6 +306,8 @@ describe "Module#prepend" do A.prepend M A.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstIncludedPrependedOverride) end it "updates the constant when there is a base included constant and the prepended module is later updated" do @@ -305,6 +331,8 @@ describe "Module#prepend" do M.const_set(:FOO, 'm') A.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstIncludedPrependedLaterUpdated) end it "updates the constant when a module prepended after a constant is later updated" do @@ -328,6 +356,8 @@ describe "Module#prepend" do M.const_set(:FOO, 'm') B.foo.should == 'm' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatedPrependedAfterLaterUpdated) end it "updates the constant when a module is prepended after another and the constant is defined later on that module" do @@ -352,6 +382,8 @@ describe "Module#prepend" do N.const_set(:FOO, 'n') A.foo.should == 'n' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatedPrependedAfterConstDefined) end it "updates the constant when a module is included in a prepended module and the constant is defined later" do @@ -379,6 +411,8 @@ describe "Module#prepend" do N.const_set(:FOO, 'n') A.foo.should == 'n' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatedIncludedInPrependedConstDefinedLater) end it "updates the constant when a new module with an included module is prepended" do @@ -405,6 +439,8 @@ describe "Module#prepend" do B.prepend M B.foo.should == 'n' end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatedNewModuleIncludedPrepended) end it "raises a TypeError when the argument is not a Module" do @@ -412,7 +448,23 @@ describe "Module#prepend" do end it "does not raise a TypeError when the argument is an instance of a subclass of Module" do - -> { ModuleSpecs::SubclassSpec.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) + class ModuleSpecs::SubclassSpec::AClass + end + -> { ModuleSpecs::SubclassSpec::AClass.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError) + ensure + ModuleSpecs::SubclassSpec.send(:remove_const, :AClass) + end + + it "raises a TypeError when the argument is a refinement" do + refinement = nil + + Module.new do + refine String do + refinement = self + end + end + + -> { ModuleSpecs::Basic.prepend(refinement) }.should raise_error(TypeError, "Cannot prepend refinement") end it "imports constants" do @@ -726,6 +778,33 @@ describe "Module#prepend" do ary.should == [3, 2, 1] end + it "does not prepend a second copy if the module already indirectly exists in the hierarchy" do + mod = Module.new do; end + submod = Module.new do; end + klass = Class.new do; end + klass.include(mod) + mod.prepend(submod) + klass.include(mod) + + klass.ancestors.take(4).should == [klass, submod, mod, Object] + end + + # https://bugs.ruby-lang.org/issues/17423 + describe "when module already exists in ancestor chain" do + it "modifies the ancestor chain" do + m = Module.new do; end + a = Module.new do; end + b = Class.new do; end + + b.include(a) + a.prepend(m) + b.ancestors.take(4).should == [b, m, a, Object] + + b.prepend(m) + b.ancestors.take(5).should == [m, b, m, a, Object] + end + end + describe "called on a module" do describe "included into a class" it "does not obscure the module's methods from reflective access" do diff --git a/spec/ruby/core/module/private_spec.rb b/spec/ruby/core/module/private_spec.rb index ead806637c..9e1a297eea 100644 --- a/spec/ruby/core/module/private_spec.rb +++ b/spec/ruby/core/module/private_spec.rb @@ -38,25 +38,13 @@ describe "Module#private" do :module_specs_public_method_on_object_for_kernel_private) end - ruby_version_is ""..."3.1" do - it "returns self" do - (class << Object.new; self; end).class_eval do - def foo; end - private(:foo).should equal(self) - private.should equal(self) - end - end - end - - ruby_version_is "3.1" do - it "returns argument or arguments if given" do - (class << Object.new; self; end).class_eval do - def foo; end - private(:foo).should equal(:foo) - private([:foo, :foo]).should == [:foo, :foo] - private(:foo, :foo).should == [:foo, :foo] - private.should equal(nil) - end + it "returns argument or arguments if given" do + (class << Object.new; self; end).class_eval do + def foo; end + private(:foo).should equal(:foo) + private([:foo, :foo]).should == [:foo, :foo] + private(:foo, :foo).should == [:foo, :foo] + private.should equal(nil) end end diff --git a/spec/ruby/core/module/protected_spec.rb b/spec/ruby/core/module/protected_spec.rb index 058d49d751..9e37223e18 100644 --- a/spec/ruby/core/module/protected_spec.rb +++ b/spec/ruby/core/module/protected_spec.rb @@ -39,25 +39,13 @@ describe "Module#protected" do :module_specs_public_method_on_object_for_kernel_protected) end - ruby_version_is ""..."3.1" do - it "returns self" do - (class << Object.new; self; end).class_eval do - def foo; end - protected(:foo).should equal(self) - protected.should equal(self) - end - end - end - - ruby_version_is "3.1" do - it "returns argument or arguments if given" do - (class << Object.new; self; end).class_eval do - def foo; end - protected(:foo).should equal(:foo) - protected([:foo, :foo]).should == [:foo, :foo] - protected(:foo, :foo).should == [:foo, :foo] - protected.should equal(nil) - end + it "returns argument or arguments if given" do + (class << Object.new; self; end).class_eval do + def foo; end + protected(:foo).should equal(:foo) + protected([:foo, :foo]).should == [:foo, :foo] + protected(:foo, :foo).should == [:foo, :foo] + protected.should equal(nil) end end diff --git a/spec/ruby/core/module/public_spec.rb b/spec/ruby/core/module/public_spec.rb index e3b183f228..ce31eb5d0e 100644 --- a/spec/ruby/core/module/public_spec.rb +++ b/spec/ruby/core/module/public_spec.rb @@ -27,25 +27,13 @@ describe "Module#public" do :module_specs_private_method_on_object_for_kernel_public) end - ruby_version_is ""..."3.1" do - it "returns self" do - (class << Object.new; self; end).class_eval do - def foo; end - public(:foo).should equal(self) - public.should equal(self) - end - end - end - - ruby_version_is "3.1" do - it "returns argument or arguments if given" do - (class << Object.new; self; end).class_eval do - def foo; end - public(:foo).should equal(:foo) - public([:foo, :foo]).should == [:foo, :foo] - public(:foo, :foo).should == [:foo, :foo] - public.should equal(nil) - end + it "returns argument or arguments if given" do + (class << Object.new; self; end).class_eval do + def foo; end + public(:foo).should equal(:foo) + public([:foo, :foo]).should == [:foo, :foo] + public(:foo, :foo).should == [:foo, :foo] + public.should equal(nil) end end diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb index 11085c325b..d219b98825 100644 --- a/spec/ruby/core/module/refine_spec.rb +++ b/spec/ruby/core/module/refine_spec.rb @@ -243,36 +243,10 @@ describe "Module#refine" do result.should == "foo from singleton class" end - ruby_version_is ""..."3.2" do - it "looks in the included modules for builtin methods" do - result = ruby_exe(<<-RUBY) - a = Module.new do - def /(other) quo(other) end - end - - refinement = Module.new do - refine Integer do - include a - end - end - - result = nil - Module.new do - using refinement - result = 1 / 2 - end - - print result.class - RUBY - - result.should == 'Rational' - end - end - it "looks in later included modules of the refined module first" do a = Module.new do def foo - "foo from A" + "foo from A" end end @@ -300,67 +274,6 @@ describe "Module#refine" do result.should == "foo from IncludeMeLater" end - ruby_version_is ""..."3.1" do - it "looks in prepended modules from the refinement first" do - refined_class = ModuleSpecs.build_refined_class - - refinement = Module.new do - refine refined_class do - include ModuleSpecs::IncludedModule - prepend ModuleSpecs::PrependedModule - - def foo; "foo from refinement"; end - end - end - - result = nil - Module.new do - using refinement - result = refined_class.new.foo - end - - result.should == "foo from prepended module" - end - - it "looks in refinement then" do - refined_class = ModuleSpecs.build_refined_class - - refinement = Module.new do - refine(refined_class) do - include ModuleSpecs::IncludedModule - - def foo; "foo from refinement"; end - end - end - - result = nil - Module.new do - using refinement - result = refined_class.new.foo - end - - result.should == "foo from refinement" - end - - it "looks in included modules from the refinement then" do - refined_class = ModuleSpecs.build_refined_class - - refinement = Module.new do - refine refined_class do - include ModuleSpecs::IncludedModule - end - end - - result = nil - Module.new do - using refinement - result = refined_class.new.foo - end - - result.should == "foo from included module" - end - end - it "looks in the class then" do refined_class = ModuleSpecs.build_refined_class @@ -606,30 +519,6 @@ describe "Module#refine" do end context "when super is called in a refinement" do - ruby_version_is ""..."3.1" do - it "looks in the included to refinery module" do - refined_class = ModuleSpecs.build_refined_class - - refinement = Module.new do - refine refined_class do - include ModuleSpecs::IncludedModule - - def foo - super - end - end - end - - result = nil - Module.new do - using refinement - result = refined_class.new.foo - end - - result.should == "foo from included module" - end - end - it "looks in the refined class" do refined_class = ModuleSpecs.build_refined_class @@ -650,59 +539,6 @@ describe "Module#refine" do result.should == "foo" end - ruby_version_is ""..."3.1" do - it "looks in the refined class from included module" do - refined_class = ModuleSpecs.build_refined_class(for_super: true) - - a = Module.new do - def foo - [:A] + super - end - end - - refinement = Module.new do - refine refined_class do - include a - end - end - - result = nil - Module.new do - using refinement - - result = refined_class.new.foo - end - - result.should == [:A, :C] - end - - it "looks in the refined ancestors from included module" do - refined_class = ModuleSpecs.build_refined_class(for_super: true) - subclass = Class.new(refined_class) - - a = Module.new do - def foo - [:A] + super - end - end - - refinement = Module.new do - refine refined_class do - include a - end - end - - result = nil - Module.new do - using refinement - - result = subclass.new.foo - end - - result.should == [:A, :C] - end - end - # super in a method of a refinement invokes the method in the refined # class even if there is another refinement which has been activated # in the same context. @@ -763,179 +599,6 @@ describe "Module#refine" do }.should raise_error(NoMethodError) end end - - ruby_version_is ""..."3.1" do - it "does't have access to active refinements for C from included module" do - refined_class = ModuleSpecs.build_refined_class - - a = Module.new do - def foo - super + bar - end - end - - refinement = Module.new do - refine refined_class do - include a - - def bar - "bar is not seen from A methods" - end - end - end - - Module.new do - using refinement - -> { - refined_class.new.foo - }.should raise_error(NameError) { |e| e.name.should == :bar } - end - end - - it "does't have access to other active refinements from included module" do - refined_class = ModuleSpecs.build_refined_class - - refinement_integer = Module.new do - refine Integer do - def bar - "bar is not seen from A methods" - end - end - end - - a = Module.new do - def foo - super + 1.bar - end - end - - refinement = Module.new do - refine refined_class do - include a - end - end - - Module.new do - using refinement - using refinement_integer - -> { - refined_class.new.foo - }.should raise_error(NameError) { |e| e.name.should == :bar } - end - end - - # https://bugs.ruby-lang.org/issues/16977 - it "looks in the another active refinement if super called from included modules" do - refined_class = ModuleSpecs.build_refined_class(for_super: true) - - a = Module.new do - def foo - [:A] + super - end - end - - b = Module.new do - def foo - [:B] + super - end - end - - refinement_a = Module.new do - refine refined_class do - include a - end - end - - refinement_b = Module.new do - refine refined_class do - include b - end - end - - result = nil - Module.new do - using refinement_a - using refinement_b - result = refined_class.new.foo - end - - result.should == [:B, :A, :C] - end - - it "looks in the current active refinement from included modules" do - refined_class = ModuleSpecs.build_refined_class(for_super: true) - - a = Module.new do - def foo - [:A] + super - end - end - - b = Module.new do - def foo - [:B] + super - end - end - - refinement = Module.new do - refine refined_class do - def foo - [:LAST] + super - end - end - end - - refinement_a_b = Module.new do - refine refined_class do - include a - include b - end - end - - result = nil - Module.new do - using refinement - using refinement_a_b - result = refined_class.new.foo - end - - result.should == [:B, :A, :LAST, :C] - end - - it "looks in the lexical scope refinements before other active refinements" do - refined_class = ModuleSpecs.build_refined_class(for_super: true) - - refinement_local = Module.new do - refine refined_class do - def foo - [:LOCAL] + super - end - end - end - - a = Module.new do - using refinement_local - - def foo - [:A] + super - end - end - - refinement = Module.new do - refine refined_class do - include a - end - end - - result = nil - Module.new do - using refinement - result = refined_class.new.foo - end - - result.should == [:A, :LOCAL, :C] - end - end end it 'and alias aliases a method within a refinement module, but not outside it' do diff --git a/spec/ruby/core/module/refinements_spec.rb b/spec/ruby/core/module/refinements_spec.rb index 5648fcbd6f..05658a8b0e 100644 --- a/spec/ruby/core/module/refinements_spec.rb +++ b/spec/ruby/core/module/refinements_spec.rb @@ -1,45 +1,43 @@ require_relative '../../spec_helper' describe "Module#refinements" do - ruby_version_is "3.2" do - it "returns refinements defined in a module" do - ScratchPad.record [] - - m = Module.new do - refine String do - ScratchPad << self - end - - refine Array do - ScratchPad << self - end + it "returns refinements defined in a module" do + ScratchPad.record [] + + m = Module.new do + refine String do + ScratchPad << self end - m.refinements.sort_by(&:object_id).should == ScratchPad.recorded.sort_by(&:object_id) + refine Array do + ScratchPad << self + end end - it "does not return refinements defined in the included module" do - ScratchPad.record [] + m.refinements.sort_by(&:object_id).should == ScratchPad.recorded.sort_by(&:object_id) + end - m1 = Module.new do - refine Integer do - nil - end + it "does not return refinements defined in the included module" do + ScratchPad.record [] + + m1 = Module.new do + refine Integer do + nil end + end - m2 = Module.new do - include m1 + m2 = Module.new do + include m1 - refine String do - ScratchPad << self - end + refine String do + ScratchPad << self end - - m2.refinements.should == ScratchPad.recorded end - it "returns an empty array if no refinements defined in a module" do - Module.new.refinements.should == [] - end + m2.refinements.should == ScratchPad.recorded + end + + it "returns an empty array if no refinements defined in a module" do + Module.new.refinements.should == [] end end diff --git a/spec/ruby/core/module/remove_const_spec.rb b/spec/ruby/core/module/remove_const_spec.rb index 0ac23f05a5..35a9d65105 100644 --- a/spec/ruby/core/module/remove_const_spec.rb +++ b/spec/ruby/core/module/remove_const_spec.rb @@ -101,5 +101,7 @@ describe "Module#remove_const" do A.send(:remove_const,:FOO) A.foo.should == 'm' end + ensure + ConstantSpecs.send(:remove_const, :RemovedConstantUpdate) end end diff --git a/spec/ruby/core/module/ruby2_keywords_spec.rb b/spec/ruby/core/module/ruby2_keywords_spec.rb index dc16d712c7..652f9f7083 100644 --- a/spec/ruby/core/module/ruby2_keywords_spec.rb +++ b/spec/ruby/core/module/ruby2_keywords_spec.rb @@ -22,7 +22,6 @@ describe "Module#ruby2_keywords" do end h = {a: 1} - obj.regular(**h).should.equal?(h) last = mark(**h).last Hash.ruby2_keywords_hash?(last).should == true @@ -77,122 +76,60 @@ describe "Module#ruby2_keywords" do Hash.ruby2_keywords_hash?(marked).should == true end - ruby_version_is "3.2" do - it "makes a copy and unmark the Hash when calling a method taking (*args)" do - obj = Object.new - obj.singleton_class.class_exec do - def splat(*args) - args.last - end - - def splat1(arg, *args) - args.last - end + it "makes a copy and unmark the Hash when calling a method taking (*args)" do + obj = Object.new + obj.singleton_class.class_exec do + def splat(*args) + args.last + end - def proc_call(*args) - -> *a { a.last }.call(*args) - end + def splat1(arg, *args) + args.last end - h = { a: 1 } - args = mark(**h) - marked = args.last - Hash.ruby2_keywords_hash?(marked).should == true - - after_usage = obj.splat(*args) - after_usage.should == h - after_usage.should_not.equal?(h) - after_usage.should_not.equal?(marked) - Hash.ruby2_keywords_hash?(after_usage).should == false - Hash.ruby2_keywords_hash?(marked).should == true - - args = mark(1, **h) - marked = args.last - after_usage = obj.splat1(*args) - after_usage.should == h - after_usage.should_not.equal?(h) - after_usage.should_not.equal?(marked) - Hash.ruby2_keywords_hash?(after_usage).should == false - Hash.ruby2_keywords_hash?(marked).should == true - - args = mark(**h) - marked = args.last - after_usage = obj.proc_call(*args) - after_usage.should == h - after_usage.should_not.equal?(h) - after_usage.should_not.equal?(marked) - Hash.ruby2_keywords_hash?(after_usage).should == false - Hash.ruby2_keywords_hash?(marked).should == true - - args = mark(**h) - marked = args.last - after_usage = obj.send(:splat, *args) - after_usage.should == h - after_usage.should_not.equal?(h) - after_usage.should_not.equal?(marked) - Hash.ruby2_keywords_hash?(after_usage).should == false - Hash.ruby2_keywords_hash?(marked).should == true + def proc_call(*args) + -> *a { a.last }.call(*args) + end end - end - ruby_version_is ""..."3.2" do - # https://bugs.ruby-lang.org/issues/18625 - it "does NOT copy the Hash when calling a method taking (*args)" do - obj = Object.new - obj.singleton_class.class_exec do - def splat(*args) - args.last - end + h = { a: 1 } + args = mark(**h) + marked = args.last + Hash.ruby2_keywords_hash?(marked).should == true - def splat1(arg, *args) - args.last - end + after_usage = obj.splat(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true - def proc_call(*args) - -> *a { a.last }.call(*args) - end - end + args = mark(1, **h) + marked = args.last + after_usage = obj.splat1(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true - h = { a: 1 } - args = mark(**h) - marked = args.last - Hash.ruby2_keywords_hash?(marked).should == true - - after_usage = obj.splat(*args) - after_usage.should == h - after_usage.should_not.equal?(h) - after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625 - Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625 - Hash.ruby2_keywords_hash?(marked).should == true - - args = mark(1, **h) - marked = args.last - after_usage = obj.splat1(*args) - after_usage.should == h - after_usage.should_not.equal?(h) - after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625 - Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625 - Hash.ruby2_keywords_hash?(marked).should == true - - args = mark(**h) - marked = args.last - after_usage = obj.proc_call(*args) - after_usage.should == h - after_usage.should_not.equal?(h) - after_usage.should.equal?(marked) # https://bugs.ruby-lang.org/issues/18625 - Hash.ruby2_keywords_hash?(after_usage).should == true # https://bugs.ruby-lang.org/issues/18625 - Hash.ruby2_keywords_hash?(marked).should == true - - args = mark(**h) - marked = args.last - after_usage = obj.send(:splat, *args) - after_usage.should == h - after_usage.should_not.equal?(h) - send_copies = RUBY_ENGINE == "ruby" # inconsistent with Proc#call above for CRuby - after_usage.equal?(marked).should == !send_copies - Hash.ruby2_keywords_hash?(after_usage).should == !send_copies - Hash.ruby2_keywords_hash?(marked).should == true - end + args = mark(**h) + marked = args.last + after_usage = obj.proc_call(*args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true + + args = mark(**h) + marked = args.last + after_usage = obj.send(:splat, *args) + after_usage.should == h + after_usage.should_not.equal?(h) + after_usage.should_not.equal?(marked) + Hash.ruby2_keywords_hash?(after_usage).should == false + Hash.ruby2_keywords_hash?(marked).should == true end it "applies to the underlying method and applies across aliasing" do @@ -238,7 +175,7 @@ describe "Module#ruby2_keywords" do obj.singleton_class.class_exec do ruby2_keywords :not_existing end - }.should raise_error(NameError, /undefined method `not_existing'/) + }.should raise_error(NameError, /undefined method [`']not_existing'/) end it "accepts String as well" do @@ -276,7 +213,7 @@ describe "Module#ruby2_keywords" do it "prints warning when a method accepts keywords" do obj = Object.new - def obj.foo(a:, b:) end + def obj.foo(*a, b:) end -> { obj.singleton_class.class_exec do @@ -287,7 +224,7 @@ describe "Module#ruby2_keywords" do it "prints warning when a method accepts keyword splat" do obj = Object.new - def obj.foo(**a) end + def obj.foo(*a, **b) end -> { obj.singleton_class.class_exec do @@ -295,4 +232,17 @@ describe "Module#ruby2_keywords" do end }.should complain(/Skipping set of ruby2_keywords flag for/) end + + ruby_version_is "4.0" do + it "prints warning when a method accepts post arguments" do + obj = Object.new + def obj.foo(*a, b) end + + -> { + obj.singleton_class.class_exec do + ruby2_keywords :foo + end + }.should complain(/Skipping set of ruby2_keywords flag for/) + end + end end diff --git a/spec/ruby/core/module/set_temporary_name_spec.rb b/spec/ruby/core/module/set_temporary_name_spec.rb index f5886a3398..46605ed675 100644 --- a/spec/ruby/core/module/set_temporary_name_spec.rb +++ b/spec/ruby/core/module/set_temporary_name_spec.rb @@ -1,4 +1,5 @@ require_relative '../../spec_helper' +require_relative 'fixtures/set_temporary_name' ruby_version_is "3.3" do describe "Module#set_temporary_name" do @@ -13,13 +14,34 @@ ruby_version_is "3.3" do m.name.should be_nil end + it "returns self" do + m = Module.new + m.set_temporary_name("fake_name").should.equal? m + end + it "can assign a temporary name which is not a valid constant path" do m = Module.new - m.set_temporary_name("a::B") - m.name.should == "a::B" + + m.set_temporary_name("name") + m.name.should == "name" m.set_temporary_name("Template['foo.rb']") m.name.should == "Template['foo.rb']" + + m.set_temporary_name("a::B") + m.name.should == "a::B" + + m.set_temporary_name("A::b") + m.name.should == "A::b" + + m.set_temporary_name("A::B::") + m.name.should == "A::B::" + + m.set_temporary_name("A::::B") + m.name.should == "A::::B" + + m.set_temporary_name("A=") + m.name.should == "A=" end it "can't assign empty string as name" do @@ -43,7 +65,7 @@ ruby_version_is "3.3" do -> { Object.set_temporary_name("fake_name") }.should raise_error(RuntimeError, "can't change permanent name") end - it "can assign a temporary name to a nested module" do + it "can assign a temporary name to a module nested into an anonymous module" do m = Module.new module m::N; end m::N.name.should =~ /\A#<Module:0x\h+>::N\z/ @@ -55,6 +77,18 @@ ruby_version_is "3.3" do m::N.name.should be_nil end + it "discards a temporary name when an outer anonymous module gets a permanent name" do + m = Module.new + module m::N; end + + m::N.set_temporary_name("fake_name") + m::N.name.should == "fake_name" + + ModuleSpecs::SetTemporaryNameSpec::M = m + m::N.name.should == "ModuleSpecs::SetTemporaryNameSpec::M::N" + ModuleSpecs::SetTemporaryNameSpec.send :remove_const, :M + end + it "can update the name when assigned to a constant" do m = Module.new m::N = Module.new @@ -64,5 +98,50 @@ ruby_version_is "3.3" do m::M = m::N m::M.name.should =~ /\A#<Module:0x\h+>::M\z/m end + + it "can reassign a temporary name repeatedly" do + m = Module.new + + m.set_temporary_name("fake_name") + m.name.should == "fake_name" + + m.set_temporary_name("fake_name_2") + m.name.should == "fake_name_2" + end + + ruby_bug "#21094", ""..."4.0" do + it "also updates a name of a nested module" do + m = Module.new + m::N = Module.new + m::N.name.should =~ /\A#<Module:0x\h+>::N\z/ + + m.set_temporary_name "m" + m::N.name.should == "m::N" + + m.set_temporary_name nil + m::N.name.should == nil + end + end + + it "keeps temporary name when assigned in an anonymous module" do + outer = Module.new + m = Module.new + m.set_temporary_name "m" + m.name.should == "m" + outer::M = m + m.name.should == "m" + m.inspect.should == "m" + end + + it "keeps temporary name when assigned in an anonymous module and nested before" do + outer = Module.new + m = Module.new + outer::A = m + m.set_temporary_name "m" + m.name.should == "m" + outer::M = m + m.name.should == "m" + m.inspect.should == "m" + end end end diff --git a/spec/ruby/core/module/shared/attr_added.rb b/spec/ruby/core/module/shared/attr_added.rb new file mode 100644 index 0000000000..ce832cdcff --- /dev/null +++ b/spec/ruby/core/module/shared/attr_added.rb @@ -0,0 +1,34 @@ +describe :module_attr_added, shared: true do + it "calls method_added for normal classes" do + ScratchPad.record [] + + cls = Class.new do + class << self + def method_added(name) + ScratchPad.recorded << name + end + end + end + + cls.send(@method, :foo) + + ScratchPad.recorded.each {|name| name.to_s.should =~ /foo[=]?/} + end + + it "calls singleton_method_added for singleton classes" do + ScratchPad.record [] + cls = Class.new do + class << self + def singleton_method_added(name) + # called for this def so ignore it + return if name == :singleton_method_added + ScratchPad.recorded << name + end + end + end + + cls.singleton_class.send(@method, :foo) + + ScratchPad.recorded.each {|name| name.to_s.should =~ /foo[=]?/} + end +end diff --git a/spec/ruby/core/module/to_s_spec.rb b/spec/ruby/core/module/to_s_spec.rb index 6b1a615ef9..83c0ae0825 100644 --- a/spec/ruby/core/module/to_s_spec.rb +++ b/spec/ruby/core/module/to_s_spec.rb @@ -51,6 +51,8 @@ describe "Module#to_s" do ModuleSpecs::RefinementInspect::R.name.should == 'ModuleSpecs::RefinementInspect::R' ModuleSpecs::RefinementInspect::R.to_s.should == '#<refinement:String@ModuleSpecs::RefinementInspect>' + ensure + ModuleSpecs.send(:remove_const, :RefinementInspect) end it 'does not call #inspect or #to_s for singleton classes' do diff --git a/spec/ruby/core/module/undef_method_spec.rb b/spec/ruby/core/module/undef_method_spec.rb index c2ad200536..d4efcd51cb 100644 --- a/spec/ruby/core/module/undef_method_spec.rb +++ b/spec/ruby/core/module/undef_method_spec.rb @@ -50,7 +50,7 @@ describe "Module#undef_method" do end it "raises a NameError when passed a missing name for a module" do - -> { @module.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for module `#{@module}'/) { |e| + -> { @module.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method [`']not_exist' for module [`']#{@module}'/) { |e| # a NameError and not a NoMethodError e.class.should == NameError } @@ -58,7 +58,7 @@ describe "Module#undef_method" do it "raises a NameError when passed a missing name for a class" do klass = Class.new - -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `#{klass}'/) { |e| + -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method [`']not_exist' for class [`']#{klass}'/) { |e| # a NameError and not a NoMethodError e.class.should == NameError } @@ -69,8 +69,8 @@ describe "Module#undef_method" do obj = klass.new sclass = obj.singleton_class - -> { sclass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `#{sclass}'/) { |e| - e.message.should include('`#<Class:#<#<Class:') + -> { sclass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method [`']not_exist' for class [`']#{sclass}'/) { |e| + e.message.should =~ /[`']#<Class:#<#<Class:/ # a NameError and not a NoMethodError e.class.should == NameError @@ -79,7 +79,7 @@ describe "Module#undef_method" do it "raises a NameError when passed a missing name for a metaclass" do klass = String.singleton_class - -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method `not_exist' for class `String'/) { |e| + -> { klass.send :undef_method, :not_exist }.should raise_error(NameError, /undefined method [`']not_exist' for class [`']String'/) { |e| # a NameError and not a NoMethodError e.class.should == NameError } diff --git a/spec/ruby/core/module/undefined_instance_methods_spec.rb b/spec/ruby/core/module/undefined_instance_methods_spec.rb index 3be860d053..d33ee93fc1 100644 --- a/spec/ruby/core/module/undefined_instance_methods_spec.rb +++ b/spec/ruby/core/module/undefined_instance_methods_spec.rb @@ -2,25 +2,23 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Module#undefined_instance_methods" do - ruby_version_is "3.2" do - it "returns methods undefined in the class" do - methods = ModuleSpecs::UndefinedInstanceMethods::Parent.undefined_instance_methods - methods.should == [:undefed_method] - end + it "returns methods undefined in the class" do + methods = ModuleSpecs::UndefinedInstanceMethods::Parent.undefined_instance_methods + methods.should == [:undefed_method] + end - it "returns inherited methods undefined in the class" do - methods = ModuleSpecs::UndefinedInstanceMethods::Child.undefined_instance_methods - methods.should include(:parent_method, :another_parent_method) - end + it "returns inherited methods undefined in the class" do + methods = ModuleSpecs::UndefinedInstanceMethods::Child.undefined_instance_methods + methods.should include(:parent_method, :another_parent_method) + end - it "returns methods from an included module that are undefined in the class" do - methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods - methods.should include(:super_included_method) - end + it "returns methods from an included module that are undefined in the class" do + methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods + methods.should include(:super_included_method) + end - it "does not returns ancestors undefined methods" do - methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods - methods.should_not include(:parent_method, :another_parent_method) - end + it "does not returns ancestors undefined methods" do + methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods + methods.should_not include(:parent_method, :another_parent_method) end end diff --git a/spec/ruby/core/module/used_refinements_spec.rb b/spec/ruby/core/module/used_refinements_spec.rb index c16cab0e3c..40dd4a444e 100644 --- a/spec/ruby/core/module/used_refinements_spec.rb +++ b/spec/ruby/core/module/used_refinements_spec.rb @@ -1,87 +1,85 @@ require_relative '../../spec_helper' describe "Module.used_refinements" do - ruby_version_is "3.2" do - it "returns list of all refinements imported in the current scope" do - refinement_int = nil - refinement_str = nil - ScratchPad.record [] - - m1 = Module.new do - refine Integer do - refinement_int = self - end + it "returns list of all refinements imported in the current scope" do + refinement_int = nil + refinement_str = nil + ScratchPad.record [] + + m1 = Module.new do + refine Integer do + refinement_int = self end + end - m2 = Module.new do - refine String do - refinement_str = self - end + m2 = Module.new do + refine String do + refinement_str = self end + end - Module.new do - using m1 - using m2 + Module.new do + using m1 + using m2 - Module.used_refinements.each { |r| ScratchPad << r } - end - - ScratchPad.recorded.sort_by(&:object_id).should == [refinement_int, refinement_str].sort_by(&:object_id) + Module.used_refinements.each { |r| ScratchPad << r } end - it "returns empty array if does not have any refinements imported" do - used_refinements = nil + ScratchPad.recorded.sort_by(&:object_id).should == [refinement_int, refinement_str].sort_by(&:object_id) + end - Module.new do - used_refinements = Module.used_refinements - end + it "returns empty array if does not have any refinements imported" do + used_refinements = nil - used_refinements.should == [] + Module.new do + used_refinements = Module.used_refinements end - it "ignores refinements imported in a module that is included into the current one" do - used_refinements = nil + used_refinements.should == [] + end - m1 = Module.new do - refine Integer do - nil - end - end + it "ignores refinements imported in a module that is included into the current one" do + used_refinements = nil - m2 = Module.new do - using m1 + m1 = Module.new do + refine Integer do + nil end + end - Module.new do - include m2 + m2 = Module.new do + using m1 + end - used_refinements = Module.used_refinements - end + Module.new do + include m2 - used_refinements.should == [] + used_refinements = Module.used_refinements end - it "returns refinements even not defined directly in a module refinements are imported from" do - used_refinements = nil - ScratchPad.record [] + used_refinements.should == [] + end - m1 = Module.new do - refine Integer do - ScratchPad << self - end - end + it "returns refinements even not defined directly in a module refinements are imported from" do + used_refinements = nil + ScratchPad.record [] - m2 = Module.new do - include m1 + m1 = Module.new do + refine Integer do + ScratchPad << self end + end - Module.new do - using m2 + m2 = Module.new do + include m1 + end - used_refinements = Module.used_refinements - end + Module.new do + using m2 - used_refinements.should == ScratchPad.recorded + used_refinements = Module.used_refinements end + + used_refinements.should == ScratchPad.recorded end end diff --git a/spec/ruby/core/module/using_spec.rb b/spec/ruby/core/module/using_spec.rb index 4781b99bb7..a908363c96 100644 --- a/spec/ruby/core/module/using_spec.rb +++ b/spec/ruby/core/module/using_spec.rb @@ -316,7 +316,7 @@ describe "Module#using" do using refinement def initialize - @a = "1703" + @a = +"1703" @a.instance_eval do def abc |
