diff options
Diffstat (limited to 'spec/ruby/core/module')
82 files changed, 3800 insertions, 1646 deletions
diff --git a/spec/ruby/core/module/alias_method_spec.rb b/spec/ruby/core/module/alias_method_spec.rb index 5d3d0c23d9..852879cc8a 100644 --- a/spec/ruby/core/module/alias_method_spec.rb +++ b/spec/ruby/core/module/alias_method_spec.rb @@ -28,12 +28,12 @@ describe "Module#alias_method" do it "retains method visibility" do @class.make_alias :private_ichi, :private_one - -> { @object.private_one }.should raise_error(NameError) - -> { @object.private_ichi }.should raise_error(NameError) + -> { @object.private_one }.should.raise(NameError) + -> { @object.private_ichi }.should.raise(NameError) @class.make_alias :public_ichi, :public_one @object.public_ichi.should == @object.public_one @class.make_alias :protected_ichi, :protected_one - -> { @object.protected_ichi }.should raise_error(NameError) + -> { @object.protected_ichi }.should.raise(NameError) end it "handles aliasing a stub that changes visibility" do @@ -55,7 +55,7 @@ describe "Module#alias_method" do end it "fails if origin method not found" do - -> { @class.make_alias :ni, :san }.should raise_error(NameError) { |e| + -> { @class.make_alias :ni, :san }.should.raise(NameError) { |e| # a NameError and not a NoMethodError e.class.should == NameError } @@ -63,7 +63,7 @@ describe "Module#alias_method" do it "raises FrozenError if frozen" do @class.freeze - -> { @class.make_alias :uno, :public_one }.should raise_error(FrozenError) + -> { @class.make_alias :uno, :public_one }.should.raise(FrozenError) end it "converts the names using #to_str" do @@ -78,25 +78,23 @@ describe "Module#alias_method" do end it "raises a TypeError when the given name can't be converted using to_str" do - -> { @class.make_alias mock('x'), :public_one }.should raise_error(TypeError) + -> { @class.make_alias mock('x'), :public_one }.should.raise(TypeError) + end + + it "raises a NoMethodError if the given name raises a NoMethodError during type coercion using to_str" do + obj = mock("mock-name") + obj.should_receive(:to_str).and_raise(NoMethodError) + -> { @class.make_alias obj, :public_one }.should.raise(NoMethodError) end it "is a public method" do - Module.should have_public_instance_method(:alias_method, false) + Module.public_instance_methods(false).should.include?(:alias_method) end describe "returned value" do - ruby_version_is ""..."3.0" do - it "returns self" do - @class.send(:alias_method, :checking_return_value, :public_one).should equal(@class) - end - end - - ruby_version_is "3.0" do - it "returns symbol of the defined method name" do - @class.send(:alias_method, :checking_return_value, :public_one).should equal(:checking_return_value) - @class.send(:alias_method, 'checking_return_value', :public_one).should equal(:checking_return_value) - end + it "returns symbol of the defined method name" do + @class.send(:alias_method, :checking_return_value, :public_one).should.equal?(:checking_return_value) + @class.send(:alias_method, 'checking_return_value', :public_one).should.equal?(:checking_return_value) end end @@ -106,14 +104,14 @@ describe "Module#alias_method" do it "works on private module methods in a module that has been reopened" do ModuleSpecs::ReopeningModule.foo.should == true - -> { ModuleSpecs::ReopeningModule.foo2 }.should_not raise_error(NoMethodError) + -> { ModuleSpecs::ReopeningModule.foo2 }.should_not.raise(NoMethodError) end it "accesses a method defined on Object from Kernel" do - Kernel.should_not have_public_instance_method(:module_specs_public_method_on_object) + Kernel.public_instance_methods(true).should_not.include?(:module_specs_public_method_on_object) - Kernel.should have_public_instance_method(:module_specs_alias_on_kernel) - Object.should have_public_instance_method(:module_specs_alias_on_kernel) + Kernel.public_instance_methods(false).should.include?(:module_specs_alias_on_kernel) + Object.public_instance_methods(true).should.include?(:module_specs_alias_on_kernel) end it "can call a method with super aliased twice" do @@ -132,42 +130,42 @@ describe "Module#alias_method" do it "keeps initialize private when aliasing" do @class.make_alias(:initialize, :public_one) - @class.private_instance_methods.include?(:initialize).should be_true + @class.private_instance_methods.include?(:initialize).should == true @subclass.make_alias(:initialize, :public_one) - @subclass.private_instance_methods.include?(:initialize).should be_true + @subclass.private_instance_methods.include?(:initialize).should == true end it "keeps initialize_copy private when aliasing" do @class.make_alias(:initialize_copy, :public_one) - @class.private_instance_methods.include?(:initialize_copy).should be_true + @class.private_instance_methods.include?(:initialize_copy).should == true @subclass.make_alias(:initialize_copy, :public_one) - @subclass.private_instance_methods.include?(:initialize_copy).should be_true + @subclass.private_instance_methods.include?(:initialize_copy).should == true end it "keeps initialize_clone private when aliasing" do @class.make_alias(:initialize_clone, :public_one) - @class.private_instance_methods.include?(:initialize_clone).should be_true + @class.private_instance_methods.include?(:initialize_clone).should == true @subclass.make_alias(:initialize_clone, :public_one) - @subclass.private_instance_methods.include?(:initialize_clone).should be_true + @subclass.private_instance_methods.include?(:initialize_clone).should == true end it "keeps initialize_dup private when aliasing" do @class.make_alias(:initialize_dup, :public_one) - @class.private_instance_methods.include?(:initialize_dup).should be_true + @class.private_instance_methods.include?(:initialize_dup).should == true @subclass.make_alias(:initialize_dup, :public_one) - @subclass.private_instance_methods.include?(:initialize_dup).should be_true + @subclass.private_instance_methods.include?(:initialize_dup).should == true end it "keeps respond_to_missing? private when aliasing" do @class.make_alias(:respond_to_missing?, :public_one) - @class.private_instance_methods.include?(:respond_to_missing?).should be_true + @class.private_instance_methods.include?(:respond_to_missing?).should == true @subclass.make_alias(:respond_to_missing?, :public_one) - @subclass.private_instance_methods.include?(:respond_to_missing?).should be_true + @subclass.private_instance_methods.include?(:respond_to_missing?).should == true end end end diff --git a/spec/ruby/core/module/allocate_spec.rb b/spec/ruby/core/module/allocate_spec.rb deleted file mode 100644 index 3b2c4119c3..0000000000 --- a/spec/ruby/core/module/allocate_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -require_relative '../../spec_helper' - -describe "Module.allocate" do - it "returns an instance of Module" do - mod = Module.allocate - mod.should be_an_instance_of(Module) - end - - it "returns a fully-formed instance of Module" do - mod = Module.allocate - mod.constants.should_not == nil - mod.methods.should_not == nil - end -end diff --git a/spec/ruby/core/module/ancestors_spec.rb b/spec/ruby/core/module/ancestors_spec.rb index 5e4c196206..f85884a4f3 100644 --- a/spec/ruby/core/module/ancestors_spec.rb +++ b/spec/ruby/core/module/ancestors_spec.rb @@ -7,27 +7,45 @@ 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 - class << ModuleSpecs::Child; self; end.ancestors.should include(ModuleSpecs::Internal, Class, Module, Object, Kernel) + class << ModuleSpecs::Child; self; end.ancestors.to_set.should >= Set[ModuleSpecs::Internal, Class, Module, Object, Kernel] end it "has 1 entry per module or class" 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 child = Class.new(parent) schild = child.singleton_class - schild.ancestors.should include(schild, + schild.ancestors.to_set.should >= Set[schild, parent.singleton_class, Object.singleton_class, BasicObject.singleton_class, @@ -35,14 +53,14 @@ describe "Module#ancestors" do Module, Object, Kernel, - BasicObject) + BasicObject] end describe 'for a standalone module' do it 'does not include Class' do s_mod = ModuleSpecs.singleton_class - s_mod.ancestors.should_not include(Class) + s_mod.ancestors.should_not.include?(Class) end it 'does not include other singleton classes' do @@ -51,19 +69,19 @@ describe "Module#ancestors" do s_object = Object.singleton_class s_basic_object = BasicObject.singleton_class - s_standalone_mod.ancestors.should_not include(s_module, s_object, s_basic_object) + (s_standalone_mod.ancestors & [s_module, s_object, s_basic_object]).should.empty? end it 'includes its own singleton class' do s_mod = ModuleSpecs.singleton_class - s_mod.ancestors.should include(s_mod) + s_mod.ancestors.should.include?(s_mod) end it 'includes standard chain' do s_mod = ModuleSpecs.singleton_class - s_mod.ancestors.should include(Module, Object, Kernel, BasicObject) + s_mod.ancestors.to_set.should >= Set[Module, Object, Kernel, BasicObject] end end end diff --git a/spec/ruby/core/module/append_features_spec.rb b/spec/ruby/core/module/append_features_spec.rb index d960798eef..4d2207330d 100644 --- a/spec/ruby/core/module/append_features_spec.rb +++ b/spec/ruby/core/module/append_features_spec.rb @@ -3,18 +3,18 @@ require_relative 'fixtures/classes' describe "Module#append_features" do it "is a private method" do - Module.should have_private_instance_method(:append_features) + Module.private_instance_methods(false).should.include?(:append_features) end describe "on Class" do it "is undefined" do - Class.should_not have_private_instance_method(:append_features, true) + Class.private_instance_methods(true).should_not.include?(:append_features) end it "raises a TypeError if calling after rebinded to Class" do -> { Module.instance_method(:append_features).bind(Class.new).call Module.new - }.should raise_error(TypeError) + }.should.raise(TypeError) end end @@ -39,28 +39,14 @@ describe "Module#append_features" do it "raises an ArgumentError on a cyclic include" do -> { ModuleSpecs::CyclicAppendA.send(:append_features, ModuleSpecs::CyclicAppendA) - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) -> { ModuleSpecs::CyclicAppendB.send(:append_features, ModuleSpecs::CyclicAppendA) - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end - ruby_version_is ''...'2.7' do - it "copies own tainted status to the given module" do - other = Module.new - Module.new.taint.send :append_features, other - other.tainted?.should be_true - end - - it "copies own untrusted status to the given module" do - other = Module.new - Module.new.untrust.send :append_features, other - other.untrusted?.should be_true - end - end - describe "when other is frozen" do before :each do @receiver = Module.new @@ -68,8 +54,8 @@ describe "Module#append_features" do end it "raises a FrozenError before appending self" do - -> { @receiver.send(:append_features, @other) }.should raise_error(FrozenError) - @other.ancestors.should_not include(@receiver) + -> { @receiver.send(:append_features, @other) }.should.raise(FrozenError) + @other.ancestors.should_not.include?(@receiver) end end end diff --git a/spec/ruby/core/module/attr_accessor_spec.rb b/spec/ruby/core/module/attr_accessor_spec.rb index e6a6ac7b66..a608760cf2 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 @@ -33,7 +34,20 @@ describe "Module#attr_accessor" do attr_accessor :spec_attr_accessor end - -> { true.spec_attr_accessor = "a" }.should raise_error(RuntimeError) + -> { true.spec_attr_accessor = "a" }.should.raise(FrozenError) + end + + it "raises FrozenError if the receiver if frozen" do + c = Class.new do + attr_accessor :foo + end + obj = c.new + obj.foo = 1 + obj.foo.should == 1 + + obj.freeze + -> { obj.foo = 42 }.should.raise(FrozenError) + obj.foo.should == 1 end it "converts non string/symbol names to strings using to_str" do @@ -48,9 +62,9 @@ describe "Module#attr_accessor" do it "raises a TypeError when the given names can't be converted to strings using to_str" do o = mock('o') - -> { Class.new { attr_accessor o } }.should raise_error(TypeError) + -> { Class.new { attr_accessor o } }.should.raise(TypeError) (o = mock('123')).should_receive(:to_str).and_return(123) - -> { Class.new { attr_accessor o } }.should raise_error(TypeError) + -> { Class.new { attr_accessor o } }.should.raise(TypeError) end it "applies current visibility to methods created" do @@ -59,27 +73,17 @@ describe "Module#attr_accessor" do attr_accessor :foo end - -> { c.new.foo }.should raise_error(NoMethodError) - -> { c.new.foo=1 }.should raise_error(NoMethodError) + -> { c.new.foo }.should.raise(NoMethodError) + -> { c.new.foo=1 }.should.raise(NoMethodError) end it "is a public method" do - Module.should have_public_instance_method(:attr_accessor, false) + Module.public_instance_methods(false).should.include?(:attr_accessor) end - ruby_version_is ""..."3.0" do - it "returns nil" do - Class.new do - (attr_accessor :foo, 'bar').should == nil - end - end - end - - ruby_version_is "3.0" do - it "returns an array of defined method names as symbols" do - Class.new do - (attr_accessor :foo, 'bar').should == [:foo, :foo=, :bar, :bar=] - end + it "returns an array of defined method names as symbols" do + Class.new do + (attr_accessor :foo, 'bar').should == [:foo, :foo=, :bar, :bar=] end end @@ -100,7 +104,9 @@ describe "Module#attr_accessor" do end it "can read through the accessor" do - 1.foobar.should be_nil + 1.foobar.should == 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 b0ae906ab5..2b4ca2100e 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 @@ -29,7 +30,7 @@ describe "Module#attr_reader" do attr_reader :spec_attr_reader end - -> { true.instance_variable_set("@spec_attr_reader", "a") }.should raise_error(RuntimeError) + -> { true.instance_variable_set("@spec_attr_reader", "a") }.should.raise(RuntimeError) end it "converts non string/symbol names to strings using to_str" do @@ -44,9 +45,9 @@ describe "Module#attr_reader" do it "raises a TypeError when the given names can't be converted to strings using to_str" do o = mock('o') - -> { Class.new { attr_reader o } }.should raise_error(TypeError) + -> { Class.new { attr_reader o } }.should.raise(TypeError) (o = mock('123')).should_receive(:to_str).and_return(123) - -> { Class.new { attr_reader o } }.should raise_error(TypeError) + -> { Class.new { attr_reader o } }.should.raise(TypeError) end it "applies current visibility to methods created" do @@ -55,26 +56,18 @@ describe "Module#attr_reader" do attr_reader :foo end - -> { c.new.foo }.should raise_error(NoMethodError) + -> { c.new.foo }.should.raise(NoMethodError) end it "is a public method" do - Module.should have_public_instance_method(:attr_reader, false) + Module.public_instance_methods(false).should.include?(:attr_reader) end - ruby_version_is ""..."3.0" do - it "returns nil" do - Class.new do - (attr_reader :foo, 'bar').should == nil - end + it "returns an array of defined method names as symbols" do + Class.new do + (attr_reader :foo, 'bar').should == [:foo, :bar] end end - ruby_version_is "3.0" do - it "returns an array of defined method names as symbols" do - Class.new do - (attr_reader :foo, 'bar').should == [:foo, :bar] - end - 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 33e0eb8628..d696864955 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 @@ -89,8 +90,8 @@ describe "Module#attr" do attr :foo, true end - -> { c.new.foo }.should raise_error(NoMethodError) - -> { c.new.foo=1 }.should raise_error(NoMethodError) + -> { c.new.foo }.should.raise(NoMethodError) + -> { c.new.foo=1 }.should.raise(NoMethodError) end it "creates a getter but no setter for all given attribute names" do @@ -120,8 +121,8 @@ describe "Module#attr" do attr :foo, :bar end - -> { c.new.foo }.should raise_error(NoMethodError) - -> { c.new.bar }.should raise_error(NoMethodError) + -> { c.new.foo }.should.raise(NoMethodError) + -> { c.new.bar }.should.raise(NoMethodError) end it "converts non string/symbol names to strings using to_str" do @@ -131,9 +132,9 @@ describe "Module#attr" do it "raises a TypeError when the given names can't be converted to strings using to_str" do o = mock('o') - -> { Class.new { attr o } }.should raise_error(TypeError) + -> { Class.new { attr o } }.should.raise(TypeError) (o = mock('123')).should_receive(:to_str).and_return(123) - -> { Class.new { attr o } }.should raise_error(TypeError) + -> { Class.new { attr o } }.should.raise(TypeError) end it "with a boolean argument emits a warning when $VERBOSE is true" do @@ -143,26 +144,16 @@ describe "Module#attr" do end it "is a public method" do - Module.should have_public_instance_method(:attr, false) + Module.public_instance_methods(false).should.include?(:attr) end - ruby_version_is ""..."3.0" do - it "returns nil" do - Class.new do - (attr :foo, 'bar').should == nil - (attr :baz, false).should == nil - (attr :qux, true).should == nil - end + it "returns an array of defined method names as symbols" do + Class.new do + (attr :foo, 'bar').should == [:foo, :bar] + (attr :baz, false).should == [:baz] + (attr :qux, true).should == [:qux, :qux=] end end - ruby_version_is "3.0" do - it "returns an array of defined method names as symbols" do - Class.new do - (attr :foo, 'bar').should == [:foo, :bar] - (attr :baz, false).should == [:baz] - (attr :qux, true).should == [:qux, :qux=] - end - 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 dbf30c8144..6089b52495 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 @@ -29,7 +30,17 @@ describe "Module#attr_writer" do attr_writer :spec_attr_writer end - -> { true.spec_attr_writer = "a" }.should raise_error(RuntimeError) + -> { true.spec_attr_writer = "a" }.should.raise(FrozenError) + end + + it "raises FrozenError if the receiver if frozen" do + c = Class.new do + attr_writer :foo + end + obj = c.new + obj.freeze + + -> { obj.foo = 42 }.should.raise(FrozenError) end it "converts non string/symbol names to strings using to_str" do @@ -44,9 +55,9 @@ describe "Module#attr_writer" do it "raises a TypeError when the given names can't be converted to strings using to_str" do o = mock('test1') - -> { Class.new { attr_writer o } }.should raise_error(TypeError) + -> { Class.new { attr_writer o } }.should.raise(TypeError) (o = mock('123')).should_receive(:to_str).and_return(123) - -> { Class.new { attr_writer o } }.should raise_error(TypeError) + -> { Class.new { attr_writer o } }.should.raise(TypeError) end it "applies current visibility to methods created" do @@ -55,26 +66,18 @@ describe "Module#attr_writer" do attr_writer :foo end - -> { c.new.foo=1 }.should raise_error(NoMethodError) + -> { c.new.foo=1 }.should.raise(NoMethodError) end it "is a public method" do - Module.should have_public_instance_method(:attr_writer, false) + Module.public_instance_methods(false).should.include?(:attr_writer) end - ruby_version_is ""..."3.0" do - it "returns nil" do - Class.new do - (attr_writer :foo, 'bar').should == nil - end + it "returns an array of defined method names as symbols" do + Class.new do + (attr_writer :foo, 'bar').should == [:foo=, :bar=] end end - ruby_version_is "3.0" do - it "returns an array of defined method names as symbols" do - Class.new do - (attr_writer :foo, 'bar').should == [:foo=, :bar=] - end - end - end + it_behaves_like :module_attr_added, :attr_writer end diff --git a/spec/ruby/core/module/autoload_relative_spec.rb b/spec/ruby/core/module/autoload_relative_spec.rb new file mode 100644 index 0000000000..2e7d85496b --- /dev/null +++ b/spec/ruby/core/module/autoload_relative_spec.rb @@ -0,0 +1,128 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +# Specs for Module#autoload_relative +module ModuleSpecs + module AutoloadRelative + # Will be used for testing + end +end + +ruby_version_is "4.1" do + describe "Module#autoload_relative" do + before :each do + @loaded_features = $".dup + end + + after :each do + $".replace @loaded_features + end + + it "is a public method" do + Module.public_instance_methods(false).should.include?(:autoload_relative) + end + + it "registers a file to load relative to the current file the first time the named constant is accessed" do + ModuleSpecs::Autoload.autoload_relative :AutoloadRelativeA, "fixtures/autoload_relative_a.rb" + path = ModuleSpecs::Autoload.autoload?(:AutoloadRelativeA) + path.should_not == nil + path.should.end_with?("autoload_relative_a.rb") + File.exist?(path).should == true + end + + it "loads the registered file when the constant is accessed" do + ModuleSpecs::Autoload.autoload_relative :AutoloadRelativeB, "fixtures/autoload_relative_a.rb" + ModuleSpecs::Autoload::AutoloadRelativeB.should.is_a?(Module) + end + + it "returns nil" do + ModuleSpecs::Autoload.autoload_relative(:AutoloadRelativeC, "fixtures/autoload_relative_a.rb").should == nil + end + + it "registers a file to load the first time the named constant is accessed" do + module ModuleSpecs::Autoload::AutoloadRelativeTest + autoload_relative :D, "fixtures/autoload_relative_a.rb" + end + path = ModuleSpecs::Autoload::AutoloadRelativeTest.autoload?(:D) + path.should_not == nil + path.should.end_with?("autoload_relative_a.rb") + end + + it "sets the autoload constant in the constants table" do + ModuleSpecs::Autoload.autoload_relative :AutoloadRelativeTableTest, "fixtures/autoload_relative_a.rb" + ModuleSpecs::Autoload.should.const_defined?(:AutoloadRelativeTableTest, false) + end + + it "calls #to_path on non-String filenames" do + name = mock("autoload_relative mock") + name.should_receive(:to_path).and_return("fixtures/autoload_relative_a.rb") + ModuleSpecs::Autoload.autoload_relative :AutoloadRelativeToPath, name + ModuleSpecs::Autoload.autoload?(:AutoloadRelativeToPath).should_not == nil + end + + it "calls #to_str on non-String filenames" do + name = mock("autoload_relative mock") + name.should_receive(:to_str).and_return("fixtures/autoload_relative_a.rb") + ModuleSpecs::Autoload.autoload_relative :AutoloadRelativeToStr, name + ModuleSpecs::Autoload.autoload?(:AutoloadRelativeToStr).should_not == nil + end + + it "raises a TypeError if the filename argument is not a String or pathname" do + -> { + ModuleSpecs::Autoload.autoload_relative :AutoloadRelativeTypError, nil + }.should.raise(TypeError) + end + + it "raises a NameError if the constant name is not valid" do + -> { + ModuleSpecs::Autoload.autoload_relative :invalid_name, "fixtures/autoload_relative_a.rb" + }.should.raise(NameError) + end + + it "raises an ArgumentError if the constant name starts with a lowercase letter" do + -> { + ModuleSpecs::Autoload.autoload_relative :autoload, "fixtures/autoload_relative_a.rb" + }.should.raise(NameError) + end + + it "raises LoadError if called from eval without file context" do + -> { + ModuleSpecs::Autoload.module_eval('autoload_relative :EvalTest, "fixtures/autoload_relative_a.rb"') + }.should.raise(LoadError, /autoload_relative called without file context/) + end + + it "can autoload in instance_eval with a file context" do + path = nil + ModuleSpecs::Autoload.instance_eval(<<-CODE, __FILE__, __LINE__) + autoload_relative :InstanceEvalTest, "fixtures/autoload_relative_a.rb" + path = autoload?(:InstanceEvalTest) + CODE + path.should_not == nil + path.should.end_with?("autoload_relative_a.rb") + end + + it "resolves paths relative to the file where it's called" do + # Using fixtures/autoload_relative_a.rb which exists + ModuleSpecs::Autoload.autoload_relative :RelativePathTest, "fixtures/autoload_relative_a.rb" + path = ModuleSpecs::Autoload.autoload?(:RelativePathTest) + path.should.include?("fixtures") + path.should.end_with?("autoload_relative_a.rb") + end + + it "can load nested directory paths" do + ModuleSpecs::Autoload.autoload_relative :NestedPath, "fixtures/autoload_relative_a.rb" + path = ModuleSpecs::Autoload.autoload?(:NestedPath) + path.should_not == nil + File.exist?(path).should == true + end + + describe "interoperability with autoload?" do + it "returns the absolute path with autoload?" do + ModuleSpecs::Autoload.autoload_relative :QueryTest, "fixtures/autoload_relative_a.rb" + path = ModuleSpecs::Autoload.autoload?(:QueryTest) + # Should be an absolute path + Pathname.new(path).absolute?.should == true + end + end +end +end diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb index f17675846b..057237a92f 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 @@ -10,7 +9,7 @@ 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 @@ -18,16 +17,14 @@ describe "Module#autoload?" do ModuleSpecs::Autoload::Child.autoload?(:AnotherAutoload).should == "another_autoload.rb" end - ruby_version_is "2.7" do - 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 be_nil - 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 + 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 @@ -58,7 +55,7 @@ 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 @@ -74,7 +71,7 @@ describe "Module#autoload" do 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 @@ -85,7 +82,7 @@ describe "Module#autoload" do 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 @@ -95,7 +92,7 @@ describe "Module#autoload" do 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 @@ -134,7 +131,7 @@ describe "Module#autoload" do @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 @@ -147,7 +144,7 @@ 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 @@ -159,9 +156,9 @@ describe "Module#autoload" do 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 @@ -174,7 +171,7 @@ describe "Module#autoload" do ModuleSpecs::Autoload.autoload :S, filename @remove << :S - ModuleSpecs::Autoload.autoload?(:S).should be_nil + ModuleSpecs::Autoload.autoload?(:S).should == nil end it "retains the autoload even if the request to require fails" do @@ -185,7 +182,7 @@ describe "Module#autoload" do -> { require filename - }.should raise_error(LoadError) + }.should.raise(LoadError) ModuleSpecs::Autoload.autoload?(:NotThere).should == filename end @@ -208,7 +205,7 @@ describe "Module#autoload" do filename = fixture(__FILE__, "autoload_during_require_current_file.rb") require filename - ScratchPad.recorded.should be_nil + ScratchPad.recorded.should == nil end describe "interacting with defined?" do @@ -218,9 +215,9 @@ 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 @@ -238,7 +235,7 @@ 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 @@ -345,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") @@ -353,72 +373,97 @@ 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 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 - -> { 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 - -> { 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 @@ -427,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 - -> { 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 - -> { 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 - -> { 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 @@ -463,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 @@ -479,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 @@ -487,15 +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 - ruby_bug "[Bug #15780]", ""..."2.7" do - ModuleSpecs::Autoload.const_defined?("P").should be_true - end + 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 @@ -530,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 @@ -554,14 +597,41 @@ 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 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 -> { @@ -570,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 @@ -645,10 +714,25 @@ 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] 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 @@ -661,7 +745,7 @@ describe "Module#autoload" do end @remove << :W - ModuleSpecs::Autoload::W::Y.should be_kind_of(Class) + ModuleSpecs::Autoload::W::Y.should.is_a?(Class) ScratchPad.recorded.should == :loaded end @@ -676,7 +760,7 @@ 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 @@ -686,15 +770,15 @@ describe "Module#autoload" do -> { Kernel.require fixture(__FILE__, "autoload_self_during_require.rb") }.should_not complain(verbose: true) - ModuleSpecs::Autoload::AutoloadSelfDuringRequire.should be_kind_of(Class) + 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 be_kind_of(Class) - ModuleSpecs::Autoload::Foo::Baz.should be_kind_of(Class) + ModuleSpecs::Autoload::Foo::Bar.should.is_a?(Class) + ModuleSpecs::Autoload::Foo::Baz.should.is_a?(Class) ensure $LOAD_PATH.shift end @@ -707,19 +791,19 @@ describe "Module#autoload" do end it "raises an ArgumentError when an empty filename is given" do - -> { 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 - -> { 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 - -> { 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 - -> { 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 @@ -736,7 +820,7 @@ describe "Module#autoload" do mod2.autoload?(:T).should == filename mod1::T.should == :autoload_t - -> { 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 @@ -747,27 +831,27 @@ describe "Module#autoload" 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") - -> { 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") - -> { 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 FrozenError before setting the name" do frozen_module = Module.new.freeze - -> { frozen_module.autoload :Foo, @non_existent }.should raise_error(FrozenError) - frozen_module.should_not have_constant(:Foo) + -> { frozen_module.autoload :Foo, @non_existent }.should.raise(FrozenError) + frozen_module.should_not.const_defined?(:Foo) end end @@ -832,7 +916,7 @@ describe "Module#autoload" do t1_val.should == 1 t2_val.should == t1_val - t2_exc.should be_nil + t2_exc.should == nil end # https://bugs.ruby-lang.org/issues/10892 @@ -864,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 @@ -872,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 @@ -881,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 @@ -904,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 @@ -929,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 diff --git a/spec/ruby/core/module/class_variable_defined_spec.rb b/spec/ruby/core/module/class_variable_defined_spec.rb index c0f2072a37..dedce9589a 100644 --- a/spec/ruby/core/module/class_variable_defined_spec.rb +++ b/spec/ruby/core/module/class_variable_defined_spec.rb @@ -19,13 +19,13 @@ describe "Module#class_variable_defined?" do obj = mock("metaclass class variable") meta = obj.singleton_class meta.send :class_variable_set, :@@var, 1 - meta.send(:class_variable_defined?, :@@var).should be_true + meta.send(:class_variable_defined?, :@@var).should == true end it "returns false if the class variable is not defined in a metaclass" do obj = mock("metaclass class variable") meta = obj.singleton_class - meta.class_variable_defined?(:@@var).should be_false + meta.class_variable_defined?(:@@var).should == false end it "returns true if a class variables with the given name is defined in an included module" do @@ -44,11 +44,11 @@ describe "Module#class_variable_defined?" do -> { c.class_variable_defined?(:invalid_name) - }.should raise_error(NameError) + }.should.raise(NameError) -> { c.class_variable_defined?("@invalid_name") - }.should raise_error(NameError) + }.should.raise(NameError) end it "converts a non string/symbol name to string using to_str" do @@ -62,11 +62,11 @@ describe "Module#class_variable_defined?" do o = mock('123') -> { c.class_variable_defined?(o) - }.should raise_error(TypeError) + }.should.raise(TypeError) o.should_receive(:to_str).and_return(123) -> { c.class_variable_defined?(o) - }.should raise_error(TypeError) + }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/class_variable_get_spec.rb b/spec/ruby/core/module/class_variable_get_spec.rb index e5d06731ec..13f06cb94a 100644 --- a/spec/ruby/core/module/class_variable_get_spec.rb +++ b/spec/ruby/core/module/class_variable_get_spec.rb @@ -15,8 +15,8 @@ describe "Module#class_variable_get" do it "raises a NameError for a class variable named '@@'" do c = Class.new - -> { c.send(:class_variable_get, "@@") }.should raise_error(NameError) - -> { c.send(:class_variable_get, :"@@") }.should raise_error(NameError) + -> { c.send(:class_variable_get, "@@") }.should.raise(NameError) + -> { c.send(:class_variable_get, :"@@") }.should.raise(NameError) end it "raises a NameError for a class variables with the given name defined in an extended module" do @@ -24,7 +24,7 @@ describe "Module#class_variable_get" do c.extend ModuleSpecs::MVars -> { c.send(:class_variable_get, "@@mvar") - }.should raise_error(NameError) + }.should.raise(NameError) end it "returns class variables defined in the class body and accessed in the metaclass" do @@ -49,15 +49,15 @@ describe "Module#class_variable_get" do it "raises a NameError when an uninitialized class variable is accessed" do c = Class.new [:@@no_class_var, "@@no_class_var"].each do |cvar| - -> { c.send(:class_variable_get, cvar) }.should raise_error(NameError) + -> { c.send(:class_variable_get, cvar) }.should.raise(NameError) end end it "raises a NameError when the given name is not allowed" do c = Class.new - -> { c.send(:class_variable_get, :invalid_name) }.should raise_error(NameError) - -> { c.send(:class_variable_get, "@invalid_name") }.should raise_error(NameError) + -> { c.send(:class_variable_get, :invalid_name) }.should.raise(NameError) + -> { c.send(:class_variable_get, "@invalid_name") }.should.raise(NameError) end it "converts a non string/symbol name to string using to_str" do @@ -69,8 +69,8 @@ describe "Module#class_variable_get" do it "raises a TypeError when the given names can't be converted to strings using to_str" do c = Class.new { class_variable_set :@@class_var, "test" } o = mock('123') - -> { c.send(:class_variable_get, o) }.should raise_error(TypeError) + -> { c.send(:class_variable_get, o) }.should.raise(TypeError) o.should_receive(:to_str).and_return(123) - -> { c.send(:class_variable_get, o) }.should raise_error(TypeError) + -> { c.send(:class_variable_get, o) }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/class_variable_set_spec.rb b/spec/ruby/core/module/class_variable_set_spec.rb index 63f32f5389..a3d759767b 100644 --- a/spec/ruby/core/module/class_variable_set_spec.rb +++ b/spec/ruby/core/module/class_variable_set_spec.rb @@ -28,10 +28,10 @@ describe "Module#class_variable_set" do it "raises a FrozenError when self is frozen" do -> { Class.new.freeze.send(:class_variable_set, :@@test, "test") - }.should raise_error(FrozenError) + }.should.raise(FrozenError) -> { Module.new.freeze.send(:class_variable_set, :@@test, "test") - }.should raise_error(FrozenError) + }.should.raise(FrozenError) end it "raises a NameError when the given name is not allowed" do @@ -39,10 +39,10 @@ describe "Module#class_variable_set" do -> { c.send(:class_variable_set, :invalid_name, "test") - }.should raise_error(NameError) + }.should.raise(NameError) -> { c.send(:class_variable_set, "@invalid_name", "test") - }.should raise_error(NameError) + }.should.raise(NameError) end it "converts a non string/symbol name to string using to_str" do @@ -55,8 +55,8 @@ describe "Module#class_variable_set" do it "raises a TypeError when the given names can't be converted to strings using to_str" do c = Class.new { class_variable_set :@@class_var, "test" } o = mock('123') - -> { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError) + -> { c.send(:class_variable_set, o, "test") }.should.raise(TypeError) o.should_receive(:to_str).and_return(123) - -> { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError) + -> { c.send(:class_variable_set, o, "test") }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/class_variables_spec.rb b/spec/ruby/core/module/class_variables_spec.rb index fd7aa93aa8..9529df48ae 100644 --- a/spec/ruby/core/module/class_variables_spec.rb +++ b/spec/ruby/core/module/class_variables_spec.rb @@ -3,8 +3,8 @@ require_relative 'fixtures/classes' describe "Module#class_variables" do it "returns an Array with the names of class variables of self" do - ModuleSpecs::ClassVars::A.class_variables.should include(:@@a_cvar) - ModuleSpecs::ClassVars::M.class_variables.should include(:@@m_cvar) + ModuleSpecs::ClassVars::A.class_variables.should.include?(:@@a_cvar) + ModuleSpecs::ClassVars::M.class_variables.should.include?(:@@m_cvar) end it "returns an Array of Symbols of class variable names defined in a metaclass" do @@ -15,12 +15,20 @@ describe "Module#class_variables" do end it "returns an Array with names of class variables defined in metaclasses" do - ModuleSpecs::CVars.class_variables.should include(:@@cls, :@@meta) + ModuleSpecs::CVars.class_variables.to_set.should >= Set[:@@cls, :@@meta] end it "does not return class variables defined in extended modules" do c = Class.new c.extend ModuleSpecs::MVars - c.class_variables.should_not include(:@@mvar) + c.class_variables.should_not.include?(:@@mvar) + end + + it "returns the correct class variables when inherit is given" do + ModuleSpecs::SubCVars.class_variables(false).should == [:@@sub] + ModuleSpecs::SubCVars.new.singleton_class.class_variables(false).should == [] + + ModuleSpecs::SubCVars.class_variables(true).should == [:@@sub, :@@cls, :@@meta] + ModuleSpecs::SubCVars.new.singleton_class.class_variables(true).should == [:@@sub, :@@cls, :@@meta] end end diff --git a/spec/ruby/core/module/const_added_spec.rb b/spec/ruby/core/module/const_added_spec.rb new file mode 100644 index 0000000000..b60af7a2e8 --- /dev/null +++ b/spec/ruby/core/module/const_added_spec.rb @@ -0,0 +1,238 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' +require_relative 'fixtures/const_added' + +describe "Module#const_added" do + it "is a private instance method" do + Module.private_instance_methods(false).should.include?(: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 + + parent = Class.new do + def self.inherited(_) + ScratchPad << :inherited + end + end + + class mod::C < parent; 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 + + class parent::C < parent; end + + ScratchPad.recorded.should == [:C, parent] + end + + 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 + + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + TEST = 1 + RUBY + + 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 + + it "is called when a new module is defined under self" do + ScratchPad.record [] + + mod = Module.new do + def self.const_added(name) + ScratchPad << name + end + end + + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + module SubModule + end + + module SubModule + end + RUBY + + 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 + + module self::B + end + end + end + + ScratchPad.recorded.should == [:A, :B] + ModuleSpecs::ConstAddedSpecs.send :remove_const, :NamedModule + end + + 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 + + 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 + + class self::A + def self.const_added(name) + ScratchPad << name + end + + class self::B + end + end + end + + ScratchPad.recorded.should == [:A, :B] + ModuleSpecs::ConstAddedSpecs.send :remove_const, :NamedModuleB + end + + it "is called when an autoload is defined" do + ScratchPad.record [] + + mod = Module.new do + def self.const_added(name) + ScratchPad << name + end + end + + mod.autoload :Autoload, "foo" + ScratchPad.recorded.should == [:Autoload] + end + + it "is called with a precise caller location with the line of definition" do + ScratchPad.record [] + + mod = Module.new do + def self.const_added(name) + location = caller_locations(1, 1)[0] + ScratchPad << location.lineno + end + end + + line = __LINE__ + mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1) + TEST = 1 + + module SubModule + end + + class SubClass + end + RUBY + + mod.const_set(:CONST_SET, 1) + + 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 = 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 [] + + 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 0673adca3d..9d973c2b2b 100644 --- a/spec/ruby/core/module/const_defined_spec.rb +++ b/spec/ruby/core/module/const_defined_spec.rb @@ -14,30 +14,40 @@ describe "Module#const_defined?" do it "returns true if the constant is defined in the receiver's superclass" do # CS_CONST4 is defined in the superclass of ChildA - ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4).should be_true + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4).should == true end - it "returns true if the constant is defined in a mixed-in module of the receiver" do + it "returns true if the constant is defined in a mixed-in module of the receiver's parent" do # CS_CONST10 is defined in a module included by ChildA - ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST10).should be_true + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST10).should == true + end + + it "returns true if the constant is defined in a mixed-in module (with prepends) of the receiver" do + # CS_CONST11 is defined in the module included by ContainerPrepend + ConstantSpecs::ContainerPrepend.const_defined?(:CS_CONST11).should == true end it "returns true if the constant is defined in Object and the receiver is a module" do # CS_CONST1 is defined in Object - ConstantSpecs::ModuleA.const_defined?(:CS_CONST1).should be_true + ConstantSpecs::ModuleA.const_defined?(:CS_CONST1).should == true end it "returns true if the constant is defined in Object and the receiver is a class that has Object among its ancestors" do # CS_CONST1 is defined in Object - ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST1).should be_true + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST1).should == true end it "returns false if the constant is defined in the receiver's superclass and the inherit flag is false" do - ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, false).should be_false + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, false).should == false end it "returns true if the constant is defined in the receiver's superclass and the inherit flag is true" do - ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, true).should be_true + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, true).should == true + end + + it "coerces the inherit flag to a boolean" do + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, nil).should == false + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, :true).should == true end it "returns true if the given String names a constant defined in the receiver" do @@ -48,21 +58,23 @@ describe "Module#const_defined?" do end it "returns true when passed a constant name with unicode characters" do - ConstantUnicodeSpecs.const_defined?("CS_CONSTλ").should be_true + ConstantUnicodeSpecs.const_defined?("CS_CONSTλ").should == true end it "returns true when passed a constant name with EUC-JP characters" do str = "CS_CONSTλ".encode("euc-jp") ConstantSpecs.const_set str, 1 - ConstantSpecs.const_defined?(str).should be_true + ConstantSpecs.const_defined?(str).should == 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 # The following constant isn't defined at all. - ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4726).should be_false + ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4726).should == false # DETACHED_CONSTANT is defined in ConstantSpecs::Detached, which isn't # included by or inherited from ParentA - ConstantSpecs::ParentA.const_defined?(:DETACHED_CONSTANT).should be_false + ConstantSpecs::ParentA.const_defined?(:DETACHED_CONSTANT).should == false end it "does not call #const_missing if the constant is not defined in the receiver" do @@ -70,54 +82,67 @@ describe "Module#const_defined?" do ConstantSpecs::ClassA.const_defined?(:CS_CONSTX).should == false end - it "calls #to_str to convert the given name to a String" do - name = mock("ClassA") - name.should_receive(:to_str).and_return("ClassA") - ConstantSpecs.const_defined?(name).should == true + describe "converts the given name to a String using #to_str" do + it "calls #to_str to convert the given name to a String" do + name = mock("ClassA") + name.should_receive(:to_str).and_return("ClassA") + ConstantSpecs.const_defined?(name).should == true + end + + it "raises a TypeError if the given name can't be converted to a String" do + -> { ConstantSpecs.const_defined?(nil) }.should.raise(TypeError) + -> { ConstantSpecs.const_defined?([]) }.should.raise(TypeError) + end + + it "raises a NoMethodError if the given argument raises a NoMethodError during type coercion to a String" do + name = mock("classA") + name.should_receive(:to_str).and_raise(NoMethodError) + -> { ConstantSpecs.const_defined?(name) }.should.raise(NoMethodError) + end end it "special cases Object and checks it's included Modules" do - Object.const_defined?(:CS_CONST10).should be_true + Object.const_defined?(:CS_CONST10).should == true end it "returns true for toplevel constant when the name begins with '::'" do - ConstantSpecs.const_defined?("::Array").should be_true + ConstantSpecs.const_defined?("::Array").should == true end it "returns true when passed a scoped constant name" do - ConstantSpecs.const_defined?("ClassC::CS_CONST1").should be_true + ConstantSpecs.const_defined?("ClassC::CS_CONST1").should == true end it "returns true when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is default" do - ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2").should be_true + ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2").should == true end it "returns true when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is true" do - ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2", true).should be_true + ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2", true).should == true end it "returns false when passed a scoped constant name for a constant in the inheritance hierarchy and the inherited flag is false" do - ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2", false).should be_false + ConstantSpecs::ClassD.const_defined?("ClassE::CS_CONST2", false).should == false end it "returns false when the name begins with '::' and the toplevel constant does not exist" do - ConstantSpecs.const_defined?("::Name").should be_false + ConstantSpecs.const_defined?("::Name").should == false end it "raises a NameError if the name does not start with a capital letter" do - -> { ConstantSpecs.const_defined? "name" }.should raise_error(NameError) + -> { ConstantSpecs.const_defined? "name" }.should.raise(NameError) end it "raises a NameError if the name starts with '_'" do - -> { ConstantSpecs.const_defined? "__CONSTX__" }.should raise_error(NameError) + -> { ConstantSpecs.const_defined? "__CONSTX__" }.should.raise(NameError) end it "raises a NameError if the name starts with '@'" do - -> { ConstantSpecs.const_defined? "@Name" }.should raise_error(NameError) + -> { ConstantSpecs.const_defined? "@Name" }.should.raise(NameError) end it "raises a NameError if the name starts with '!'" do - -> { ConstantSpecs.const_defined? "!Name" }.should raise_error(NameError) + -> { ConstantSpecs.const_defined? "!Name" }.should.raise(NameError) end it "returns true or false for the nested name" do @@ -130,15 +155,15 @@ describe "Module#const_defined?" do it "raises a NameError if the name contains non-alphabetic characters except '_'" do ConstantSpecs.const_defined?("CS_CONSTX").should == false - -> { ConstantSpecs.const_defined? "Name=" }.should raise_error(NameError) - -> { ConstantSpecs.const_defined? "Name?" }.should raise_error(NameError) + -> { ConstantSpecs.const_defined? "Name=" }.should.raise(NameError) + -> { ConstantSpecs.const_defined? "Name?" }.should.raise(NameError) end it "raises a TypeError if conversion to a String by calling #to_str fails" do name = mock('123') - -> { ConstantSpecs.const_defined? name }.should raise_error(TypeError) + -> { ConstantSpecs.const_defined? name }.should.raise(TypeError) name.should_receive(:to_str).and_return(123) - -> { ConstantSpecs.const_defined? name }.should raise_error(TypeError) + -> { ConstantSpecs.const_defined? name }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/const_get_spec.rb b/spec/ruby/core/module/const_get_spec.rb index 0a6702903f..68b5594faa 100644 --- a/spec/ruby/core/module/const_get_spec.rb +++ b/spec/ruby/core/module/const_get_spec.rb @@ -9,28 +9,28 @@ describe "Module#const_get" do end it "raises a NameError if no constant is defined in the search path" do - -> { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError) + -> { ConstantSpecs.const_get :CS_CONSTX }.should.raise(NameError) end it "raises a NameError with the not found constant symbol" do error_inspection = -> e { e.name.should == :CS_CONSTX } - -> { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError, &error_inspection) + -> { ConstantSpecs.const_get :CS_CONSTX }.should.raise(NameError, &error_inspection) end it "raises a NameError if the name does not start with a capital letter" do - -> { ConstantSpecs.const_get "name" }.should raise_error(NameError) + -> { ConstantSpecs.const_get "name" }.should.raise(NameError) end it "raises a NameError if the name starts with a non-alphabetic character" do - -> { ConstantSpecs.const_get "__CONSTX__" }.should raise_error(NameError) - -> { ConstantSpecs.const_get "@CS_CONST1" }.should raise_error(NameError) - -> { ConstantSpecs.const_get "!CS_CONST1" }.should raise_error(NameError) + -> { ConstantSpecs.const_get "__CONSTX__" }.should.raise(NameError) + -> { ConstantSpecs.const_get "@CS_CONST1" }.should.raise(NameError) + -> { ConstantSpecs.const_get "!CS_CONST1" }.should.raise(NameError) end it "raises a NameError if the name contains non-alphabetic characters except '_'" do Object.const_get("CS_CONST1").should == :const1 - -> { ConstantSpecs.const_get "CS_CONST1=" }.should raise_error(NameError) - -> { ConstantSpecs.const_get "CS_CONST1?" }.should raise_error(NameError) + -> { ConstantSpecs.const_get "CS_CONST1=" }.should.raise(NameError) + -> { ConstantSpecs.const_get "CS_CONST1?" }.should.raise(NameError) end it "calls #to_str to convert the given name to a String" do @@ -41,10 +41,10 @@ describe "Module#const_get" do it "raises a TypeError if conversion to a String by calling #to_str fails" do name = mock('123') - -> { ConstantSpecs.const_get(name) }.should raise_error(TypeError) + -> { ConstantSpecs.const_get(name) }.should.raise(TypeError) name.should_receive(:to_str).and_return(123) - -> { ConstantSpecs.const_get(name) }.should raise_error(TypeError) + -> { ConstantSpecs.const_get(name) }.should.raise(TypeError) end it "calls #const_missing on the receiver if unable to locate the constant" do @@ -55,21 +55,21 @@ describe "Module#const_get" do it "does not search the singleton class of a Class or Module" do -> do ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST14) - end.should raise_error(NameError) - -> { ConstantSpecs.const_get(:CS_CONST14) }.should raise_error(NameError) + end.should.raise(NameError) + -> { ConstantSpecs.const_get(:CS_CONST14) }.should.raise(NameError) end it "does not search the containing scope" do ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST20).should == :const20_2 -> do ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST5) - end.should raise_error(NameError) + end.should.raise(NameError) end it "raises a NameError if the constant is defined in the receiver's superclass and the inherit flag is false" do -> do ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST4, false) - end.should raise_error(NameError) + end.should.raise(NameError) end it "searches into the receiver superclasses if the inherit flag is true" do @@ -79,41 +79,59 @@ describe "Module#const_get" do it "raises a NameError when the receiver is a Module, the constant is defined at toplevel and the inherit flag is false" do -> do ConstantSpecs::ModuleA.const_get(:CS_CONST1, false) - end.should raise_error(NameError) + end.should.raise(NameError) end it "raises a NameError when the receiver is a Class, the constant is defined at toplevel and the inherit flag is false" do -> do ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST1, false) - end.should raise_error(NameError) + end.should.raise(NameError) + end + + it "coerces the inherit flag to a boolean" do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST4, :true).should == :const4 + + -> do + ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST1, nil) + end.should.raise(NameError) end it "accepts a toplevel scope qualifier" do ConstantSpecs.const_get("::CS_CONST1").should == :const1 end + it "accepts a toplevel scope qualifier when inherit is false" do + ConstantSpecs.const_get("::CS_CONST1", false).should == :const1 + -> { ConstantSpecs.const_get("CS_CONST1", false) }.should.raise(NameError) + end + + it "returns a constant whose module is defined the toplevel" do + ConstantSpecs.const_get("ConstantSpecsTwo::Foo").should == :cs_two_foo + ConstantSpecsThree.const_get("ConstantSpecsTwo::Foo").should == :cs_three_foo + end + it "accepts a scoped constant name" do ConstantSpecs.const_get("ClassA::CS_CONST10").should == :const10_10 end it "raises a NameError if the name includes two successive scope separators" do - -> { ConstantSpecs.const_get("ClassA::::CS_CONST10") }.should raise_error(NameError) + -> { ConstantSpecs.const_get("ClassA::::CS_CONST10") }.should.raise(NameError) end it "raises a NameError if only '::' is passed" do - -> { ConstantSpecs.const_get("::") }.should raise_error(NameError) + -> { ConstantSpecs.const_get("::") }.should.raise(NameError) end it "raises a NameError if a Symbol has a toplevel scope qualifier" do - -> { ConstantSpecs.const_get(:'::CS_CONST1') }.should raise_error(NameError) + -> { ConstantSpecs.const_get(:'::CS_CONST1') }.should.raise(NameError) end it "raises a NameError if a Symbol is a scoped constant name" do - -> { ConstantSpecs.const_get(:'ClassA::CS_CONST10') }.should raise_error(NameError) + -> { ConstantSpecs.const_get(:'ClassA::CS_CONST10') }.should.raise(NameError) 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 @@ -132,6 +150,10 @@ describe "Module#const_get" do Object.const_get('CSAutoloadD::InnerModule').name.should == 'CSAutoloadD::InnerModule' end + it "raises a NameError when the nested constant does not exist on the module but exists in Object" do + -> { Object.const_get('ConstantSpecs::CS_CONST1') }.should.raise(NameError) + end + describe "with statically assigned constants" do it "searches the immediate class or module first" do ConstantSpecs::ClassA.const_get(:CS_CONST10).should == :const10_10 @@ -180,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 @@ -224,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_missing_spec.rb b/spec/ruby/core/module/const_missing_spec.rb index 742218281c..80a2caccab 100644 --- a/spec/ruby/core/module/const_missing_spec.rb +++ b/spec/ruby/core/module/const_missing_spec.rb @@ -13,7 +13,7 @@ describe "Module#const_missing" do it "raises NameError and includes the name of the value that wasn't found" do -> { ConstantSpecs.const_missing("HelloMissing") - }.should raise_error(NameError, /ConstantSpecs::HelloMissing/) + }.should.raise(NameError, /ConstantSpecs::HelloMissing/) end it "raises NameError and does not include toplevel Object" do diff --git a/spec/ruby/core/module/const_set_spec.rb b/spec/ruby/core/module/const_set_spec.rb index b537d3f133..aa3c6bbcfc 100644 --- a/spec/ruby/core/module/const_set_spec.rb +++ b/spec/ruby/core/module/const_set_spec.rb @@ -8,32 +8,29 @@ 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 - ruby_version_is ""..."3.0" do - it "does not set the name of a module scoped by an anonymous module" do - a, b = Module.new, Module.new - a.const_set :B, b - b.name.should be_nil - end - end - - ruby_version_is "3.0" do - it "sets the name of a module scoped by an anonymous module" do - a, b = Module.new, Module.new - a.const_set :B, b - b.name.should.end_with? '::B' - end + it "sets the name of a module scoped by an anonymous module" do + a, b = Module.new, Module.new + a.const_set :B, b + b.name.should.end_with? '::B' end it "sets the name of contained modules when assigning a toplevel anonymous module" do @@ -48,23 +45,27 @@ 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 - -> { ConstantSpecs.const_set "name", 1 }.should raise_error(NameError) + -> { ConstantSpecs.const_set "name", 1 }.should.raise(NameError) end it "raises a NameError if the name starts with a non-alphabetic character" do - -> { ConstantSpecs.const_set "__CONSTX__", 1 }.should raise_error(NameError) - -> { ConstantSpecs.const_set "@Name", 1 }.should raise_error(NameError) - -> { ConstantSpecs.const_set "!Name", 1 }.should raise_error(NameError) - -> { ConstantSpecs.const_set "::Name", 1 }.should raise_error(NameError) + -> { ConstantSpecs.const_set "__CONSTX__", 1 }.should.raise(NameError) + -> { ConstantSpecs.const_set "@Name", 1 }.should.raise(NameError) + -> { ConstantSpecs.const_set "!Name", 1 }.should.raise(NameError) + -> { ConstantSpecs.const_set "::Name", 1 }.should.raise(NameError) end it "raises a NameError if the name contains non-alphabetic characters except '_'" 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) + -> { ConstantSpecs.const_set "Name=", 1 }.should.raise(NameError) + -> { ConstantSpecs.const_set "Name?", 1 }.should.raise(NameError) + ensure + ConstantSpecs.send(:remove_const, :CS_CONST404) end it "calls #to_str to convert the given name to a String" do @@ -72,14 +73,16 @@ 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 name = mock('123') - -> { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError) + -> { ConstantSpecs.const_set name, 1 }.should.raise(TypeError) name.should_receive(:to_str).and_return(123) - -> { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError) + -> { ConstantSpecs.const_set name, 1 }.should.raise(TypeError) end describe "when overwriting an existing constant" do @@ -101,15 +104,14 @@ describe "Module#const_set" do mod.const_get(:Foo).should == 1 end - it "does not warn if the previous value was undefined" do + it "does not warn after a failed autoload" do path = fixture(__FILE__, "autoload_o.rb") ScratchPad.record [] mod = Module.new mod.autoload :Foo, path - -> { mod::Foo }.should raise_error(NameError) + -> { mod::Foo }.should.raise(NameError) - mod.should have_constant(:Foo) mod.const_defined?(:Foo).should == false mod.autoload?(:Foo).should == nil @@ -136,8 +138,8 @@ describe "Module#const_set" do end it "raises a FrozenError before setting the name" do - -> { @frozen.const_set @name, nil }.should raise_error(FrozenError) - @frozen.should_not have_constant(@name) + -> { @frozen.const_set @name, nil }.should.raise(FrozenError) + @frozen.should_not.const_defined?(@name) end end end diff --git a/spec/ruby/core/module/const_source_location_spec.rb b/spec/ruby/core/module/const_source_location_spec.rb index 9e1f2c1c49..e6cef727e2 100644 --- a/spec/ruby/core/module/const_source_location_spec.rb +++ b/spec/ruby/core/module/const_source_location_spec.rb @@ -6,214 +6,275 @@ describe "Module#const_source_location" do @constants_fixture_path = File.expand_path('../../fixtures/constants.rb', __dir__) end - ruby_version_is "2.7" do - describe "with dynamically assigned constants" do - it "searches a path in the immediate class or module first" do - ConstantSpecs::ClassA::CSL_CONST301 = :const301_1 - ConstantSpecs::ClassA.const_source_location(:CSL_CONST301).should == [__FILE__, __LINE__ - 1] + describe "with dynamically assigned constants" do + it "searches a path in the immediate class or module first" do + ConstantSpecs::ClassA::CSL_CONST301 = :const301_1 + ConstantSpecs::ClassA.const_source_location(:CSL_CONST301).should == [__FILE__, __LINE__ - 1] + + ConstantSpecs::ModuleA::CSL_CONST301 = :const301_2 + ConstantSpecs::ModuleA.const_source_location(:CSL_CONST301).should == [__FILE__, __LINE__ - 1] + + ConstantSpecs::ParentA::CSL_CONST301 = :const301_3 + ConstantSpecs::ParentA.const_source_location(:CSL_CONST301).should == [__FILE__, __LINE__ - 1] + + 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 - ConstantSpecs::ModuleA::CSL_CONST301 = :const301_2 - ConstantSpecs::ModuleA.const_source_location(:CSL_CONST301).should == [__FILE__, __LINE__ - 1] + 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 - ConstantSpecs::ParentA::CSL_CONST301 = :const301_3 - ConstantSpecs::ParentA.const_source_location(:CSL_CONST301).should == [__FILE__, __LINE__ - 1] + 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 - ConstantSpecs::ContainerA::ChildA::CSL_CONST301 = :const301_5 - ConstantSpecs::ContainerA::ChildA.const_source_location(:CSL_CONST301).should == [__FILE__, __LINE__ - 1] - 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 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] - 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 "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] - 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 "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] - 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 "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] - end + it "returns path to the updated value of a constant" do + ConstantSpecs::ClassB::CSL_CONST309 = :const309_1 + ConstantSpecs::ClassB.const_source_location(:CSL_CONST309).should == [__FILE__, __LINE__ - 1] - 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] - end + -> { + 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 - 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] - end + describe "with statically assigned constants" do + it "works for the module and class keywords" do + ConstantSpecs.const_source_location(:ModuleB).should == [@constants_fixture_path, ConstantSpecs::ModuleB::LINE] + ConstantSpecs.const_source_location(:ClassA).should == [@constants_fixture_path, ConstantSpecs::ClassA::LINE] + end - it "returns path to the updated value of a constant" do - ConstantSpecs::ClassB::CSL_CONST309 = :const309_1 - ConstantSpecs::ClassB.const_source_location(:CSL_CONST309).should == [__FILE__, __LINE__ - 1] + it "searches location path the immediate class or module first" do + ConstantSpecs::ClassA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CONST10_LINE] + ConstantSpecs::ModuleA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ModuleA::CS_CONST10_LINE] + ConstantSpecs::ParentA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ParentA::CS_CONST10_LINE] + ConstantSpecs::ContainerA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ContainerA::CS_CONST10_LINE] + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ContainerA::ChildA::CS_CONST10_LINE] + end - -> { - ConstantSpecs::ClassB::CSL_CONST309 = :const309_2 - }.should complain(/already initialized constant/) - ConstantSpecs::ClassB.const_source_location(:CSL_CONST309).should == [__FILE__, __LINE__ - 2] - end + it "searches location path a module included in the immediate class before the superclass" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST15).should == [@constants_fixture_path, ConstantSpecs::ModuleC::CS_CONST15_LINE] end - describe "with statically assigned constants" do - it "searches location path the immediate class or module first" do - ConstantSpecs::ClassA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CONST10_LINE] - ConstantSpecs::ModuleA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ModuleA::CS_CONST10_LINE] - ConstantSpecs::ParentA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ParentA::CS_CONST10_LINE] - ConstantSpecs::ContainerA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ContainerA::CS_CONST10_LINE] - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ContainerA::ChildA::CS_CONST10_LINE] - end + it "searches location path the superclass before a module included in the superclass" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST11).should == [@constants_fixture_path, ConstantSpecs::ParentA::CS_CONST11_LINE] + end - it "searches location path a module included in the immediate class before the superclass" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST15).should == [@constants_fixture_path, ConstantSpecs::ModuleC::CS_CONST15_LINE] - end + it "searches location path a module included in the superclass" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST12).should == [@constants_fixture_path, ConstantSpecs::ModuleB::CS_CONST12_LINE] + end - it "searches location path the superclass before a module included in the superclass" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST11).should == [@constants_fixture_path, ConstantSpecs::ParentA::CS_CONST11_LINE] - end + it "searches location path the superclass chain" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST13).should == [@constants_fixture_path, ConstantSpecs::ModuleA::CS_CONST13_LINE] + end - it "searches location path a module included in the superclass" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST12).should == [@constants_fixture_path, ConstantSpecs::ModuleB::CS_CONST12_LINE] - end + it "returns location path a toplevel constant when the receiver is a Class" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_LINE] + end - it "searches location path the superclass chain" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST13).should == [@constants_fixture_path, ConstantSpecs::ModuleA::CS_CONST13_LINE] - end + it "returns location path a toplevel constant when the receiver is a Module" do + ConstantSpecs.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_LINE] + ConstantSpecs::ModuleA.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_LINE] + end + end - it "returns location path a toplevel constant when the receiver is a Class" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_LINE] - end + it "return empty path if constant defined in C code" do + Object.const_source_location(:String).should == [] + end - it "returns location path a toplevel constant when the receiver is a Module" do - ConstantSpecs.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_LINE] - ConstantSpecs::ModuleA.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_LINE] - end - end + it "accepts a String or Symbol name" do + Object.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_LINE] + Object.const_source_location("CS_CONST1").should == [@constants_fixture_path, CS_CONST1_LINE] + end - it "return empty path if constant defined in C code" do - Object.const_source_location(:String).should == [] - end + it "returns nil if no constant is defined in the search path" do + ConstantSpecs.const_source_location(:CS_CONSTX).should == nil + end - it "accepts a String or Symbol name" do - Object.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_LINE] - Object.const_source_location("CS_CONST1").should == [@constants_fixture_path, CS_CONST1_LINE] - end + it "raises a NameError if the name does not start with a capital letter" do + -> { ConstantSpecs.const_source_location "name" }.should.raise(NameError) + end - it "returns nil if no constant is defined in the search path" do - ConstantSpecs.const_source_location(:CS_CONSTX).should == nil - end + it "raises a NameError if the name starts with a non-alphabetic character" do + -> { ConstantSpecs.const_source_location "__CONSTX__" }.should.raise(NameError) + -> { ConstantSpecs.const_source_location "@CS_CONST1" }.should.raise(NameError) + -> { ConstantSpecs.const_source_location "!CS_CONST1" }.should.raise(NameError) + end - it "raises a NameError if the name does not start with a capital letter" do - -> { ConstantSpecs.const_source_location "name" }.should raise_error(NameError) - end + it "raises a NameError if the name contains non-alphabetic characters except '_'" do + Object.const_source_location("CS_CONST1").should == [@constants_fixture_path, CS_CONST1_LINE] + -> { ConstantSpecs.const_source_location "CS_CONST1=" }.should.raise(NameError) + -> { ConstantSpecs.const_source_location "CS_CONST1?" }.should.raise(NameError) + end - it "raises a NameError if the name starts with a non-alphabetic character" do - -> { ConstantSpecs.const_source_location "__CONSTX__" }.should raise_error(NameError) - -> { ConstantSpecs.const_source_location "@CS_CONST1" }.should raise_error(NameError) - -> { ConstantSpecs.const_source_location "!CS_CONST1" }.should raise_error(NameError) - end + it "calls #to_str to convert the given name to a String" do + name = mock("ClassA") + name.should_receive(:to_str).and_return("ClassA") + ConstantSpecs.const_source_location(name).should == [@constants_fixture_path, ConstantSpecs::ClassA::LINE] + end - it "raises a NameError if the name contains non-alphabetic characters except '_'" do - Object.const_source_location("CS_CONST1").should == [@constants_fixture_path, CS_CONST1_LINE] - -> { ConstantSpecs.const_source_location "CS_CONST1=" }.should raise_error(NameError) - -> { ConstantSpecs.const_source_location "CS_CONST1?" }.should raise_error(NameError) - end + it "raises a TypeError if conversion to a String by calling #to_str fails" do + name = mock('123') + -> { ConstantSpecs.const_source_location(name) }.should.raise(TypeError) - it "calls #to_str to convert the given name to a String" do - name = mock("ClassA") - name.should_receive(:to_str).and_return("ClassA") - ConstantSpecs.const_source_location(name).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CLASS_A_LINE] - end + name.should_receive(:to_str).and_return(123) + -> { ConstantSpecs.const_source_location(name) }.should.raise(TypeError) + end - it "raises a TypeError if conversion to a String by calling #to_str fails" do - name = mock('123') - -> { ConstantSpecs.const_source_location(name) }.should raise_error(TypeError) + it "does not search the singleton class of a Class or Module" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST14).should == nil + ConstantSpecs.const_source_location(:CS_CONST14).should == nil + end - name.should_receive(:to_str).and_return(123) - -> { ConstantSpecs.const_source_location(name) }.should raise_error(TypeError) - end + it "does not search the containing scope" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST20).should == [@constants_fixture_path, ConstantSpecs::ParentA::CS_CONST20_LINE] + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST5) == nil + end - it "does not search the singleton class of a Class or Module" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST14).should == nil - ConstantSpecs.const_source_location(:CS_CONST14).should == nil - end + it "returns nil if the constant is defined in the receiver's superclass and the inherit flag is false" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST4, false).should == nil + end - it "does not search the containing scope" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST20).should == [@constants_fixture_path, ConstantSpecs::ParentA::CS_CONST20_LINE] - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST5) == nil - end + it "searches into the receiver superclasses if the inherit flag is true" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST4, true).should == [@constants_fixture_path, ConstantSpecs::ParentA::CS_CONST4_LINE] + end - it "returns nil if the constant is defined in the receiver's superclass and the inherit flag is false" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST4, false).should == nil - end + it "returns nil when the receiver is a Module, the constant is defined at toplevel and the inherit flag is false" do + ConstantSpecs::ModuleA.const_source_location(:CS_CONST1, false).should == nil + end - it "searches into the receiver superclasses if the inherit flag is true" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST4, true).should == [@constants_fixture_path, ConstantSpecs::ParentA::CS_CONST4_LINE] - end + it "returns nil when the receiver is a Class, the constant is defined at toplevel and the inherit flag is false" do + ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST1, false).should == nil + end - it "returns nil when the receiver is a Module, the constant is defined at toplevel and the inherit flag is false" do - ConstantSpecs::ModuleA.const_source_location(:CS_CONST1, false).should == nil - end + it "accepts a toplevel scope qualifier" do + ConstantSpecs.const_source_location("::CS_CONST1").should == [@constants_fixture_path, CS_CONST1_LINE] + end - it "returns nil when the receiver is a Class, the constant is defined at toplevel and the inherit flag is false" do - ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST1, false).should == nil - end + it "accepts a scoped constant name" do + ConstantSpecs.const_source_location("ClassA::CS_CONST10").should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CONST10_LINE] + end - it "accepts a toplevel scope qualifier" do - ConstantSpecs.const_source_location("::CS_CONST1").should == [@constants_fixture_path, CS_CONST1_LINE] - end + it "returns updated location from const_set" do + mod = Module.new + const_line = __LINE__ + 1 + mod.const_set :Foo, 1 + mod.const_source_location(:Foo).should == [__FILE__, const_line] + end - it "accepts a scoped constant name" do - ConstantSpecs.const_source_location("ClassA::CS_CONST10").should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CONST10_LINE] - end + it "raises a NameError if the name includes two successive scope separators" do + -> { ConstantSpecs.const_source_location("ClassA::::CS_CONST10") }.should.raise(NameError) + end - it "raises a NameError if the name includes two successive scope separators" do - -> { ConstantSpecs.const_source_location("ClassA::::CS_CONST10") }.should raise_error(NameError) - end + it "raises a NameError if only '::' is passed" do + -> { ConstantSpecs.const_source_location("::") }.should.raise(NameError) + end + + it "raises a NameError if a Symbol has a toplevel scope qualifier" do + -> { ConstantSpecs.const_source_location(:'::CS_CONST1') }.should.raise(NameError) + end + + it "raises a NameError if a Symbol is a scoped constant name" do + -> { ConstantSpecs.const_source_location(:'ClassA::CS_CONST10') }.should.raise(NameError) + end + + it "does search private constants path" do + ConstantSpecs.const_source_location(:CS_PRIVATE).should == [@constants_fixture_path, ConstantSpecs::CS_PRIVATE_LINE] + end - it "raises a NameError if only '::' is passed" do - -> { ConstantSpecs.const_source_location("::") }.should raise_error(NameError) + it "works for eval with a given line" do + c = Class.new do + eval('self::C = 1', nil, "foo", 100) end + c.const_source_location(:C).should == ["foo", 100] + end - it "raises a NameError if a Symbol has a toplevel scope qualifier" do - -> { ConstantSpecs.const_source_location(:'::CS_CONST1') }.should raise_error(NameError) + context 'autoload' do + before :all do + ConstantSpecs.autoload :CSL_CONST1, "#{__dir__}/notexisting.rb" + @line = __LINE__ - 1 end - it "raises a NameError if a Symbol is a scoped constant name" do - -> { ConstantSpecs.const_source_location(:'ClassA::CS_CONST10') }.should raise_error(NameError) + before :each do + @loaded_features = $".dup end - it "does search private constants path" do - ConstantSpecs.const_source_location(:CS_PRIVATE).should == [@constants_fixture_path, ConstantSpecs::CS_PRIVATE_LINE] + after :each do + $".replace @loaded_features end - context 'autoload' do - before :all do - ConstantSpecs.autoload :CSL_CONST1, "#{__dir__}/notexisting.rb" - @line = __LINE__ - 1 - end + it 'returns the autoload location while not resolved' do + ConstantSpecs.const_source_location('CSL_CONST1').should == [__FILE__, @line] + end - it 'returns the autoload location while not resolved' do - ConstantSpecs.const_source_location('CSL_CONST1').should == [__FILE__, @line] - end + it 'returns where the constant was resolved when resolved' do + file = fixture(__FILE__, 'autoload_location.rb') + ConstantSpecs.autoload :CONST_LOCATION, file + line = ConstantSpecs::CONST_LOCATION + ConstantSpecs.const_source_location('CONST_LOCATION').should == [file, line] + end - it 'returns where the constant was resolved when resolved' do - file = fixture(__FILE__, 'autoload_location.rb') - ConstantSpecs.autoload :CONST_LOCATION, file - line = ConstantSpecs::CONST_LOCATION - ConstantSpecs.const_source_location('CONST_LOCATION').should == [file, line] + 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 diff --git a/spec/ruby/core/module/constants_spec.rb b/spec/ruby/core/module/constants_spec.rb index fe95143872..f2f12761e3 100644 --- a/spec/ruby/core/module/constants_spec.rb +++ b/spec/ruby/core/module/constants_spec.rb @@ -13,12 +13,13 @@ describe "Module.constants" do it "returns an array of Symbol names" do # This in NOT an exhaustive list - Module.constants.should include(:Array, :Class, :Comparable, :Dir, + Module.constants.to_set.should >= Set[ + :Array, :Class, :Comparable, :Dir, :Enumerable, :ENV, :Exception, :FalseClass, :File, :Float, :Hash, :Integer, :IO, :Kernel, :Math, :Method, :Module, :NilClass, :Numeric, :Object, :Range, :Regexp, :String, - :Symbol, :Thread, :Time, :TrueClass) + :Symbol, :Thread, :Time, :TrueClass] end it "returns Module's constants when given a parameter" do @@ -74,6 +75,12 @@ describe "Module#constants" do it "returns only public constants" do ModuleSpecs::PrivConstModule.constants.should == [:PUBLIC_CONSTANT] end + + it "returns only constants starting with an uppercase letter" do + # e.g. fatal, IO::generic_readable and IO::generic_writable should not be returned by Module#constants + Object.constants.each { |c| c[0].should == c[0].upcase } + IO.constants.each { |c| c[0].should == c[0].upcase } + end end describe "Module#constants" do @@ -86,6 +93,6 @@ describe "Module#constants" do end it "includes names of constants defined after a module is included" do - ConstantSpecs::ContainerA.constants.should include(:CS_CONST251) + ConstantSpecs::ContainerA.constants.should.include?(:CS_CONST251) end end diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb index 49472c18e1..f838e2b85f 100644 --- a/spec/ruby/core/module/define_method_spec.rb +++ b/spec/ruby/core/module/define_method_spec.rb @@ -12,7 +12,7 @@ describe "passed { |a, b = 1| } creates a method that" do end it "raises an ArgumentError when passed zero arguments" do - -> { @klass.new.m }.should raise_error(ArgumentError) + -> { @klass.new.m }.should.raise(ArgumentError) end it "has a default value for b when passed one argument" do @@ -24,7 +24,7 @@ describe "passed { |a, b = 1| } creates a method that" do end it "raises an ArgumentError when passed three arguments" do - -> { @klass.new.m(1, 2, 3) }.should raise_error(ArgumentError) + -> { @klass.new.m(1, 2, 3) }.should.raise(ArgumentError) end end @@ -47,7 +47,7 @@ describe "Module#define_method when given an UnboundMethod" do end define_method(:another_test_method, instance_method(:test_method)) end - klass.new.should have_method(:another_test_method) + klass.new.should.respond_to?(:another_test_method) end describe "defining a method on a singleton class" do @@ -83,11 +83,28 @@ describe "Module#define_method when given an UnboundMethod" do define_method :piggy, instance_method(:ziggy) end - -> { foo.new.ziggy }.should raise_error(NoMethodError) + -> { foo.new.ziggy }.should.raise(NoMethodError) foo.new.piggy.should == 'piggy' end end +describe "Module#define_method" do + describe "when the default definee is not the same as the module" do + it "sets the visibility of the method to public" do + klass = Class.new + class << klass + private + define_method(:meta) do + define_method(:foo) { :foo } + end + end + + klass.send :meta + klass.new.foo.should == :foo + end + end +end + describe "Module#define_method when name is not a special private name" do describe "given an UnboundMethod" do describe "and called from the target module" do @@ -98,8 +115,8 @@ describe "Module#define_method when name is not a special private name" do define_method(:baz, ModuleSpecs::EmptyFooMethod) end - klass.should have_public_instance_method(:bar) - klass.should have_private_instance_method(:baz) + klass.public_instance_methods(false).should.include?(:bar) + klass.private_instance_methods(false).should.include?(:baz) end end @@ -112,10 +129,21 @@ describe "Module#define_method when name is not a special private name" do klass.send(:define_method, :baz, ModuleSpecs::EmptyFooMethod) end - klass.should have_public_instance_method(:bar) - klass.should have_public_instance_method(:baz) + klass.public_instance_methods(false).should.include?(:bar) + klass.public_instance_methods(false).should.include?(:baz) end end + + it "sets the method owner for a dynamically added method with a different original owner" do + mixin_module = Module.new do + def bar; end + end + + foo = Object.new + foo.singleton_class.define_method(:bar, mixin_module.instance_method(:bar)) + + foo.method(:bar).owner.should == foo.singleton_class + end end describe "passed a block" do @@ -127,8 +155,8 @@ describe "Module#define_method when name is not a special private name" do define_method(:baz) {} end - klass.should have_public_instance_method(:bar) - klass.should have_private_instance_method(:baz) + klass.public_instance_methods(false).should.include?(:bar) + klass.private_instance_methods(false).should.include?(:baz) end end @@ -141,8 +169,8 @@ describe "Module#define_method when name is not a special private name" do klass.send(:define_method, :baz) {} end - klass.should have_public_instance_method(:bar) - klass.should have_public_instance_method(:baz) + klass.public_instance_methods(false).should.include?(:bar) + klass.public_instance_methods(false).should.include?(:baz) end end end @@ -154,7 +182,7 @@ describe "Module#define_method when name is :initialize" do klass = Class.new do define_method(:initialize) { } end - klass.should have_private_instance_method(:initialize) + klass.private_instance_methods(false).should.include?(:initialize) end end @@ -165,7 +193,7 @@ describe "Module#define_method when name is :initialize" do end define_method(:initialize, instance_method(:test_method)) end - klass.should have_private_instance_method(:initialize) + klass.private_instance_methods(false).should.include?(:initialize) end end end @@ -202,24 +230,61 @@ describe "Module#define_method" do o.block_test2.should == o end + it "raises TypeError if name cannot converted to String" do + -> { + Class.new { define_method(1001, -> {}) } + }.should.raise(TypeError, /is not a symbol nor a string/) + + -> { + Class.new { define_method([], -> {}) } + }.should.raise(TypeError, /is not a symbol nor a string/) + end + + it "converts non-String name to String with #to_str" do + obj = Object.new + def obj.to_str() "foo" end + + new_class = Class.new { define_method(obj, -> { :called }) } + new_class.new.foo.should == :called + end + + it "raises TypeError when #to_str called on non-String name returns non-String value" do + obj = Object.new + def obj.to_str() [] end + + -> { + Class.new { define_method(obj, -> {}) } + }.should raise_consistent_error(TypeError, /can't convert Object into String/) + end + it "raises a TypeError when the given method is no Method/Proc" do -> { Class.new { define_method(:test, "self") } - }.should raise_error(TypeError) + }.should.raise(TypeError, "wrong argument type String (expected Proc/Method/UnboundMethod)") -> { Class.new { define_method(:test, 1234) } - }.should raise_error(TypeError) + }.should.raise(TypeError, "wrong argument type Integer (expected Proc/Method/UnboundMethod)") -> { Class.new { define_method(:test, nil) } - }.should raise_error(TypeError) + }.should.raise(TypeError, "wrong argument type NilClass (expected Proc/Method/UnboundMethod)") + end + + it "uses provided Method/Proc even if block is specified" do + new_class = Class.new do + define_method(:test, -> { :method_is_called }) do + :block_is_called + end + end + + new_class.new.test.should == :method_is_called end it "raises an ArgumentError when no block is given" do -> { Class.new { define_method(:test) } - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end it "does not use the caller block when no block is given" do @@ -232,7 +297,7 @@ describe "Module#define_method" do -> { o.define(:foo) { raise "not used" } - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end it "does not change the arity check style of the original proc" do @@ -242,13 +307,13 @@ describe "Module#define_method" do end obj = DefineMethodSpecClass.new - -> { obj.proc_style_test :arg }.should raise_error(ArgumentError) + -> { obj.proc_style_test :arg }.should.raise(ArgumentError) end it "raises a FrozenError if frozen" do -> { Class.new { freeze; define_method(:foo) {} } - }.should raise_error(FrozenError) + }.should.raise(FrozenError) end it "accepts a Method (still bound)" do @@ -261,13 +326,13 @@ describe "Module#define_method" do o = DefineMethodSpecClass.new o.data = :foo m = o.method(:inspect_data) - m.should be_an_instance_of(Method) + m.should.instance_of?(Method) klass = Class.new(DefineMethodSpecClass) klass.send(:define_method,:other_inspect, m) c = klass.new c.data = :bar c.other_inspect.should == "data is bar" - ->{o.other_inspect}.should raise_error(NoMethodError) + ->{o.other_inspect}.should.raise(NoMethodError) end it "raises a TypeError when a Method from a singleton class is defined on another class" do @@ -281,7 +346,7 @@ describe "Module#define_method" do -> { Class.new { define_method :bar, m } - }.should raise_error(TypeError, /can't bind singleton method to a different class/) + }.should.raise(TypeError, /can't bind singleton method to a different class/) end it "raises a TypeError when a Method from one class is defined on an unrelated class" do @@ -293,7 +358,7 @@ describe "Module#define_method" do -> { Class.new { define_method :bar, m } - }.should raise_error(TypeError) + }.should.raise(TypeError) end it "accepts an UnboundMethod from an attr_accessor method" do @@ -305,7 +370,7 @@ describe "Module#define_method" do o = DefineMethodSpecClass.new DefineMethodSpecClass.send(:undef_method, :accessor_method) - -> { o.accessor_method }.should raise_error(NoMethodError) + -> { o.accessor_method }.should.raise(NoMethodError) DefineMethodSpecClass.send(:define_method, :accessor_method, m) @@ -333,6 +398,14 @@ describe "Module#define_method" do object2.other_cool_method.should == "data is foo" end + it "accepts a proc from a Symbol" do + symbol_proc = :+.to_proc + klass = Class.new do + define_method :foo, &symbol_proc + end + klass.new.foo(1, 2).should == 3 + end + it "maintains the Proc's scope" do class DefineMethodByProcClass in_scope = true @@ -342,7 +415,7 @@ describe "Module#define_method" do end o = DefineMethodByProcClass.new - o.proc_test.should be_true + o.proc_test.should == true end it "accepts a String method name" do @@ -356,7 +429,7 @@ describe "Module#define_method" do end it "is a public method" do - Module.should have_public_instance_method(:define_method) + Module.public_instance_methods(false).should.include?(:define_method) end it "returns its symbol" do @@ -370,7 +443,7 @@ describe "Module#define_method" do klass = Class.new { define_method :bar, ModuleSpecs::UnboundMethodTest.instance_method(:foo) } - klass.new.should respond_to(:bar) + klass.new.should.respond_to?(:bar) end it "allows an UnboundMethod from a parent class to be defined on a child class" do @@ -378,7 +451,7 @@ describe "Module#define_method" do child = Class.new(parent) { define_method :baz, parent.instance_method(:foo) } - child.new.should respond_to(:baz) + child.new.should.respond_to?(:baz) end it "allows an UnboundMethod from a module to be defined on another unrelated module" do @@ -386,7 +459,7 @@ describe "Module#define_method" do define_method :bar, ModuleSpecs::UnboundMethodTest.instance_method(:foo) } klass = Class.new { include mod } - klass.new.should respond_to(:bar) + klass.new.should.respond_to?(:bar) end @@ -402,7 +475,10 @@ describe "Module#define_method" do ParentClass = Class.new { define_method(:foo) { :bar } } 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/) + }.should.raise(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 @@ -410,7 +486,7 @@ describe "Module#define_method" do DestinationClass = Class.new { define_method :bar, ModuleSpecs::InstanceMeth.instance_method(:foo) } - }.should raise_error(TypeError, /bind argument must be a subclass of ModuleSpecs::InstanceMeth/) + }.should.raise(TypeError, /bind argument must be a subclass of ModuleSpecs::InstanceMeth/) end it "raises a TypeError when an UnboundMethod from a singleton class is defined on another class" do @@ -424,7 +500,34 @@ describe "Module#define_method" do -> { Class.new { define_method :bar, m } - }.should raise_error(TypeError, /can't bind singleton method to a different class/) + }.should.raise(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(NoMethodError) end end @@ -441,11 +544,11 @@ describe "Module#define_method" do end it "raises an ArgumentError when passed one argument" do - -> { @klass.new.m 1 }.should raise_error(ArgumentError) + -> { @klass.new.m 1 }.should.raise(ArgumentError) end it "raises an ArgumentError when passed two arguments" do - -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError) + -> { @klass.new.m 1, 2 }.should.raise(ArgumentError) end end @@ -461,11 +564,11 @@ describe "Module#define_method" do end it "raises an ArgumentError when passed one argument" do - -> { @klass.new.m 1 }.should raise_error(ArgumentError) + -> { @klass.new.m 1 }.should.raise(ArgumentError) end it "raises an ArgumentError when passed two arguments" do - -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError) + -> { @klass.new.m 1, 2 }.should.raise(ArgumentError) end end @@ -477,21 +580,50 @@ describe "Module#define_method" do end it "raises an ArgumentError when passed zero arguments" do - -> { @klass.new.m }.should raise_error(ArgumentError) + -> { @klass.new.m }.should.raise(ArgumentError) end it "raises an ArgumentError when passed zero arguments and a block" do - -> { @klass.new.m { :computed } }.should raise_error(ArgumentError) + -> { @klass.new.m { :computed } }.should.raise(ArgumentError) end it "raises an ArgumentError when passed two arguments" do - -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError) + -> { @klass.new.m 1, 2 }.should.raise(ArgumentError) end it "receives the value passed as the argument when passed one argument" do @klass.new.m(1).should == 1 end + end + describe "passed { |a,| } creates a method that" do + before :each do + @klass = Class.new do + define_method(:m) { |a,| a } + end + end + + it "raises an ArgumentError when passed zero arguments" do + -> { @klass.new.m }.should.raise(ArgumentError) + end + + it "raises an ArgumentError when passed zero arguments and a block" do + -> { @klass.new.m { :computed } }.should.raise(ArgumentError) + end + + it "raises an ArgumentError when passed two arguments" do + -> { @klass.new.m 1, 2 }.should.raise(ArgumentError) + end + + it "receives the value passed as the argument when passed one argument" do + @klass.new.m(1).should == 1 + end + + it "does not destructure the passed argument" do + @klass.new.m([1, 2]).should == [1, 2] + # for comparison: + proc { |a,| a }.call([1, 2]).should == 1 + end end describe "passed { |*a| } creates a method that" do @@ -522,7 +654,7 @@ describe "Module#define_method" do end it "raises an ArgumentError when passed zero arguments" do - -> { @klass.new.m }.should raise_error(ArgumentError) + -> { @klass.new.m }.should.raise(ArgumentError) end it "returns the value computed by the block when passed one argument" do @@ -550,19 +682,19 @@ describe "Module#define_method" do end it "raises an ArgumentError when passed zero arguments" do - -> { @klass.new.m }.should raise_error(ArgumentError) + -> { @klass.new.m }.should.raise(ArgumentError) end it "raises an ArgumentError when passed one argument" do - -> { @klass.new.m 1 }.should raise_error(ArgumentError) + -> { @klass.new.m 1 }.should.raise(ArgumentError) end it "raises an ArgumentError when passed one argument and a block" do - -> { @klass.new.m(1) { } }.should raise_error(ArgumentError) + -> { @klass.new.m(1) { } }.should.raise(ArgumentError) end it "raises an ArgumentError when passed three arguments" do - -> { @klass.new.m 1, 2, 3 }.should raise_error(ArgumentError) + -> { @klass.new.m 1, 2, 3 }.should.raise(ArgumentError) end end @@ -574,15 +706,15 @@ describe "Module#define_method" do end it "raises an ArgumentError when passed zero arguments" do - -> { @klass.new.m }.should raise_error(ArgumentError) + -> { @klass.new.m }.should.raise(ArgumentError) end it "raises an ArgumentError when passed one argument" do - -> { @klass.new.m 1 }.should raise_error(ArgumentError) + -> { @klass.new.m 1 }.should.raise(ArgumentError) end it "raises an ArgumentError when passed one argument and a block" do - -> { @klass.new.m(1) { } }.should raise_error(ArgumentError) + -> { @klass.new.m(1) { } }.should.raise(ArgumentError) end it "receives an empty array as the third argument when passed two arguments" do @@ -595,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) @@ -620,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) @@ -645,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 @@ -663,9 +795,52 @@ describe "Method#define_method when passed a Proc object" do o = c.new o.test - o.should_not have_method :nested_method_in_proc_for_define_method + o.should_not.respond_to? :nested_method_in_proc_for_define_method t.new.nested_method_in_proc_for_define_method.should == 42 end end end + +describe "Module#define_method when passed a block" do + describe "behaves exactly like a lambda" do + it "for return" do + Class.new do + define_method(:foo) do + return 42 + end + end.new.foo.should == 42 + end + + it "for break" do + Class.new do + define_method(:foo) do + break 42 + end + end.new.foo.should == 42 + end + + it "for next" do + Class.new do + define_method(:foo) do + next 42 + end + end.new.foo.should == 42 + end + + it "for redo" do + Class.new do + result = [] + define_method(:foo) do + if result.empty? + result << :first + redo + else + result << :second + result + end + end + end.new.foo.should == [:first, :second] + end + end +end diff --git a/spec/ruby/core/module/deprecate_constant_spec.rb b/spec/ruby/core/module/deprecate_constant_spec.rb index 7bcced981b..597379eb87 100644 --- a/spec/ruby/core/module/deprecate_constant_spec.rb +++ b/spec/ruby/core/module/deprecate_constant_spec.rb @@ -9,7 +9,6 @@ describe "Module#deprecate_constant" do @module::PRIVATE = @value @module.private_constant :PRIVATE @module.deprecate_constant :PRIVATE - @pattern = /deprecated/ end describe "when accessing the deprecated module" do @@ -19,32 +18,53 @@ describe "Module#deprecate_constant" do value = nil -> { value = @module::PUBLIC1 - }.should complain(@pattern) - value.should equal(@value) + }.should complain(/warning: constant .+::PUBLIC1 is deprecated/) + value.should.equal?(@value) - -> { @module::PRIVATE }.should raise_error(NameError) + -> { @module::PRIVATE }.should.raise(NameError) end it "warns with a message" do @module.deprecate_constant :PUBLIC1 - -> { @module::PUBLIC1 }.should complain(@pattern) - -> { @module.const_get :PRIVATE }.should complain(@pattern) + -> { @module::PUBLIC1 }.should complain(/warning: constant .+::PUBLIC1 is deprecated/) + -> { @module.const_get :PRIVATE }.should complain(/warning: constant .+::PRIVATE is deprecated/) + end + + it "does not warn if Warning[:deprecated] is false" do + @module.deprecate_constant :PUBLIC1 + + deprecated = Warning[:deprecated] + begin + Warning[:deprecated] = false + -> { @module::PUBLIC1 }.should_not complain + ensure + Warning[:deprecated] = deprecated + end + 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 - -> { @module::PUBLIC1 }.should complain(@pattern) - -> { @module::PUBLIC2 }.should complain(@pattern) + -> { @module::PUBLIC1 }.should complain(/warning: constant .+::PUBLIC1 is deprecated/) + -> { @module::PUBLIC2 }.should complain(/warning: constant .+::PUBLIC2 is deprecated/) end it "returns self" do - @module.deprecate_constant(:PUBLIC1).should equal(@module) + @module.deprecate_constant(:PUBLIC1).should.equal?(@module) end it "raises a NameError when given an undefined name" do - -> { @module.deprecate_constant :UNDEFINED }.should raise_error(NameError) + -> { @module.deprecate_constant :UNDEFINED }.should.raise(NameError) end end diff --git a/spec/ruby/core/module/extend_object_spec.rb b/spec/ruby/core/module/extend_object_spec.rb index e66b87efef..b428eb7924 100644 --- a/spec/ruby/core/module/extend_object_spec.rb +++ b/spec/ruby/core/module/extend_object_spec.rb @@ -7,18 +7,18 @@ describe "Module#extend_object" do end it "is a private method" do - Module.should have_private_instance_method(:extend_object) + Module.private_instance_methods(false).should.include?(:extend_object) end describe "on Class" do it "is undefined" do - Class.should_not have_private_instance_method(:extend_object, true) + Class.private_instance_methods(true).should_not.include?(:extend_object) end it "raises a TypeError if calling after rebinded to Class" do -> { Module.instance_method(:extend_object).bind(Class.new).call Object.new - }.should raise_error(TypeError) + }.should.raise(TypeError) end end @@ -42,20 +42,6 @@ describe "Module#extend_object" do ScratchPad.recorded.should == :extended end - ruby_version_is ''...'2.7' do - it "does not copy own tainted status to the given object" do - other = Object.new - Module.new.taint.send :extend_object, other - other.tainted?.should be_false - end - - it "does not copy own untrusted status to the given object" do - other = Object.new - Module.new.untrust.send :extend_object, other - other.untrusted?.should be_false - end - end - describe "when given a frozen object" do before :each do @receiver = Module.new @@ -63,8 +49,8 @@ describe "Module#extend_object" do end it "raises a RuntimeError before extending the object" do - -> { @receiver.send(:extend_object, @object) }.should raise_error(RuntimeError) - @object.should_not be_kind_of(@receiver) + -> { @receiver.send(:extend_object, @object) }.should.raise(RuntimeError) + @object.should_not.is_a?(@receiver) end end end diff --git a/spec/ruby/core/module/extended_spec.rb b/spec/ruby/core/module/extended_spec.rb index c6300ffa0b..a4ec5a4ba7 100644 --- a/spec/ruby/core/module/extended_spec.rb +++ b/spec/ruby/core/module/extended_spec.rb @@ -39,6 +39,6 @@ describe "Module#extended" do end it "is private in its default implementation" do - Module.new.private_methods.should include(:extended) + Module.new.private_methods.should.include?(:extended) end end 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/autoload_relative_a.rb b/spec/ruby/core/module/fixtures/autoload_relative_a.rb new file mode 100644 index 0000000000..494181adc2 --- /dev/null +++ b/spec/ruby/core/module/fixtures/autoload_relative_a.rb @@ -0,0 +1,9 @@ +module ModuleSpecs + module Autoload + class AutoloadRelativeA + end + + class AutoloadRelativeB + end + end +end diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb index 40777cdbbd..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 @@ -42,6 +42,14 @@ module ModuleSpecs class LookupChild < Lookup end + module ModuleWithPrepend + prepend LookupMod + end + + class WithPrependedModule + include ModuleWithPrepend + end + class Parent # For private_class_method spec def self.private_method; end @@ -352,6 +360,10 @@ module ModuleSpecs end end + class SubCVars < CVars + @@sub = :sub + end + module MVars @@mvar = :mvar end @@ -376,6 +388,7 @@ module ModuleSpecs # empty modules module M1; end module M2; end + module M3; end module Autoload def self.use_ex1 @@ -583,6 +596,32 @@ module ModuleSpecs private :foo end EmptyFooMethod = m.instance_method(:foo) + + # for undefined_instance_methods spec + module UndefinedInstanceMethods + module Super + def super_included_method; end + end + + class Parent + def undefed_method; end + undef_method :undefed_method + + def parent_method; end + def another_parent_method; end + end + + class Child < Parent + include Super + + undef_method :parent_method + undef_method :another_parent_method + end + + class Grandchild < Child + undef_method :super_included_method + end + end end class Object 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/module.rb b/spec/ruby/core/module/fixtures/module.rb index 9050a272ec..34543ca2b4 100644 --- a/spec/ruby/core/module/fixtures/module.rb +++ b/spec/ruby/core/module/fixtures/module.rb @@ -1,4 +1,8 @@ module ModuleSpecs module Anonymous + module Child + end + + SameChild = Child 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/gt_spec.rb b/spec/ruby/core/module/gt_spec.rb index b8a73c9ff9..04cdd90efb 100644 --- a/spec/ruby/core/module/gt_spec.rb +++ b/spec/ruby/core/module/gt_spec.rb @@ -3,10 +3,10 @@ require_relative 'fixtures/classes' describe "Module#>" do it "returns false if self is a subclass of or includes the given module" do - (ModuleSpecs::Child > ModuleSpecs::Parent).should be_false - (ModuleSpecs::Child > ModuleSpecs::Basic).should be_false - (ModuleSpecs::Child > ModuleSpecs::Super).should be_false - (ModuleSpecs::Super > ModuleSpecs::Basic).should be_false + (ModuleSpecs::Child > ModuleSpecs::Parent).should == false + (ModuleSpecs::Child > ModuleSpecs::Basic).should == false + (ModuleSpecs::Child > ModuleSpecs::Super).should == false + (ModuleSpecs::Super > ModuleSpecs::Basic).should == false end it "returns true if self is a superclass of or included by the given module" do @@ -31,6 +31,6 @@ describe "Module#>" do end it "raises a TypeError if the argument is not a class/module" do - -> { ModuleSpecs::Parent > mock('x') }.should raise_error(TypeError) + -> { ModuleSpecs::Parent > mock('x') }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/gte_spec.rb b/spec/ruby/core/module/gte_spec.rb index 18c60ba586..b19fc9fac3 100644 --- a/spec/ruby/core/module/gte_spec.rb +++ b/spec/ruby/core/module/gte_spec.rb @@ -28,6 +28,6 @@ describe "Module#>=" do end it "raises a TypeError if the argument is not a class/module" do - -> { ModuleSpecs::Parent >= mock('x') }.should raise_error(TypeError) + -> { ModuleSpecs::Parent >= mock('x') }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb index e7f99a5981..b6201550e4 100644 --- a/spec/ruby/core/module/include_spec.rb +++ b/spec/ruby/core/module/include_spec.rb @@ -3,7 +3,7 @@ require_relative 'fixtures/classes' describe "Module#include" do it "is a public method" do - Module.should have_public_instance_method(:include, false) + Module.public_instance_methods(false).should.include?(:include) end it "calls #append_features(self) in reversed order on each module" do @@ -33,24 +33,40 @@ describe "Module#include" do end it "adds all ancestor modules when a previously included module is included again" do - ModuleSpecs::MultipleIncludes.ancestors.should include(ModuleSpecs::MA, ModuleSpecs::MB) + ModuleSpecs::MultipleIncludes.ancestors.to_set.should >= Set[ModuleSpecs::MA, ModuleSpecs::MB] ModuleSpecs::MB.include(ModuleSpecs::MC) ModuleSpecs::MultipleIncludes.include(ModuleSpecs::MB) - ModuleSpecs::MultipleIncludes.ancestors.should include(ModuleSpecs::MA, ModuleSpecs::MB, ModuleSpecs::MC) + ModuleSpecs::MultipleIncludes.ancestors.to_set.should >= Set[ModuleSpecs::MA, ModuleSpecs::MB, ModuleSpecs::MC] end it "raises a TypeError when the argument is not a Module" do - -> { ModuleSpecs::Basic.include(Class.new) }.should raise_error(TypeError) + -> { ModuleSpecs::Basic.include(Class.new) }.should.raise(TypeError) 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(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(TypeError, "Cannot include refinement") end it "imports constants to modules and classes" do - ModuleSpecs::A.constants.should include(:CONSTANT_A) - ModuleSpecs::B.constants.should include(:CONSTANT_A, :CONSTANT_B) - ModuleSpecs::C.constants.should include(:CONSTANT_A, :CONSTANT_B) + ModuleSpecs::A.constants.should.include?(:CONSTANT_A) + ModuleSpecs::B.constants.to_set.should >= Set[:CONSTANT_A, :CONSTANT_B] + ModuleSpecs::C.constants.to_set.should >= Set[:CONSTANT_A, :CONSTANT_B] end it "shadows constants from ancestors" do @@ -68,9 +84,9 @@ describe "Module#include" do end it "imports instance methods to modules and classes" do - ModuleSpecs::A.instance_methods.should include(:ma) - ModuleSpecs::B.instance_methods.should include(:ma,:mb) - ModuleSpecs::C.instance_methods.should include(:ma,:mb) + ModuleSpecs::A.instance_methods.should.include?(:ma) + ModuleSpecs::B.instance_methods.to_set.should >= Set[:ma,:mb] + ModuleSpecs::C.instance_methods.to_set.should >= Set[:ma,:mb] end it "does not import methods to modules and classes" do @@ -104,9 +120,9 @@ describe "Module#include" do class A; include M; end class B < A; include M; end - all = [A,B,M] + all = [A, B, M] - (B.ancestors & all).should == [B, A, M] + (B.ancestors.filter { |a| all.include?(a) }).should == [B, A, M] end end @@ -130,7 +146,7 @@ describe "Module#include" do anc = B.ancestors [B, U, V, W, A, X].each do |i| - anc.include?(i).should be_true + anc.include?(i).should == true end class B; include V; end @@ -138,7 +154,7 @@ describe "Module#include" do # the only new module is Y, it is added after U since it follows U in V mixin list: anc = B.ancestors [B, U, Y, V, W, A, X].each do |i| - anc.include?(i).should be_true + anc.include?(i).should == true end end end @@ -162,7 +178,7 @@ describe "Module#include" do module ModuleSpecs::M include ModuleSpecs::M end - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end it "doesn't accept no-arguments" do @@ -170,7 +186,7 @@ describe "Module#include" do Module.new do include end - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end it "returns the class it's included into" do @@ -234,6 +250,360 @@ describe "Module#include" do remove_const :C end end + + it "updates the method when an included module is updated" do + a_class = Class.new do + def foo + 'a' + end + end + + m_module = Module.new + + b_class = Class.new(a_class) do + include m_module + end + + b = b_class.new + + foo = -> { b.foo } + + foo.call.should == 'a' + + m_module.module_eval do + def foo + 'm' + end + end + + foo.call.should == 'm' + end + + + it "updates the method when a module included after a call is later updated" do + m_module = Module.new + a_class = Class.new do + def foo + 'a' + end + end + b_class = Class.new(a_class) + b = b_class.new + foo = -> { b.foo } + foo.call.should == 'a' + + b_class.include m_module + foo.call.should == 'a' + + m_module.module_eval do + def foo + "m" + end + end + foo.call.should == 'm' + end + + it "updates the method when a nested included module is updated" do + a_class = Class.new do + def foo + 'a' + end + end + + n_module = Module.new + + m_module = Module.new do + include n_module + end + + b_class = Class.new(a_class) do + include m_module + end + + b = b_class.new + + foo = -> { b.foo } + + foo.call.should == 'a' + + n_module.module_eval do + def foo + 'n' + end + end + + foo.call.should == 'n' + end + + it "updates the method when a new module is included" do + a_class = Class.new do + def foo + 'a' + end + end + + m_module = Module.new do + def foo + 'm' + end + end + + b_class = Class.new(a_class) + b = b_class.new + + foo = -> { b.foo } + + foo.call.should == 'a' + + b_class.class_eval do + include m_module + end + + foo.call.should == 'm' + end + + it "updates the method when a new module with nested module is included" do + a_class = Class.new do + def foo + 'a' + end + end + + n_module = Module.new do + def foo + 'n' + end + end + + m_module = Module.new do + include n_module + end + + b_class = Class.new(a_class) + b = b_class.new + + foo = -> { b.foo } + + foo.call.should == 'a' + + b_class.class_eval do + include m_module + end + + foo.call.should == 'n' + end + + it "updates the constant when an included module is updated" do + module ModuleSpecs::ConstUpdated + module A + FOO = 'a' + end + + module M + end + + module B + include A + include M + def self.foo + FOO + end + end + + B.foo.should == 'a' + + 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 + module ModuleSpecs::ConstLaterUpdated + module A + FOO = 'a' + end + + module B + include A + def self.foo + FOO + end + end + + B.foo.should == 'a' + + module M + end + B.include M + + B.foo.should == 'a' + + 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 + module ModuleSpecs::ConstModuleLaterUpdated + module A + FOO = 'a' + end + + module B + include A + def self.foo + FOO + end + end + + B.foo.should == 'a' + + module M + end + B.include M + + B.foo.should == 'a' + + 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 + module ModuleSpecs::ConstUpdatedNestedIncludeUpdated + module A + FOO = 'a' + end + + module N + end + + module M + include N + end + + module B + include A + include M + def self.foo + FOO + end + end + + B.foo.should == 'a' + + 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 + module ModuleSpecs::ConstUpdatedNewInclude + module A + FOO = 'a' + end + + module M + FOO = 'm' + end + + module B + include A + def self.foo + FOO + end + end + + B.foo.should == 'a' + + 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 + module ModuleSpecs::ConstUpdatedNestedIncluded + module A + FOO = 'a' + end + + module N + FOO = 'n' + end + + module M + include N + end + + module B + include A + def self.foo + FOO + end + end + + B.foo.should == 'a' + + B.include M + B.foo.should == 'n' + end + ensure + ModuleSpecs.send(:remove_const, :ConstUpdatedNestedIncluded) + end + + it "overrides a previous super method call" do + c1 = Class.new do + def foo + [:c1] + end + end + c2 = Class.new(c1) do + def foo + [:c2] + super + end + end + c2.new.foo.should == [:c2, :c1] + m = Module.new do + def foo + [:m1] + end + end + 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 @@ -252,7 +622,7 @@ describe "Module#include?" do end it "raises a TypeError when no module was given" do - -> { ModuleSpecs::Child.include?("Test") }.should raise_error(TypeError) - -> { ModuleSpecs::Child.include?(ModuleSpecs::Parent) }.should raise_error(TypeError) + -> { ModuleSpecs::Child.include?("Test") }.should.raise(TypeError) + -> { ModuleSpecs::Child.include?(ModuleSpecs::Parent) }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/included_modules_spec.rb b/spec/ruby/core/module/included_modules_spec.rb index 40e20953f4..e22ee5e6cf 100644 --- a/spec/ruby/core/module/included_modules_spec.rb +++ b/spec/ruby/core/module/included_modules_spec.rb @@ -4,9 +4,11 @@ require_relative 'fixtures/classes' describe "Module#included_modules" do it "returns a list of modules included in self" do ModuleSpecs.included_modules.should == [] - ModuleSpecs::Child.included_modules.should include(ModuleSpecs::Super, ModuleSpecs::Basic, Kernel) - ModuleSpecs::Parent.included_modules.should include(Kernel) + + ModuleSpecs::Child.included_modules.to_set.should >= Set[ModuleSpecs::Super, ModuleSpecs::Basic, Kernel] + ModuleSpecs::Parent.included_modules.should.include?(Kernel) ModuleSpecs::Basic.included_modules.should == [] - ModuleSpecs::Super.included_modules.should include(ModuleSpecs::Basic) + ModuleSpecs::Super.included_modules.should.include?(ModuleSpecs::Basic) + ModuleSpecs::WithPrependedModule.included_modules.should.include?(ModuleSpecs::ModuleWithPrepend) end end diff --git a/spec/ruby/core/module/included_spec.rb b/spec/ruby/core/module/included_spec.rb index 2fdd4325f2..d8a6c3f839 100644 --- a/spec/ruby/core/module/included_spec.rb +++ b/spec/ruby/core/module/included_spec.rb @@ -34,7 +34,7 @@ describe "Module#included" do end it "is private in its default implementation" do - Module.should have_private_instance_method(:included) + Module.private_instance_methods(false).should.include?(:included) end it "works with super using a singleton class" do diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb index b4d6a0d8c8..8615e352f6 100644 --- a/spec/ruby/core/module/instance_method_spec.rb +++ b/spec/ruby/core/module/instance_method_spec.rb @@ -9,7 +9,7 @@ describe "Module#instance_method" do end it "is a public method" do - Module.should have_public_instance_method(:instance_method, false) + Module.public_instance_methods(false).should.include?(:instance_method) end it "requires an argument" do @@ -17,26 +17,26 @@ describe "Module#instance_method" do end it "returns an UnboundMethod corresponding to the given name" do - @parent_um.should be_kind_of(UnboundMethod) + @parent_um.should.is_a?(UnboundMethod) @parent_um.bind(ModuleSpecs::InstanceMeth.new).call.should == :foo end it "returns an UnboundMethod corresponding to the given name from a superclass" do - @child_um.should be_kind_of(UnboundMethod) + @child_um.should.is_a?(UnboundMethod) @child_um.bind(ModuleSpecs::InstanceMethChild.new).call.should == :foo end it "returns an UnboundMethod corresponding to the given name from an included Module" do - @mod_um.should be_kind_of(UnboundMethod) + @mod_um.should.is_a?(UnboundMethod) @mod_um.bind(ModuleSpecs::InstanceMethChild.new).call.should == :bar end it "returns an UnboundMethod when given a protected method name" do - ModuleSpecs::Basic.instance_method(:protected_module).should be_an_instance_of(UnboundMethod) + ModuleSpecs::Basic.instance_method(:protected_module).should.instance_of?(UnboundMethod) end it "returns an UnboundMethod when given a private method name" do - ModuleSpecs::Basic.instance_method(:private_module).should be_an_instance_of(UnboundMethod) + ModuleSpecs::Basic.instance_method(:private_module).should.instance_of?(UnboundMethod) end it "gives UnboundMethod method name, Module defined in and Module extracted from" do @@ -45,20 +45,41 @@ describe "Module#instance_method" do @parent_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/ @child_um.inspect.should =~ /\bfoo\b/ @child_um.inspect.should =~ /\bModuleSpecs::InstanceMeth\b/ - @child_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/ + @mod_um.inspect.should =~ /\bbar\b/ @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethMod\b/ - @mod_um.inspect.should =~ /\bModuleSpecs::InstanceMethChild\b/ end - it "raises a TypeError if not passed a symbol" do - -> { Object.instance_method([]) }.should raise_error(TypeError) - -> { Object.instance_method(0) }.should raise_error(TypeError) + it "raises a TypeError if the given name is not a String/Symbol" do + -> { Object.instance_method([]) }.should.raise(TypeError, /is not a symbol nor a string/) + -> { Object.instance_method(0) }.should.raise(TypeError, /is not a symbol nor a string/) + -> { Object.instance_method(nil) }.should.raise(TypeError, /is not a symbol nor a string/) + -> { Object.instance_method(mock('x')) }.should.raise(TypeError, /is not a symbol nor a string/) + end + + it "accepts String name argument" do + method = ModuleSpecs::InstanceMeth.instance_method(:foo) + method.should.is_a?(UnboundMethod) + end + + it "accepts Symbol name argument" do + method = ModuleSpecs::InstanceMeth.instance_method("foo") + method.should.is_a?(UnboundMethod) end - it "raises a TypeError if the given name is not a string/symbol" do - -> { Object.instance_method(nil) }.should raise_error(TypeError) - -> { Object.instance_method(mock('x')) }.should raise_error(TypeError) + it "converts non-String name by calling #to_str method" do + obj = Object.new + def obj.to_str() "foo" end + + method = ModuleSpecs::InstanceMeth.instance_method(obj) + method.should.is_a?(UnboundMethod) + end + + it "raises TypeError when passed non-String name and #to_str returns non-String value" do + obj = Object.new + def obj.to_str() [] end + + -> { ModuleSpecs::InstanceMeth.instance_method(obj) }.should raise_consistent_error(TypeError, /can't convert Object into String/) end it "raises a NameError if the method has been undefined" do @@ -68,11 +89,11 @@ describe "Module#instance_method" do um.should == @parent_um -> do child.instance_method(:foo) - end.should raise_error(NameError) + end.should.raise(NameError) end it "raises a NameError if the method does not exist" do - -> { Object.instance_method(:missing) }.should raise_error(NameError) + -> { Object.instance_method(:missing) }.should.raise(NameError) end it "sets the NameError#name attribute to the name of the missing method" do diff --git a/spec/ruby/core/module/instance_methods_spec.rb b/spec/ruby/core/module/instance_methods_spec.rb index d2d38cdda2..dbb6df8c34 100644 --- a/spec/ruby/core/module/instance_methods_spec.rb +++ b/spec/ruby/core/module/instance_methods_spec.rb @@ -4,21 +4,22 @@ require_relative 'fixtures/classes' describe "Module#instance_methods" do it "does not return methods undefined in a superclass" do methods = ModuleSpecs::Parent.instance_methods(false) - methods.should_not include(:undefed_method) + methods.should_not.include?(:undefed_method) end it "only includes module methods on an included module" do methods = ModuleSpecs::Basic.instance_methods(false) - methods.should include(:public_module) + methods.should.include?(:public_module) # Child is an including class methods = ModuleSpecs::Child.instance_methods(false) - methods.should include(:public_child) - methods.should_not include(:public_module) + methods.should.include?(:public_child) + methods.should_not.include?(:public_module) end it "does not return methods undefined in a subclass" do methods = ModuleSpecs::Grandchild.instance_methods - methods.should_not include(:parent_method, :another_parent_method) + methods.should_not.include?(:parent_method) + methods.should_not.include?(:another_parent_method) end it "does not return methods undefined in the current class" do @@ -28,34 +29,35 @@ describe "Module#instance_methods" do end ModuleSpecs::Child.send(:undef_method, :undefed_child) methods = ModuleSpecs::Child.instance_methods - methods.should_not include(:undefed_method, :undefed_child) + methods.should_not.include?(:undefed_method) + methods.should_not.include?(:undefed_child) end it "does not return methods from an included module that are undefined in the class" do - ModuleSpecs::Grandchild.instance_methods.should_not include(:super_included_method) + ModuleSpecs::Grandchild.instance_methods.should_not.include?(:super_included_method) end it "returns the public and protected methods of self if include_super is false" do methods = ModuleSpecs::Parent.instance_methods(false) - methods.should include(:protected_parent, :public_parent) + methods.to_set.should >= Set[:protected_parent, :public_parent] methods = ModuleSpecs::Child.instance_methods(false) - methods.should include(:protected_child, :public_child) + methods.to_set.should >= Set[:protected_child, :public_child] end it "returns the public and protected methods of self and it's ancestors" do methods = ModuleSpecs::Basic.instance_methods - methods.should include(:protected_module, :public_module) + methods.to_set.should >= Set[:protected_module, :public_module] methods = ModuleSpecs::Super.instance_methods - methods.should include(:protected_module, :protected_super_module, - :public_module, :public_super_module) + methods.to_set.should >= Set[:protected_module, :protected_super_module, + :public_module, :public_super_module] end it "makes a private Object instance method public in Kernel" do methods = Kernel.instance_methods - methods.should include(:module_specs_private_method_on_object_for_kernel_public) + methods.should.include?(:module_specs_private_method_on_object_for_kernel_public) methods = Object.instance_methods - methods.should_not include(:module_specs_private_method_on_object_for_kernel_public) + methods.should_not.include?(:module_specs_private_method_on_object_for_kernel_public) end end diff --git a/spec/ruby/core/module/lt_spec.rb b/spec/ruby/core/module/lt_spec.rb index d7771e07a8..8567a24993 100644 --- a/spec/ruby/core/module/lt_spec.rb +++ b/spec/ruby/core/module/lt_spec.rb @@ -10,10 +10,10 @@ describe "Module#<" do end it "returns false if self is a superclass of or included by the given module" do - (ModuleSpecs::Parent < ModuleSpecs::Child).should be_false - (ModuleSpecs::Basic < ModuleSpecs::Child).should be_false - (ModuleSpecs::Super < ModuleSpecs::Child).should be_false - (ModuleSpecs::Basic < ModuleSpecs::Super).should be_false + (ModuleSpecs::Parent < ModuleSpecs::Child).should == false + (ModuleSpecs::Basic < ModuleSpecs::Child).should == false + (ModuleSpecs::Super < ModuleSpecs::Child).should == false + (ModuleSpecs::Basic < ModuleSpecs::Super).should == false end it "returns false if self is the same as the given module" do @@ -31,6 +31,6 @@ describe "Module#<" do end it "raises a TypeError if the argument is not a class/module" do - -> { ModuleSpecs::Parent < mock('x') }.should raise_error(TypeError) + -> { ModuleSpecs::Parent < mock('x') }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/lte_spec.rb b/spec/ruby/core/module/lte_spec.rb index 7a0e8496b8..c6aab94e9f 100644 --- a/spec/ruby/core/module/lte_spec.rb +++ b/spec/ruby/core/module/lte_spec.rb @@ -28,6 +28,6 @@ describe "Module#<=" do end it "raises a TypeError if the argument is not a class/module" do - -> { ModuleSpecs::Parent <= mock('x') }.should raise_error(TypeError) + -> { ModuleSpecs::Parent <= mock('x') }.should.raise(TypeError) end end diff --git a/spec/ruby/core/module/method_added_spec.rb b/spec/ruby/core/module/method_added_spec.rb index b983c8da76..996a14eb86 100644 --- a/spec/ruby/core/module/method_added_spec.rb +++ b/spec/ruby/core/module/method_added_spec.rb @@ -2,8 +2,12 @@ require_relative '../../spec_helper' require_relative 'fixtures/classes' describe "Module#method_added" do + before :each do + ScratchPad.record [] + end + it "is a private instance method" do - Module.should have_private_instance_method(:method_added) + Module.private_instance_methods(false).should.include?(:method_added) end it "returns nil in the default implementation" do @@ -13,8 +17,6 @@ describe "Module#method_added" do end it "is called when a new instance method is defined in self" do - ScratchPad.record [] - Module.new do def self.method_added(name) ScratchPad << name @@ -32,8 +34,6 @@ describe "Module#method_added" do it "is not called when a singleton method is added" do # obj.singleton_method_added is called instead - ScratchPad.record [] - klass = Class.new def klass.method_added(name) ScratchPad << name @@ -57,11 +57,74 @@ describe "Module#method_added" do undef_method :method_to_undef end - m.should_not have_method(:method_to_undef) + m.should_not.respond_to?(:method_to_undef) + end + + it "is not called when a method changes visibility" do + Module.new do + def public_method + end + + def private_method + end + + def self.method_added(name) + ScratchPad << name + end + + public :public_method + private :public_method + + private :private_method + public :private_method + end + + ScratchPad.recorded.should == [] + end + + it "is called when using #private in a subclass" do + parent = Class.new do + def foo + end + end + + Class.new(parent) do + def self.method_added(name) + ScratchPad << name + end + + # Create an instance as that might initialize some method lookup caches, which is interesting to test + self.new.foo + + private :foo + public :foo + end + + ScratchPad.recorded.should == [:foo] + end + + it "is not called when a method is copied via module_function, rather #singleton_method_added is called" do + Module.new do + def mod_function + end + + def self.method_added(name) + ScratchPad << [:method_added, name] + end + + def self.singleton_method_added(name) + ScratchPad << [:singleton_method_added, name] + end + + ScratchPad.record [] + + module_function :mod_function + end + + ScratchPad.recorded.should == [[:singleton_method_added, :mod_function]] end it "is called with a precise caller location with the line of the 'def'" do - ScratchPad.record [] line = nil Module.new do diff --git a/spec/ruby/core/module/method_defined_spec.rb b/spec/ruby/core/module/method_defined_spec.rb index dccee870b1..e6b4c7b817 100644 --- a/spec/ruby/core/module/method_defined_spec.rb +++ b/spec/ruby/core/module/method_defined_spec.rb @@ -27,17 +27,17 @@ describe "Module#method_defined?" do it "does not search Object or Kernel when called on a module" do m = Module.new - m.method_defined?(:module_specs_public_method_on_kernel).should be_false + m.method_defined?(:module_specs_public_method_on_kernel).should == false end it "raises a TypeError when the given object is not a string/symbol" do c = Class.new o = mock('123') - -> { c.method_defined?(o) }.should raise_error(TypeError) + -> { c.method_defined?(o) }.should.raise(TypeError) o.should_receive(:to_str).and_return(123) - -> { c.method_defined?(o) }.should raise_error(TypeError) + -> { c.method_defined?(o) }.should.raise(TypeError) end it "converts the given name to a string using to_str" do @@ -47,54 +47,52 @@ describe "Module#method_defined?" do c.method_defined?(o).should == true end - ruby_version_is "2.6" do - # works as method_defined?(method_name) - describe "when passed true as a second optional argument" do - it "performs a lookup in ancestors" do - ModuleSpecs::Child.method_defined?(:public_child, true).should == true - ModuleSpecs::Child.method_defined?(:protected_child, true).should == true - ModuleSpecs::Child.method_defined?(:accessor_method, true).should == true - ModuleSpecs::Child.method_defined?(:private_child, true).should == false - - # Defined in Parent - ModuleSpecs::Child.method_defined?(:public_parent, true).should == true - ModuleSpecs::Child.method_defined?(:protected_parent, true).should == true - ModuleSpecs::Child.method_defined?(:private_parent, true).should == false - - # Defined in Module - ModuleSpecs::Child.method_defined?(:public_module, true).should == true - ModuleSpecs::Child.method_defined?(:protected_module, true).should == true - ModuleSpecs::Child.method_defined?(:private_module, true).should == false - - # Defined in SuperModule - ModuleSpecs::Child.method_defined?(:public_super_module, true).should == true - ModuleSpecs::Child.method_defined?(:protected_super_module, true).should == true - ModuleSpecs::Child.method_defined?(:private_super_module, true).should == false - end + # works as method_defined?(method_name) + describe "when passed true as a second optional argument" do + it "performs a lookup in ancestors" do + ModuleSpecs::Child.method_defined?(:public_child, true).should == true + ModuleSpecs::Child.method_defined?(:protected_child, true).should == true + ModuleSpecs::Child.method_defined?(:accessor_method, true).should == true + ModuleSpecs::Child.method_defined?(:private_child, true).should == false + + # Defined in Parent + ModuleSpecs::Child.method_defined?(:public_parent, true).should == true + ModuleSpecs::Child.method_defined?(:protected_parent, true).should == true + ModuleSpecs::Child.method_defined?(:private_parent, true).should == false + + # Defined in Module + ModuleSpecs::Child.method_defined?(:public_module, true).should == true + ModuleSpecs::Child.method_defined?(:protected_module, true).should == true + ModuleSpecs::Child.method_defined?(:private_module, true).should == false + + # Defined in SuperModule + ModuleSpecs::Child.method_defined?(:public_super_module, true).should == true + ModuleSpecs::Child.method_defined?(:protected_super_module, true).should == true + ModuleSpecs::Child.method_defined?(:private_super_module, true).should == false end + end - describe "when passed false as a second optional argument" do - it "checks only the class itself" do - ModuleSpecs::Child.method_defined?(:public_child, false).should == true - ModuleSpecs::Child.method_defined?(:protected_child, false).should == true - ModuleSpecs::Child.method_defined?(:accessor_method, false).should == true - ModuleSpecs::Child.method_defined?(:private_child, false).should == false - - # Defined in Parent - ModuleSpecs::Child.method_defined?(:public_parent, false).should == false - ModuleSpecs::Child.method_defined?(:protected_parent, false).should == false - ModuleSpecs::Child.method_defined?(:private_parent, false).should == false - - # Defined in Module - ModuleSpecs::Child.method_defined?(:public_module, false).should == false - ModuleSpecs::Child.method_defined?(:protected_module, false).should == false - ModuleSpecs::Child.method_defined?(:private_module, false).should == false - - # Defined in SuperModule - ModuleSpecs::Child.method_defined?(:public_super_module, false).should == false - ModuleSpecs::Child.method_defined?(:protected_super_module, false).should == false - ModuleSpecs::Child.method_defined?(:private_super_module, false).should == false - end + describe "when passed false as a second optional argument" do + it "checks only the class itself" do + ModuleSpecs::Child.method_defined?(:public_child, false).should == true + ModuleSpecs::Child.method_defined?(:protected_child, false).should == true + ModuleSpecs::Child.method_defined?(:accessor_method, false).should == true + ModuleSpecs::Child.method_defined?(:private_child, false).should == false + + # Defined in Parent + ModuleSpecs::Child.method_defined?(:public_parent, false).should == false + ModuleSpecs::Child.method_defined?(:protected_parent, false).should == false + ModuleSpecs::Child.method_defined?(:private_parent, false).should == false + + # Defined in Module + ModuleSpecs::Child.method_defined?(:public_module, false).should == false + ModuleSpecs::Child.method_defined?(:protected_module, false).should == false + ModuleSpecs::Child.method_defined?(:private_module, false).should == false + + # Defined in SuperModule + ModuleSpecs::Child.method_defined?(:public_super_module, false).should == false + ModuleSpecs::Child.method_defined?(:protected_super_module, false).should == false + ModuleSpecs::Child.method_defined?(:private_super_module, false).should == false end end end diff --git a/spec/ruby/core/module/method_removed_spec.rb b/spec/ruby/core/module/method_removed_spec.rb index 9b39eb77a6..80e546406c 100644 --- a/spec/ruby/core/module/method_removed_spec.rb +++ b/spec/ruby/core/module/method_removed_spec.rb @@ -3,7 +3,7 @@ require_relative 'fixtures/classes' describe "Module#method_removed" do it "is a private instance method" do - Module.should have_private_instance_method(:method_removed) + Module.private_instance_methods(false).should.include?(:method_removed) end it "returns nil in the default implementation" do diff --git a/spec/ruby/core/module/method_undefined_spec.rb b/spec/ruby/core/module/method_undefined_spec.rb index a94388fe0a..596c5c50e2 100644 --- a/spec/ruby/core/module/method_undefined_spec.rb +++ b/spec/ruby/core/module/method_undefined_spec.rb @@ -3,7 +3,7 @@ require_relative 'fixtures/classes' describe "Module#method_undefined" do it "is a private instance method" do - Module.should have_private_instance_method(:method_undefined) + Module.private_instance_methods(false).should.include?(:method_undefined) end it "returns nil in the default implementation" do diff --git a/spec/ruby/core/module/module_function_spec.rb b/spec/ruby/core/module/module_function_spec.rb index 407237d48f..41bd152608 100644 --- a/spec/ruby/core/module/module_function_spec.rb +++ b/spec/ruby/core/module/module_function_spec.rb @@ -3,22 +3,22 @@ require_relative 'fixtures/classes' describe "Module#module_function" do it "is a private method" do - Module.should have_private_instance_method(:module_function) + Module.private_instance_methods(false).should.include?(:module_function) end describe "on Class" do it "is undefined" do - Class.should_not have_private_instance_method(:module_function, true) + Class.private_instance_methods(true).should_not.include?(:module_function) end it "raises a TypeError if calling after rebinded to Class" do -> { Module.instance_method(:module_function).bind(Class.new).call - }.should raise_error(TypeError) + }.should.raise(TypeError) -> { Module.instance_method(:module_function).bind(Class.new).call :foo - }.should raise_error(TypeError) + }.should.raise(TypeError) end end end @@ -38,14 +38,12 @@ describe "Module#module_function with specific method names" do m.respond_to?(:test3).should == false end - it "returns the current module" do - x = nil - m = Module.new do - def test() end - x = module_function :test + 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 - - x.should == m end it "creates an independent copy of the method, not a redirect" do @@ -85,9 +83,9 @@ describe "Module#module_function with specific method names" do (o = mock('x')).extend(m) o.respond_to?(:test).should == false - m.should have_private_instance_method(:test) + m.private_instance_methods(false).should.include?(:test) o.send(:test).should == "hello" - -> { o.test }.should raise_error(NoMethodError) + -> { o.test }.should.raise(NoMethodError) end it "makes the new Module methods public" do @@ -116,17 +114,17 @@ describe "Module#module_function with specific method names" do it "raises a TypeError when the given names can't be converted to string using to_str" do o = mock('123') - -> { Module.new { module_function(o) } }.should raise_error(TypeError) + -> { Module.new { module_function(o) } }.should.raise(TypeError) o.should_receive(:to_str).and_return(123) - -> { Module.new { module_function(o) } }.should raise_error(TypeError) + -> { Module.new { module_function(o) } }.should.raise(TypeError) end it "can make accessible private methods" do # JRUBY-4214 m = Module.new do module_function :require end - m.respond_to?(:require).should be_true + m.respond_to?(:require).should == true end it "creates Module methods that super up the singleton class of the module" do @@ -146,38 +144,82 @@ describe "Module#module_function with specific method names" do m.foo.should == ["m", "super_m"] end + + context "methods created with define_method" do + context "passed a block" do + it "creates duplicates of the given instance methods" do + m = Module.new do + define_method :test1 do; end + module_function :test1 + end + + m.respond_to?(:test1).should == true + end + end + + context "passed a method" do + it "creates duplicates of the given instance methods" do + module_with_method = Module.new do + def test1; end + end + + c = Class.new do + extend module_with_method + end + + m = Module.new do + define_method :test2, c.method(:test1) + module_function :test2 + end + + m.respond_to?(:test2).should == true + end + end + + context "passed an unbound method" do + it "creates duplicates of the given instance methods" do + module_with_method = Module.new do + def test1; end + end + + m = Module.new do + define_method :test2, module_with_method.instance_method(:test1) + module_function :test2 + end + + m.respond_to?(:test2).should == true + end + end + end end describe "Module#module_function as a toggle (no arguments) in a Module body" do it "makes any subsequently defined methods module functions with the normal semantics" do - m = Module.new { + m = Module.new do module_function def test1() end def test2() end - } + end m.respond_to?(:test1).should == true m.respond_to?(:test2).should == true end - it "returns the current module" do - x = nil - m = Module.new { - x = module_function - } - - x.should == m + it "returns nil" do + Module.new do + module_function.should.equal?(nil) + end end it "stops creating module functions if the body encounters another toggle " \ "like public/protected/private without arguments" do - m = Module.new { + m = Module.new do module_function def test1() end def test2() end public def test3() end - } + end m.respond_to?(:test1).should == true m.respond_to?(:test2).should == true @@ -186,14 +228,14 @@ describe "Module#module_function as a toggle (no arguments) in a Module body" do it "does not stop creating module functions if the body encounters " \ "public/protected/private WITH arguments" do - m = Module.new { + m = Module.new do def foo() end module_function def test1() end def test2() end public :foo def test3() end - } + end m.respond_to?(:test1).should == true m.respond_to?(:test2).should == true @@ -201,69 +243,116 @@ describe "Module#module_function as a toggle (no arguments) in a Module body" do end it "does not affect module_evaled method definitions also if outside the eval itself" do - m = Module.new { + m = Module.new do module_function module_eval { def test1() end } module_eval " def test2() end " - } + end m.respond_to?(:test1).should == false m.respond_to?(:test2).should == false end it "has no effect if inside a module_eval if the definitions are outside of it" do - m = Module.new { + m = Module.new do module_eval { module_function } def test1() end def test2() end - } + end m.respond_to?(:test1).should == false m.respond_to?(:test2).should == false end it "functions normally if both toggle and definitions inside a module_eval" do - m = Module.new { - module_eval { + m = Module.new do + module_eval do module_function def test1() end def test2() end - } - } + end + end m.respond_to?(:test1).should == true m.respond_to?(:test2).should == true end - it "affects evaled method definitions also even when outside the eval itself" do - m = Module.new { + it "affects eval'ed method definitions also even when outside the eval itself" do + m = Module.new do module_function eval "def test1() end" - } + end m.respond_to?(:test1).should == true end it "doesn't affect definitions when inside an eval even if the definitions are outside of it" do - m = Module.new { + m = Module.new do eval "module_function" def test1() end - } + end m.respond_to?(:test1).should == false end it "functions normally if both toggle and definitions inside a eval" do - m = Module.new { + m = Module.new do eval <<-CODE module_function def test1() end def test2() end CODE - } + end m.respond_to?(:test1).should == true m.respond_to?(:test2).should == true end + + context "methods are defined with define_method" do + context "passed a block" do + it "makes any subsequently defined methods module functions with the normal semantics" do + m = Module.new do + module_function + define_method :test1 do; end + end + + m.respond_to?(:test1).should == true + end + end + + context "passed a method" do + it "makes any subsequently defined methods module functions with the normal semantics" do + module_with_method = Module.new do + def test1; end + end + + c = Class.new do + extend module_with_method + end + + m = Module.new do + module_function + define_method :test2, c.method(:test1) + end + + m.respond_to?(:test2).should == true + end + end + + context "passed an unbound method" do + it "makes any subsequently defined methods module functions with the normal semantics" do + module_with_method = Module.new do + def test1; end + end + + m = Module.new do + module_function + define_method :test2, module_with_method.instance_method(:test1) + end + + m.respond_to?(:test2).should == true + end + end + end end diff --git a/spec/ruby/core/module/name_spec.rb b/spec/ruby/core/module/name_spec.rb index ca9106a973..332c08d782 100644 --- a/spec/ruby/core/module/name_spec.rb +++ b/spec/ruby/core/module/name_spec.rb @@ -3,23 +3,13 @@ require_relative 'fixtures/module' describe "Module#name" do it "is nil for an anonymous module" do - Module.new.name.should be_nil + Module.new.name.should == nil end - ruby_version_is ""..."3.0" do - it "is nil when assigned to a constant in an anonymous module" do - m = Module.new - m::N = Module.new - m::N.name.should be_nil - end - end - - ruby_version_is "3.0" do - it "is not nil when assigned to a constant in an anonymous module" do - m = Module.new - m::N = Module.new - m::N.name.should.end_with? '::N' - end + it "is not nil when assigned to a constant in an anonymous module" do + m = Module.new + m::N = Module.new + m::N.name.should.end_with? '::N' end it "is not nil for a nested module created with the module keyword" do @@ -29,9 +19,9 @@ describe "Module#name" do end it "returns nil for a singleton class" do - Module.new.singleton_class.name.should be_nil - String.singleton_class.name.should be_nil - Object.new.singleton_class.name.should be_nil + Module.new.singleton_class.name.should == nil + String.singleton_class.name.should == nil + Object.new.singleton_class.name.should == nil end it "changes when the module is reachable through a constant path" do @@ -40,6 +30,21 @@ 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 + m = Module.new + n = Module.new + + suppress_warning do + ModuleSpecs::Anonymous::SameName = m + ModuleSpecs::Anonymous::SameName = n + end + + m.name.should == "ModuleSpecs::Anonymous::SameName" + n.name.should == "ModuleSpecs::Anonymous::SameName" end it "is set after it is removed from a constant" do @@ -69,10 +74,20 @@ describe "Module#name" do ModuleSpecs::Anonymous.name.should == "ModuleSpecs::Anonymous" end - it "is set when assigning to a constant" do + it "is set when assigning to a constant (constant path matches outer 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 @@ -81,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 @@ -111,34 +129,77 @@ describe "Module#name" do ModuleSpecs::NameEncoding.new.name.encoding.should == Encoding::UTF_8 end - it "is set when the anonymous outer module name is set" do + it "is set when the anonymous outer module name is set (module in one single constant)" do m = Module.new m::N = Module.new ModuleSpecs::Anonymous::E = m m::N.name.should == "ModuleSpecs::Anonymous::E::N" + ensure + ModuleSpecs::Anonymous.send(:remove_const, :E) end - ruby_version_is ""..."2.7" do - it "returns a mutable string" do - ModuleSpecs.name.frozen?.should be_false - end + # https://bugs.ruby-lang.org/issues/19681 + it "is set when the anonymous outer module name is set (module in several constants)" do + m = Module.new + m::N = Module.new + m::O = m::N + ModuleSpecs::Anonymous::StoredInMultiplePlaces = m + valid_names = [ + "ModuleSpecs::Anonymous::StoredInMultiplePlaces::N", + "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 - it "returns a mutable string that when mutated does not modify the original module name" do - ModuleSpecs.name << "foo" + # module with name + module TEST1 + end - ModuleSpecs.name.should == "ModuleSpecs" - end + # anonymous module + TEST2 = Module.new + RUBY end - ruby_version_is "2.7" do - it "returns a frozen String" do - ModuleSpecs.name.should.frozen? - 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 - it "always returns the same String for a given Module" do - s1 = ModuleSpecs.name - s2 = ModuleSpecs.name - s1.should equal(s2) + 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 + ModuleSpecs.name.should.frozen? + end + + it "always returns the same String for a given Module" do + s1 = ModuleSpecs.name + s2 = ModuleSpecs.name + s1.should.equal?(s2) end end diff --git a/spec/ruby/core/module/new_spec.rb b/spec/ruby/core/module/new_spec.rb index da7f3b8720..ec7a0cfa77 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 == 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_features_spec.rb b/spec/ruby/core/module/prepend_features_spec.rb index 2d1fa713b7..3a50f2c2a4 100644 --- a/spec/ruby/core/module/prepend_features_spec.rb +++ b/spec/ruby/core/module/prepend_features_spec.rb @@ -3,7 +3,7 @@ require_relative 'fixtures/classes' describe "Module#prepend_features" do it "is a private method" do - Module.should have_private_instance_method(:prepend_features, true) + Module.private_instance_methods(false).should.include?(:prepend_features) end it "gets called when self is included in another module/class" do @@ -25,21 +25,7 @@ describe "Module#prepend_features" do it "raises an ArgumentError on a cyclic prepend" do -> { ModuleSpecs::CyclicPrepend.send(:prepend_features, ModuleSpecs::CyclicPrepend) - }.should raise_error(ArgumentError) - end - - ruby_version_is ''...'2.7' do - it "copies own tainted status to the given module" do - other = Module.new - Module.new.taint.send :prepend_features, other - other.tainted?.should be_true - end - - it "copies own untrusted status to the given module" do - other = Module.new - Module.new.untrust.send :prepend_features, other - other.untrusted?.should be_true - end + }.should.raise(ArgumentError) end it "clears caches of the given module" do @@ -66,13 +52,13 @@ describe "Module#prepend_features" do describe "on Class" do it "is undefined" do - Class.should_not have_private_instance_method(:prepend_features, true) + Class.private_instance_methods(true).should_not.include?(:prepend_features) end it "raises a TypeError if calling after rebinded to Class" do -> { Module.instance_method(:prepend_features).bind(Class.new).call Module.new - }.should raise_error(TypeError) + }.should.raise(TypeError) end end end diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb index a501b5e50c..f7887e6d6a 100644 --- a/spec/ruby/core/module/prepend_spec.rb +++ b/spec/ruby/core/module/prepend_spec.rb @@ -3,7 +3,7 @@ require_relative 'fixtures/classes' describe "Module#prepend" do it "is a public method" do - Module.should have_public_instance_method(:prepend, false) + Module.public_instance_methods(false).should.include?(:prepend) end it "does not affect the superclass" do @@ -36,27 +36,450 @@ describe "Module#prepend" do ScratchPad.recorded.should == [ [ m3, c], [ m2, c ], [ m, c ] ] end + it "updates the method when a module is prepended" do + m_module = Module.new do + def foo + "m" + end + end + a_class = Class.new do + def foo + 'a' + end + end + a = a_class.new + foo = -> { a.foo } + foo.call.should == 'a' + a_class.class_eval do + prepend m_module + end + foo.call.should == 'm' + end + + it "updates the method when a prepended module is updated" do + m_module = Module.new + a_class = Class.new do + prepend m_module + def foo + 'a' + end + end + a = a_class.new + foo = -> { a.foo } + foo.call.should == 'a' + m_module.module_eval do + def foo + "m" + end + end + 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 + 'a' + end + end + a_class = Class.new do + include base_module + end + a = a_class.new + foo = -> { a.foo } + foo.call.should == 'a' + + m_module = Module.new do + def foo + "m" + end + end + a_class.prepend m_module + foo.call.should == 'm' + end + + it "updates the method when there is a base included method and the prepended module is later updated" do + base_module = Module.new do + def foo + 'a' + end + end + a_class = Class.new do + include base_module + end + a = a_class.new + foo = -> { a.foo } + foo.call.should == 'a' + + m_module = Module.new + a_class.prepend m_module + foo.call.should == 'a' + + m_module.module_eval do + def foo + "m" + end + end + foo.call.should == 'm' + end + + it "updates the method when a module prepended after a call is later updated" do + m_module = Module.new + a_class = Class.new do + def foo + 'a' + end + end + a = a_class.new + foo = -> { a.foo } + foo.call.should == 'a' + + a_class.prepend m_module + foo.call.should == 'a' + + m_module.module_eval do + def foo + "m" + end + end + foo.call.should == 'm' + end + + it "updates the method when a module is prepended after another and the method is defined later on that module" do + m_module = Module.new do + def foo + 'a' + end + end + a_class = Class.new + a_class.prepend m_module + a = a_class.new + foo = -> { a.foo } + foo.call.should == 'a' + + n_module = Module.new + a_class.prepend n_module + foo.call.should == 'a' + + n_module.module_eval do + def foo + "n" + end + end + foo.call.should == 'n' + end + + it "updates the method when a module is included in a prepended module and the method is defined later" do + a_class = Class.new + base_module = Module.new do + def foo + 'a' + end + end + a_class.prepend base_module + a = a_class.new + foo = -> { a.foo } + foo.call.should == 'a' + + m_module = Module.new + n_module = Module.new + m_module.include n_module + a_class.prepend m_module + + n_module.module_eval do + def foo + "n" + end + end + foo.call.should == 'n' + end + + it "updates the method when a new module with an included module is prepended" do + a_class = Class.new do + def foo + 'a' + end + end + + n_module = Module.new do + def foo + 'n' + end + end + + m_module = Module.new do + include n_module + end + + a = a_class.new + foo = -> { a.foo } + + foo.call.should == 'a' + + a_class.class_eval do + prepend m_module + end + + foo.call.should == 'n' + end + + it "updates the constant when a module is prepended" do + module ModuleSpecs::ConstUpdatePrepended + module M + FOO = 'm' + end + module A + FOO = 'a' + end + module B + include A + def self.foo + FOO + end + end + + B.foo.should == 'a' + 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 + module ModuleSpecs::ConstPrependedUpdated + module M + end + module A + FOO = 'a' + end + module B + include A + prepend M + def self.foo + FOO + end + end + B.foo.should == 'a' + 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 + module ModuleSpecs::ConstIncludedPrependedOverride + module Base + FOO = 'a' + end + module A + include Base + def self.foo + FOO + end + end + A.foo.should == 'a' + + module M + FOO = 'm' + end + 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 + module ModuleSpecs::ConstIncludedPrependedLaterUpdated + module Base + FOO = 'a' + end + module A + include Base + def self.foo + FOO + end + end + A.foo.should == 'a' + + module M + end + A.prepend M + A.foo.should == 'a' + + 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 + module ModuleSpecs::ConstUpdatedPrependedAfterLaterUpdated + module M + end + module A + FOO = 'a' + end + module B + include A + def self.foo + FOO + end + end + B.foo.should == 'a' + + B.prepend M + B.foo.should == 'a' + + 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 + module ModuleSpecs::ConstUpdatedPrependedAfterConstDefined + module M + FOO = 'm' + end + module A + prepend M + def self.foo + FOO + end + end + + A.foo.should == 'm' + + module N + end + A.prepend N + A.foo.should == 'm' + + 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 + module ModuleSpecs::ConstUpdatedIncludedInPrependedConstDefinedLater + module A + def self.foo + FOO + end + end + module Base + FOO = 'a' + end + + A.prepend Base + A.foo.should == 'a' + + module N + end + module M + include N + end + + A.prepend M + + 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 + module ModuleSpecs::ConstUpdatedNewModuleIncludedPrepended + module A + FOO = 'a' + end + module B + include A + def self.foo + FOO + end + end + module N + FOO = 'n' + end + + module M + include N + end + + B.foo.should == 'a' + + 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 - -> { ModuleSpecs::Basic.prepend(Class.new) }.should raise_error(TypeError) + -> { ModuleSpecs::Basic.prepend(Class.new) }.should.raise(TypeError) 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(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(TypeError, "Cannot prepend refinement") end it "imports constants" do m1 = Module.new m1::MY_CONSTANT = 1 m2 = Module.new { prepend(m1) } - m2.constants.should include(:MY_CONSTANT) + m2.constants.should.include?(:MY_CONSTANT) end it "imports instance methods" do - Module.new { prepend ModuleSpecs::A }.instance_methods.should include(:ma) + Module.new { prepend ModuleSpecs::A }.instance_methods.should.include?(:ma) end it "does not import methods to modules and classes" do - Module.new { prepend ModuleSpecs::A }.methods.should_not include(:ma) + Module.new { prepend ModuleSpecs::A }.methods.should_not.include?(:ma) end it "allows wrapping methods" do @@ -82,7 +505,7 @@ describe "Module#prepend" do it "includes prepended modules in ancestors" do m = Module.new - Class.new { prepend(m) }.ancestors.should include(m) + Class.new { prepend(m) }.ancestors.should.include?(m) end it "reports the prepended module as the method owner" do @@ -119,48 +542,31 @@ describe "Module#prepend" do it "sees an instance of a prepended class as kind of the prepended module" do m = Module.new c = Class.new { prepend(m) } - c.new.should be_kind_of(m) + c.new.should.is_a?(m) end it "keeps the module in the chain when dupping the class" do m = Module.new c = Class.new { prepend(m) } - c.dup.new.should be_kind_of(m) - end - - ruby_version_is '0'...'3.0' do - it "keeps the module in the chain when dupping an intermediate module" do - m1 = Module.new { def calc(x) x end } - m2 = Module.new { prepend(m1) } - c1 = Class.new { prepend(m2) } - m2dup = m2.dup - m2dup.ancestors.should == [m2dup,m1,m2] - c2 = Class.new { prepend(m2dup) } - c1.ancestors[0,3].should == [m1,m2,c1] - c1.new.should be_kind_of(m1) - c2.ancestors[0,4].should == [m2dup,m1,m2,c2] - c2.new.should be_kind_of(m1) - end + c.dup.new.should.is_a?(m) end - ruby_version_is '3.0' do - it "uses only new module when dupping the module" do - m1 = Module.new { def calc(x) x end } - m2 = Module.new { prepend(m1) } - c1 = Class.new { prepend(m2) } - m2dup = m2.dup - m2dup.ancestors.should == [m1,m2dup] - c2 = Class.new { prepend(m2dup) } - c1.ancestors[0,3].should == [m1,m2,c1] - c1.new.should be_kind_of(m1) - c2.ancestors[0,3].should == [m1,m2dup,c2] - c2.new.should be_kind_of(m1) - end + it "uses only new module when dupping the module" do + m1 = Module.new { def calc(x) x end } + m2 = Module.new { prepend(m1) } + c1 = Class.new { prepend(m2) } + m2dup = m2.dup + m2dup.ancestors.should == [m1,m2dup] + c2 = Class.new { prepend(m2dup) } + c1.ancestors[0,3].should == [m1,m2,c1] + c1.new.should.is_a?(m1) + c2.ancestors[0,3].should == [m1,m2dup,c2] + c2.new.should.is_a?(m1) end it "depends on prepend_features to add the module" do m = Module.new { def self.prepend_features(mod) end } - Class.new { prepend(m) }.ancestors.should_not include(m) + Class.new { prepend(m) }.ancestors.should_not.include?(m) end it "adds the module in the subclass chains" do @@ -221,7 +627,7 @@ describe "Module#prepend" do super << :class end end - -> { c.new.chain }.should raise_error(NoMethodError) + -> { c.new.chain }.should.raise(NoMethodError) end it "calls prepended after prepend_features" do @@ -240,12 +646,24 @@ describe "Module#prepend" do ScratchPad.recorded.should == [[:prepend_features, c], [:prepended, c]] end + it "prepends a module if it is included in a super class" do + module ModuleSpecs::M3 + module M; end + class A; include M; end + class B < A; prepend M; end + + all = [A, B, M] + + (B.ancestors.filter { |a| all.include?(a) }).should == [M, B, A, M] + end + end + it "detects cyclic prepends" do -> { module ModuleSpecs::P prepend ModuleSpecs::P end - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end it "doesn't accept no-arguments" do @@ -253,7 +671,7 @@ describe "Module#prepend" do Module.new do prepend end - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError) end it "returns the class it's included into" do @@ -360,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 @@ -372,7 +817,7 @@ describe "Module#prepend" do pre = Module.new mod.prepend pre - cls.instance_methods.should include(:foo) + cls.instance_methods.should.include?(:foo) end end end diff --git a/spec/ruby/core/module/prepended_spec.rb b/spec/ruby/core/module/prepended_spec.rb index bd95d8fd05..ccd450d668 100644 --- a/spec/ruby/core/module/prepended_spec.rb +++ b/spec/ruby/core/module/prepended_spec.rb @@ -8,7 +8,7 @@ describe "Module#prepended" do end it "is a private method" do - Module.should have_private_instance_method(:prepended, true) + Module.private_instance_methods(false).should.include?(:prepended) end it "is invoked when self is prepended to another module or class" do diff --git a/spec/ruby/core/module/private_class_method_spec.rb b/spec/ruby/core/module/private_class_method_spec.rb index e35b50d986..7ce07f1600 100644 --- a/spec/ruby/core/module/private_class_method_spec.rb +++ b/spec/ruby/core/module/private_class_method_spec.rb @@ -22,11 +22,11 @@ describe "Module#private_class_method" do it "makes an existing class method private" do ModuleSpecs::Parent.private_method_1.should == nil ModuleSpecs::Parent.private_class_method :private_method_1 - -> { ModuleSpecs::Parent.private_method_1 }.should raise_error(NoMethodError) + -> { ModuleSpecs::Parent.private_method_1 }.should.raise(NoMethodError) # Technically above we're testing the Singleton classes, class method(right?). # Try a "real" class method set private. - -> { ModuleSpecs::Parent.private_method }.should raise_error(NoMethodError) + -> { ModuleSpecs::Parent.private_method }.should.raise(NoMethodError) end it "makes an existing class method private up the inheritance tree" do @@ -34,8 +34,8 @@ describe "Module#private_class_method" do ModuleSpecs::Child.private_method_1.should == nil ModuleSpecs::Child.private_class_method :private_method_1 - -> { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError) - -> { ModuleSpecs::Child.private_method }.should raise_error(NoMethodError) + -> { ModuleSpecs::Child.private_method_1 }.should.raise(NoMethodError) + -> { ModuleSpecs::Child.private_method }.should.raise(NoMethodError) end it "accepts more than one method at a time" do @@ -44,14 +44,14 @@ describe "Module#private_class_method" do ModuleSpecs::Child.private_class_method :private_method_1, :private_method_2 - -> { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError) - -> { ModuleSpecs::Child.private_method_2 }.should raise_error(NoMethodError) + -> { ModuleSpecs::Child.private_method_1 }.should.raise(NoMethodError) + -> { ModuleSpecs::Child.private_method_2 }.should.raise(NoMethodError) end it "raises a NameError if class method doesn't exist" do -> do ModuleSpecs.private_class_method :no_method_here - end.should raise_error(NameError) + end.should.raise(NameError) end it "makes a class method private" do @@ -59,7 +59,7 @@ describe "Module#private_class_method" do def self.foo() "foo" end private_class_method :foo end - -> { c.foo }.should raise_error(NoMethodError) + -> { c.foo }.should.raise(NoMethodError) end it "raises a NameError when the given name is not a method" do @@ -67,7 +67,7 @@ describe "Module#private_class_method" do Class.new do private_class_method :foo end - end.should raise_error(NameError) + end.should.raise(NameError) end it "raises a NameError when the given name is an instance method" do @@ -76,6 +76,16 @@ describe "Module#private_class_method" do def foo() "foo" end private_class_method :foo end - end.should raise_error(NameError) + end.should.raise(NameError) + end + + context "when single argument is passed and is an array" do + it "sets the visibility of the given methods to private" do + c = Class.new do + def self.foo() "foo" end + private_class_method [:foo] + end + -> { c.foo }.should.raise(NoMethodError) + end end end diff --git a/spec/ruby/core/module/private_constant_spec.rb b/spec/ruby/core/module/private_constant_spec.rb index 3a91b3c3cd..ca0df48c0b 100644 --- a/spec/ruby/core/module/private_constant_spec.rb +++ b/spec/ruby/core/module/private_constant_spec.rb @@ -8,7 +8,7 @@ describe "Module#private_constant" do -> do cls2.send :private_constant, :Foo - end.should raise_error(NameError) + end.should.raise(NameError) end it "accepts strings as constant names" do @@ -16,7 +16,7 @@ describe "Module#private_constant" do cls.const_set :Foo, true cls.send :private_constant, "Foo" - -> { cls::Foo }.should raise_error(NameError) + -> { cls::Foo }.should.raise(NameError) end it "accepts multiple names" do @@ -26,7 +26,7 @@ describe "Module#private_constant" do mod.send :private_constant, :Foo, :Bar - -> {mod::Foo}.should raise_error(NameError) - -> {mod::Bar}.should raise_error(NameError) + -> {mod::Foo}.should.raise(NameError) + -> {mod::Bar}.should.raise(NameError) end end diff --git a/spec/ruby/core/module/private_instance_methods_spec.rb b/spec/ruby/core/module/private_instance_methods_spec.rb index cce0f001bf..ae0f21d1dd 100644 --- a/spec/ruby/core/module/private_instance_methods_spec.rb +++ b/spec/ruby/core/module/private_instance_methods_spec.rb @@ -5,20 +5,20 @@ require_relative '../../fixtures/reflection' # TODO: rewrite describe "Module#private_instance_methods" do it "returns a list of private methods in module and its ancestors" do - ModuleSpecs::CountsMixin.should have_private_instance_method(:private_3) + ModuleSpecs::CountsMixin.private_instance_methods(false).should.include?(:private_3) - ModuleSpecs::CountsParent.should have_private_instance_method(:private_2) - ModuleSpecs::CountsParent.should have_private_instance_method(:private_3) + ModuleSpecs::CountsParent.private_instance_methods(false).should.include?(:private_2) + ModuleSpecs::CountsParent.private_instance_methods(true).should.include?(:private_3) - ModuleSpecs::CountsChild.should have_private_instance_method(:private_1) - ModuleSpecs::CountsChild.should have_private_instance_method(:private_2) - ModuleSpecs::CountsChild.should have_private_instance_method(:private_3) + ModuleSpecs::CountsChild.private_instance_methods(false).should.include?(:private_1) + ModuleSpecs::CountsChild.private_instance_methods(true).should.include?(:private_2) + ModuleSpecs::CountsChild.private_instance_methods(true).should.include?(:private_3) end it "when passed false as a parameter, should return only methods defined in that module" do - ModuleSpecs::CountsMixin.should have_private_instance_method(:private_3, false) - ModuleSpecs::CountsParent.should have_private_instance_method(:private_2, false) - ModuleSpecs::CountsChild.should have_private_instance_method(:private_1, false) + ModuleSpecs::CountsMixin.private_instance_methods(false).should.include?(:private_3) + ModuleSpecs::CountsParent.private_instance_methods(false).should.include?(:private_2) + ModuleSpecs::CountsChild.private_instance_methods(false).should.include?(:private_1) end it "default list should be the same as passing true as an argument" do diff --git a/spec/ruby/core/module/private_method_defined_spec.rb b/spec/ruby/core/module/private_method_defined_spec.rb index 951f17aaa7..f730decc0a 100644 --- a/spec/ruby/core/module/private_method_defined_spec.rb +++ b/spec/ruby/core/module/private_method_defined_spec.rb @@ -34,25 +34,25 @@ describe "Module#private_method_defined?" do it "raises a TypeError if passed an Integer" do -> do ModuleSpecs::CountsMixin.private_method_defined?(1) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed nil" do -> do ModuleSpecs::CountsMixin.private_method_defined?(nil) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed false" do -> do ModuleSpecs::CountsMixin.private_method_defined?(false) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed an object that does not defined #to_str" do -> do ModuleSpecs::CountsMixin.private_method_defined?(mock('x')) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed an object that defines #to_sym" do @@ -61,7 +61,7 @@ describe "Module#private_method_defined?" do -> do ModuleSpecs::CountsMixin.private_method_defined?(sym) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "calls #to_str to convert an Object" do @@ -70,53 +70,51 @@ describe "Module#private_method_defined?" do ModuleSpecs::CountsMixin.private_method_defined?(str).should == true end - ruby_version_is "2.6" do - describe "when passed true as a second optional argument" do - it "performs a lookup in ancestors" do - ModuleSpecs::Child.private_method_defined?(:public_child, true).should == false - ModuleSpecs::Child.private_method_defined?(:protected_child, true).should == false - ModuleSpecs::Child.private_method_defined?(:accessor_method, true).should == false - ModuleSpecs::Child.private_method_defined?(:private_child, true).should == true - - # Defined in Parent - ModuleSpecs::Child.private_method_defined?(:public_parent, true).should == false - ModuleSpecs::Child.private_method_defined?(:protected_parent, true).should == false - ModuleSpecs::Child.private_method_defined?(:private_parent, true).should == true - - # Defined in Module - ModuleSpecs::Child.private_method_defined?(:public_module, true).should == false - ModuleSpecs::Child.private_method_defined?(:protected_module, true).should == false - ModuleSpecs::Child.private_method_defined?(:private_module, true).should == true - - # Defined in SuperModule - ModuleSpecs::Child.private_method_defined?(:public_super_module, true).should == false - ModuleSpecs::Child.private_method_defined?(:protected_super_module, true).should == false - ModuleSpecs::Child.private_method_defined?(:private_super_module, true).should == true - end + describe "when passed true as a second optional argument" do + it "performs a lookup in ancestors" do + ModuleSpecs::Child.private_method_defined?(:public_child, true).should == false + ModuleSpecs::Child.private_method_defined?(:protected_child, true).should == false + ModuleSpecs::Child.private_method_defined?(:accessor_method, true).should == false + ModuleSpecs::Child.private_method_defined?(:private_child, true).should == true + + # Defined in Parent + ModuleSpecs::Child.private_method_defined?(:public_parent, true).should == false + ModuleSpecs::Child.private_method_defined?(:protected_parent, true).should == false + ModuleSpecs::Child.private_method_defined?(:private_parent, true).should == true + + # Defined in Module + ModuleSpecs::Child.private_method_defined?(:public_module, true).should == false + ModuleSpecs::Child.private_method_defined?(:protected_module, true).should == false + ModuleSpecs::Child.private_method_defined?(:private_module, true).should == true + + # Defined in SuperModule + ModuleSpecs::Child.private_method_defined?(:public_super_module, true).should == false + ModuleSpecs::Child.private_method_defined?(:protected_super_module, true).should == false + ModuleSpecs::Child.private_method_defined?(:private_super_module, true).should == true end + end - describe "when passed false as a second optional argument" do - it "checks only the class itself" do - ModuleSpecs::Child.private_method_defined?(:public_child, false).should == false - ModuleSpecs::Child.private_method_defined?(:protected_child, false).should == false - ModuleSpecs::Child.private_method_defined?(:accessor_method, false).should == false - ModuleSpecs::Child.private_method_defined?(:private_child, false).should == true - - # Defined in Parent - ModuleSpecs::Child.private_method_defined?(:public_parent, false).should == false - ModuleSpecs::Child.private_method_defined?(:protected_parent, false).should == false - ModuleSpecs::Child.private_method_defined?(:private_parent, false).should == false - - # Defined in Module - ModuleSpecs::Child.private_method_defined?(:public_module, false).should == false - ModuleSpecs::Child.private_method_defined?(:protected_module, false).should == false - ModuleSpecs::Child.private_method_defined?(:private_module, false).should == false - - # Defined in SuperModule - ModuleSpecs::Child.private_method_defined?(:public_super_module, false).should == false - ModuleSpecs::Child.private_method_defined?(:protected_super_module, false).should == false - ModuleSpecs::Child.private_method_defined?(:private_super_module, false).should == false - end + describe "when passed false as a second optional argument" do + it "checks only the class itself" do + ModuleSpecs::Child.private_method_defined?(:public_child, false).should == false + ModuleSpecs::Child.private_method_defined?(:protected_child, false).should == false + ModuleSpecs::Child.private_method_defined?(:accessor_method, false).should == false + ModuleSpecs::Child.private_method_defined?(:private_child, false).should == true + + # Defined in Parent + ModuleSpecs::Child.private_method_defined?(:public_parent, false).should == false + ModuleSpecs::Child.private_method_defined?(:protected_parent, false).should == false + ModuleSpecs::Child.private_method_defined?(:private_parent, false).should == false + + # Defined in Module + ModuleSpecs::Child.private_method_defined?(:public_module, false).should == false + ModuleSpecs::Child.private_method_defined?(:protected_module, false).should == false + ModuleSpecs::Child.private_method_defined?(:private_module, false).should == false + + # Defined in SuperModule + ModuleSpecs::Child.private_method_defined?(:public_super_module, false).should == false + ModuleSpecs::Child.private_method_defined?(:protected_super_module, false).should == false + ModuleSpecs::Child.private_method_defined?(:private_super_module, false).should == false end end end diff --git a/spec/ruby/core/module/private_spec.rb b/spec/ruby/core/module/private_spec.rb index e893c24f38..e60fdb24cc 100644 --- a/spec/ruby/core/module/private_spec.rb +++ b/spec/ruby/core/module/private_spec.rb @@ -17,7 +17,7 @@ describe "Module#private" do private :foo end - -> { obj.foo }.should raise_error(NoMethodError) + -> { obj.foo }.should.raise(NoMethodError) end it "makes a public Object instance method private in a new module" do @@ -25,31 +25,33 @@ describe "Module#private" do private :module_specs_public_method_on_object end - m.should have_private_instance_method(:module_specs_public_method_on_object) + m.private_instance_methods(false).should.include?(:module_specs_public_method_on_object) # Ensure we did not change Object's method - Object.should_not have_private_instance_method(:module_specs_public_method_on_object) + Object.private_instance_methods(true).should_not.include?(:module_specs_public_method_on_object) end it "makes a public Object instance method private in Kernel" do - Kernel.should have_private_instance_method( + Kernel.private_instance_methods(false).should.include?( :module_specs_public_method_on_object_for_kernel_private) - Object.should_not have_private_instance_method( + Object.private_instance_methods(true).should_not.include?( :module_specs_public_method_on_object_for_kernel_private) end - it "returns self" do + it "returns argument or arguments if given" do (class << Object.new; self; end).class_eval do def foo; end - private(:foo).should equal(self) - private.should equal(self) + private(:foo).should.equal?(:foo) + private([:foo, :foo]).should == [:foo, :foo] + private(:foo, :foo).should == [:foo, :foo] + private.should.equal?(nil) end end it "raises a NameError when given an undefined name" do -> do Module.new.send(:private, :undefined) - end.should raise_error(NameError) + end.should.raise(NameError) end it "only makes the method private in the class it is called on" do @@ -69,7 +71,7 @@ describe "Module#private" do base.new.wrapped.should == 1 -> do klass.new.wrapped - end.should raise_error(NameError) + end.should.raise(NameError) end it "continues to allow a prepended module method to call super" do diff --git a/spec/ruby/core/module/protected_instance_methods_spec.rb b/spec/ruby/core/module/protected_instance_methods_spec.rb index 78ce7e788f..ea7ded030e 100644 --- a/spec/ruby/core/module/protected_instance_methods_spec.rb +++ b/spec/ruby/core/module/protected_instance_methods_spec.rb @@ -6,16 +6,16 @@ require_relative '../../fixtures/reflection' describe "Module#protected_instance_methods" do it "returns a list of protected methods in module and its ancestors" do methods = ModuleSpecs::CountsMixin.protected_instance_methods - methods.should include(:protected_3) + methods.should.include?(:protected_3) methods = ModuleSpecs::CountsParent.protected_instance_methods - methods.should include(:protected_3) - methods.should include(:protected_2) + methods.should.include?(:protected_3) + methods.should.include?(:protected_2) methods = ModuleSpecs::CountsChild.protected_instance_methods - methods.should include(:protected_3) - methods.should include(:protected_2) - methods.should include(:protected_1) + methods.should.include?(:protected_3) + methods.should.include?(:protected_2) + methods.should.include?(:protected_1) end it "when passed false as a parameter, should return only methods defined in that module" do diff --git a/spec/ruby/core/module/protected_method_defined_spec.rb b/spec/ruby/core/module/protected_method_defined_spec.rb index e022d7c5c4..78ba120c2f 100644 --- a/spec/ruby/core/module/protected_method_defined_spec.rb +++ b/spec/ruby/core/module/protected_method_defined_spec.rb @@ -34,25 +34,25 @@ describe "Module#protected_method_defined?" do it "raises a TypeError if passed an Integer" do -> do ModuleSpecs::CountsMixin.protected_method_defined?(1) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed nil" do -> do ModuleSpecs::CountsMixin.protected_method_defined?(nil) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed false" do -> do ModuleSpecs::CountsMixin.protected_method_defined?(false) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed an object that does not defined #to_str" do -> do ModuleSpecs::CountsMixin.protected_method_defined?(mock('x')) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed an object that defines #to_sym" do @@ -61,7 +61,7 @@ describe "Module#protected_method_defined?" do -> do ModuleSpecs::CountsMixin.protected_method_defined?(sym) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "calls #to_str to convert an Object" do @@ -70,53 +70,51 @@ describe "Module#protected_method_defined?" do ModuleSpecs::CountsMixin.protected_method_defined?(str).should == true end - ruby_version_is "2.6" do - describe "when passed true as a second optional argument" do - it "performs a lookup in ancestors" do - ModuleSpecs::Child.protected_method_defined?(:public_child, true).should == false - ModuleSpecs::Child.protected_method_defined?(:protected_child, true).should == true - ModuleSpecs::Child.protected_method_defined?(:accessor_method, true).should == false - ModuleSpecs::Child.protected_method_defined?(:private_child, true).should == false - - # Defined in Parent - ModuleSpecs::Child.protected_method_defined?(:public_parent, true).should == false - ModuleSpecs::Child.protected_method_defined?(:protected_parent, true).should == true - ModuleSpecs::Child.protected_method_defined?(:private_parent, true).should == false - - # Defined in Module - ModuleSpecs::Child.protected_method_defined?(:public_module, true).should == false - ModuleSpecs::Child.protected_method_defined?(:protected_module, true).should == true - ModuleSpecs::Child.protected_method_defined?(:private_module, true).should == false - - # Defined in SuperModule - ModuleSpecs::Child.protected_method_defined?(:public_super_module, true).should == false - ModuleSpecs::Child.protected_method_defined?(:protected_super_module, true).should == true - ModuleSpecs::Child.protected_method_defined?(:private_super_module, true).should == false - end + describe "when passed true as a second optional argument" do + it "performs a lookup in ancestors" do + ModuleSpecs::Child.protected_method_defined?(:public_child, true).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_child, true).should == true + ModuleSpecs::Child.protected_method_defined?(:accessor_method, true).should == false + ModuleSpecs::Child.protected_method_defined?(:private_child, true).should == false + + # Defined in Parent + ModuleSpecs::Child.protected_method_defined?(:public_parent, true).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_parent, true).should == true + ModuleSpecs::Child.protected_method_defined?(:private_parent, true).should == false + + # Defined in Module + ModuleSpecs::Child.protected_method_defined?(:public_module, true).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_module, true).should == true + ModuleSpecs::Child.protected_method_defined?(:private_module, true).should == false + + # Defined in SuperModule + ModuleSpecs::Child.protected_method_defined?(:public_super_module, true).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_super_module, true).should == true + ModuleSpecs::Child.protected_method_defined?(:private_super_module, true).should == false end + end - describe "when passed false as a second optional argument" do - it "checks only the class itself" do - ModuleSpecs::Child.protected_method_defined?(:public_child, false).should == false - ModuleSpecs::Child.protected_method_defined?(:protected_child, false).should == true - ModuleSpecs::Child.protected_method_defined?(:accessor_method, false).should == false - ModuleSpecs::Child.protected_method_defined?(:private_child, false).should == false - - # Defined in Parent - ModuleSpecs::Child.protected_method_defined?(:public_parent, false).should == false - ModuleSpecs::Child.protected_method_defined?(:protected_parent, false).should == false - ModuleSpecs::Child.protected_method_defined?(:private_parent, false).should == false - - # Defined in Module - ModuleSpecs::Child.protected_method_defined?(:public_module, false).should == false - ModuleSpecs::Child.protected_method_defined?(:protected_module, false).should == false - ModuleSpecs::Child.protected_method_defined?(:private_module, false).should == false - - # Defined in SuperModule - ModuleSpecs::Child.protected_method_defined?(:public_super_module, false).should == false - ModuleSpecs::Child.protected_method_defined?(:protected_super_module, false).should == false - ModuleSpecs::Child.protected_method_defined?(:private_super_module, false).should == false - end + describe "when passed false as a second optional argument" do + it "checks only the class itself" do + ModuleSpecs::Child.protected_method_defined?(:public_child, false).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_child, false).should == true + ModuleSpecs::Child.protected_method_defined?(:accessor_method, false).should == false + ModuleSpecs::Child.protected_method_defined?(:private_child, false).should == false + + # Defined in Parent + ModuleSpecs::Child.protected_method_defined?(:public_parent, false).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_parent, false).should == false + ModuleSpecs::Child.protected_method_defined?(:private_parent, false).should == false + + # Defined in Module + ModuleSpecs::Child.protected_method_defined?(:public_module, false).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_module, false).should == false + ModuleSpecs::Child.protected_method_defined?(:private_module, false).should == false + + # Defined in SuperModule + ModuleSpecs::Child.protected_method_defined?(:public_super_module, false).should == false + ModuleSpecs::Child.protected_method_defined?(:protected_super_module, false).should == false + ModuleSpecs::Child.protected_method_defined?(:private_super_module, false).should == false end end end diff --git a/spec/ruby/core/module/protected_spec.rb b/spec/ruby/core/module/protected_spec.rb index aa04a42fb8..3ef6c76fae 100644 --- a/spec/ruby/core/module/protected_spec.rb +++ b/spec/ruby/core/module/protected_spec.rb @@ -18,7 +18,7 @@ describe "Module#protected" do protected :protected_method_1 end - -> { ModuleSpecs::Parent.protected_method_1 }.should raise_error(NoMethodError) + -> { ModuleSpecs::Parent.protected_method_1 }.should.raise(NoMethodError) end it "makes a public Object instance method protected in a new module" do @@ -26,30 +26,32 @@ describe "Module#protected" do protected :module_specs_public_method_on_object end - m.should have_protected_instance_method(:module_specs_public_method_on_object) + m.protected_instance_methods(false).should.include?(:module_specs_public_method_on_object) # Ensure we did not change Object's method - Object.should_not have_protected_instance_method(:module_specs_public_method_on_object) + Object.protected_instance_methods(true).should_not.include?(:module_specs_public_method_on_object) end it "makes a public Object instance method protected in Kernel" do - Kernel.should have_protected_instance_method( + Kernel.protected_instance_methods(false).should.include?( :module_specs_public_method_on_object_for_kernel_protected) - Object.should_not have_protected_instance_method( + Object.protected_instance_methods(true).should_not.include?( :module_specs_public_method_on_object_for_kernel_protected) end - it "returns self" do + it "returns argument or arguments if given" do (class << Object.new; self; end).class_eval do def foo; end - protected(:foo).should equal(self) - protected.should equal(self) + protected(:foo).should.equal?(:foo) + protected([:foo, :foo]).should == [:foo, :foo] + protected(:foo, :foo).should == [:foo, :foo] + protected.should.equal?(nil) end end it "raises a NameError when given an undefined name" do -> do Module.new.send(:protected, :undefined) - end.should raise_error(NameError) + end.should.raise(NameError) end end diff --git a/spec/ruby/core/module/public_class_method_spec.rb b/spec/ruby/core/module/public_class_method_spec.rb index c09cc64863..4aca4d4311 100644 --- a/spec/ruby/core/module/public_class_method_spec.rb +++ b/spec/ruby/core/module/public_class_method_spec.rb @@ -18,7 +18,7 @@ describe "Module#public_class_method" do end it "makes an existing class method public" do - -> { ModuleSpecs::Parent.public_method_1 }.should raise_error(NoMethodError) + -> { ModuleSpecs::Parent.public_method_1 }.should.raise(NoMethodError) ModuleSpecs::Parent.public_class_method :public_method_1 ModuleSpecs::Parent.public_method_1.should == nil @@ -29,7 +29,7 @@ describe "Module#public_class_method" do it "makes an existing class method public up the inheritance tree" do ModuleSpecs::Child.private_class_method :public_method_1 - -> { ModuleSpecs::Child.public_method_1 }.should raise_error(NoMethodError) + -> { ModuleSpecs::Child.public_method_1 }.should.raise(NoMethodError) ModuleSpecs::Child.public_class_method :public_method_1 ModuleSpecs::Child.public_method_1.should == nil @@ -37,8 +37,8 @@ describe "Module#public_class_method" do end it "accepts more than one method at a time" do - -> { ModuleSpecs::Parent.public_method_1 }.should raise_error(NameError) - -> { ModuleSpecs::Parent.public_method_2 }.should raise_error(NameError) + -> { ModuleSpecs::Parent.public_method_1 }.should.raise(NameError) + -> { ModuleSpecs::Parent.public_method_2 }.should.raise(NameError) ModuleSpecs::Child.public_class_method :public_method_1, :public_method_2 @@ -49,7 +49,7 @@ describe "Module#public_class_method" do it "raises a NameError if class method doesn't exist" do -> do ModuleSpecs.public_class_method :no_method_here - end.should raise_error(NameError) + end.should.raise(NameError) end it "makes a class method public" do @@ -66,7 +66,7 @@ describe "Module#public_class_method" do Class.new do public_class_method :foo end - end.should raise_error(NameError) + end.should.raise(NameError) end it "raises a NameError when the given name is an instance method" do @@ -75,6 +75,20 @@ describe "Module#public_class_method" do def foo() "foo" end public_class_method :foo end - end.should raise_error(NameError) + end.should.raise(NameError) + end + + context "when single argument is passed and is an array" do + it "makes a class method public" do + c = Class.new do + class << self + private + def foo() "foo" end + end + public_class_method [:foo] + end + + c.foo.should == "foo" + end end end diff --git a/spec/ruby/core/module/public_constant_spec.rb b/spec/ruby/core/module/public_constant_spec.rb index e624d45fd2..87a051f125 100644 --- a/spec/ruby/core/module/public_constant_spec.rb +++ b/spec/ruby/core/module/public_constant_spec.rb @@ -8,7 +8,7 @@ describe "Module#public_constant" do -> do cls2.send :public_constant, :Foo - end.should raise_error(NameError) + end.should.raise(NameError) end it "accepts strings as constant names" do diff --git a/spec/ruby/core/module/public_instance_method_spec.rb b/spec/ruby/core/module/public_instance_method_spec.rb index ba19ad0404..87c1bae599 100644 --- a/spec/ruby/core/module/public_instance_method_spec.rb +++ b/spec/ruby/core/module/public_instance_method_spec.rb @@ -3,7 +3,7 @@ require_relative 'fixtures/classes' describe "Module#public_instance_method" do it "is a public method" do - Module.should have_public_instance_method(:public_instance_method, false) + Module.public_instance_methods(false).should.include?(:public_instance_method) end it "requires an argument" do @@ -13,12 +13,12 @@ describe "Module#public_instance_method" do describe "when given a public method name" do it "returns an UnboundMethod corresponding to the defined Module" do ret = ModuleSpecs::Super.public_instance_method(:public_module) - ret.should be_an_instance_of(UnboundMethod) - ret.owner.should equal(ModuleSpecs::Basic) + ret.should.instance_of?(UnboundMethod) + ret.owner.should.equal?(ModuleSpecs::Basic) ret = ModuleSpecs::Super.public_instance_method(:public_super_module) - ret.should be_an_instance_of(UnboundMethod) - ret.owner.should equal(ModuleSpecs::Super) + ret.should.instance_of?(UnboundMethod) + ret.owner.should.equal?(ModuleSpecs::Super) end it "accepts if the name is a Symbol or String" do @@ -28,31 +28,31 @@ describe "Module#public_instance_method" do end it "raises a TypeError when given a name is not Symbol or String" do - -> { Module.new.public_instance_method(nil) }.should raise_error(TypeError) + -> { Module.new.public_instance_method(nil) }.should.raise(TypeError) end it "raises a NameError when given a protected method name" do -> do ModuleSpecs::Basic.public_instance_method(:protected_module) - end.should raise_error(NameError) + end.should.raise(NameError) end it "raises a NameError if the method is private" do -> do ModuleSpecs::Basic.public_instance_method(:private_module) - end.should raise_error(NameError) + end.should.raise(NameError) end it "raises a NameError if the method has been undefined" do -> do ModuleSpecs::Parent.public_instance_method(:undefed_method) - end.should raise_error(NameError) + end.should.raise(NameError) end it "raises a NameError if the method does not exist" do -> do Module.new.public_instance_method(:missing) - end.should raise_error(NameError) + end.should.raise(NameError) end it "sets the NameError#name attribute to the name of the missing method" do diff --git a/spec/ruby/core/module/public_instance_methods_spec.rb b/spec/ruby/core/module/public_instance_methods_spec.rb index ae7d9b5ffb..edea00d927 100644 --- a/spec/ruby/core/module/public_instance_methods_spec.rb +++ b/spec/ruby/core/module/public_instance_methods_spec.rb @@ -7,19 +7,19 @@ require_relative '../../fixtures/reflection' describe "Module#public_instance_methods" do it "returns a list of public methods in module and its ancestors" do methods = ModuleSpecs::CountsMixin.public_instance_methods - methods.should include(:public_3) + methods.should.include?(:public_3) methods = ModuleSpecs::CountsParent.public_instance_methods - methods.should include(:public_3) - methods.should include(:public_2) + methods.should.include?(:public_3) + methods.should.include?(:public_2) methods = ModuleSpecs::CountsChild.public_instance_methods - methods.should include(:public_3) - methods.should include(:public_2) - methods.should include(:public_1) + methods.should.include?(:public_3) + methods.should.include?(:public_2) + methods.should.include?(:public_1) methods = ModuleSpecs::Child2.public_instance_methods - methods.should include(:foo) + methods.should.include?(:foo) end it "when passed false as a parameter, should return only methods defined in that module" do diff --git a/spec/ruby/core/module/public_method_defined_spec.rb b/spec/ruby/core/module/public_method_defined_spec.rb index 5c9bdf1ccc..70cd992358 100644 --- a/spec/ruby/core/module/public_method_defined_spec.rb +++ b/spec/ruby/core/module/public_method_defined_spec.rb @@ -34,25 +34,25 @@ describe "Module#public_method_defined?" do it "raises a TypeError if passed an Integer" do -> do ModuleSpecs::CountsMixin.public_method_defined?(1) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed nil" do -> do ModuleSpecs::CountsMixin.public_method_defined?(nil) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed false" do -> do ModuleSpecs::CountsMixin.public_method_defined?(false) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed an object that does not defined #to_str" do -> do ModuleSpecs::CountsMixin.public_method_defined?(mock('x')) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "raises a TypeError if passed an object that defines #to_sym" do @@ -61,7 +61,7 @@ describe "Module#public_method_defined?" do -> do ModuleSpecs::CountsMixin.public_method_defined?(sym) - end.should raise_error(TypeError) + end.should.raise(TypeError) end it "calls #to_str to convert an Object" do diff --git a/spec/ruby/core/module/public_spec.rb b/spec/ruby/core/module/public_spec.rb index e7059f6aa6..f35c64143b 100644 --- a/spec/ruby/core/module/public_spec.rb +++ b/spec/ruby/core/module/public_spec.rb @@ -14,31 +14,32 @@ describe "Module#public" do public :module_specs_private_method_on_object end - m.should have_public_instance_method(:module_specs_private_method_on_object) + m.public_instance_methods(false).should.include?(:module_specs_private_method_on_object) # Ensure we did not change Object's method - Object.should_not have_public_instance_method(:module_specs_private_method_on_object) + Object.public_instance_methods(true).should_not.include?(:module_specs_private_method_on_object) end it "makes a private Object instance method public in Kernel" do - Kernel.should have_public_instance_method( + Kernel.public_instance_methods(false).should.include?( :module_specs_private_method_on_object_for_kernel_public) - Object.should_not have_public_instance_method( + Object.public_instance_methods(true).should_not.include?( :module_specs_private_method_on_object_for_kernel_public) end - it "returns self" do + it "returns argument or arguments if given" do (class << Object.new; self; end).class_eval do def foo; end - private :foo - public(:foo).should equal(self) - public.should equal(self) + public(:foo).should.equal?(:foo) + public([:foo, :foo]).should == [:foo, :foo] + public(:foo, :foo).should == [:foo, :foo] + public.should.equal?(nil) end end it "raises a NameError when given an undefined name" do -> do Module.new.send(:public, :undefined) - end.should raise_error(NameError) + end.should.raise(NameError) end end diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb index 54217a9326..d0fc7015f8 100644 --- a/spec/ruby/core/module/refine_spec.rb +++ b/spec/ruby/core/module/refine_spec.rb @@ -11,7 +11,7 @@ describe "Module#refine" do end mod.should_not == inner_self - inner_self.should be_kind_of(Module) + inner_self.should.is_a?(Module) inner_self.name.should == nil end @@ -43,7 +43,7 @@ describe "Module#refine" do end end - inner_self.public_instance_methods.should include(:blah) + inner_self.public_instance_methods.should.include?(:blah) end it "returns created anonymous module" do @@ -63,7 +63,7 @@ describe "Module#refine" do Module.new do refine {} end - end.should raise_error(ArgumentError) + end.should.raise(ArgumentError) end it "raises TypeError if not passed a class" do @@ -71,7 +71,7 @@ describe "Module#refine" do Module.new do refine("foo") {} end - end.should raise_error(TypeError) + end.should.raise(TypeError, "wrong argument type String (expected Class or Module)") end it "accepts a module as argument" do @@ -84,7 +84,7 @@ describe "Module#refine" do end end - inner_self.public_instance_methods.should include(:blah) + inner_self.public_instance_methods.should.include?(:blah) end it "applies refinements to the module" do @@ -117,7 +117,7 @@ describe "Module#refine" do Module.new do refine String end - end.should raise_error(ArgumentError) + end.should.raise(ArgumentError) end it "applies refinements to calls in the refine block" do @@ -136,7 +136,7 @@ describe "Module#refine" do refine(String) {def foo; "foo"; end} -> { "hello".foo - }.should raise_error(NoMethodError) + }.should.raise(NoMethodError) end end @@ -145,7 +145,7 @@ describe "Module#refine" do refine(String) {def foo; 'foo'; end} end - -> {"hello".foo}.should raise_error(NoMethodError) + -> {"hello".foo}.should.raise(NoMethodError) end # When defining multiple refinements in the same module, @@ -209,7 +209,7 @@ describe "Module#refine" do [1, 2].to_json_format end - }.should raise_error(NoMethodError) + }.should.raise(NoMethodError) end # method lookup: @@ -243,34 +243,10 @@ describe "Module#refine" do result.should == "foo from singleton class" end - 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 - 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 @@ -298,65 +274,6 @@ describe "Module#refine" do result.should == "foo from IncludeMeLater" end - 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 - it "looks in the class then" do refined_class = ModuleSpecs.build_refined_class @@ -453,44 +370,22 @@ describe "Module#refine" do result.should == ["(1)", "(2)", "(3)"] end - ruby_version_is "" ... "2.6" do - it "is not honored by Kernel#public_send" do - refined_class = ModuleSpecs.build_refined_class - - refinement = Module.new do - refine refined_class do - def foo; "foo from refinement"; end - end - end + it "is honored by Kernel#public_send" do + refined_class = ModuleSpecs.build_refined_class - result = nil - Module.new do - using refinement - result = refined_class.new.public_send :foo + refinement = Module.new do + refine refined_class do + def foo; "foo from refinement"; end end - - result.should == "foo" end - end - - ruby_version_is "2.6" do - it "is honored by Kernel#public_send" do - refined_class = ModuleSpecs.build_refined_class - refinement = Module.new do - refine refined_class do - def foo; "foo from refinement"; end - end - end - - result = nil - Module.new do - using refinement - result = refined_class.new.public_send :foo - end - - result.should == "foo from refinement" + result = nil + Module.new do + using refinement + result = refined_class.new.public_send :foo end + + result.should == "foo from refinement" end it "is honored by string interpolation" do @@ -536,250 +431,112 @@ describe "Module#refine" do result.should == "hello from refinement" end - ruby_version_is "" ... "2.7" do - it "is not honored by Kernel#method" do - klass = Class.new - refinement = Module.new do - refine klass do - def foo; end - end - end - - -> { - Module.new do - using refinement - klass.new.method(:foo) - end - }.should raise_error(NameError, /undefined method `foo'/) - end - end - - ruby_version_is "2.7" do - it "is honored by Kernel#method" do - klass = Class.new - refinement = Module.new do - refine klass do - def foo; end - end - end - - result = nil - Module.new do - using refinement - result = klass.new.method(:foo).class + it "is honored by Kernel#method" do + klass = Class.new + refinement = Module.new do + refine klass do + def foo; end end - - result.should == Method end - end - ruby_version_is "" ... "2.7" do - it "is not honored by Kernel#instance_method" do - klass = Class.new - refinement = Module.new do - refine klass do - def foo; end - end - end - - -> { - Module.new do - using refinement - klass.instance_method(:foo) - end - }.should raise_error(NameError, /undefined method `foo'/) - end - end - - ruby_version_is "2.7" do - it "is honored by Kernel#method" do - klass = Class.new - refinement = Module.new do - refine klass do - def foo; end - end - end - - result = nil - Module.new do - using refinement - result = klass.instance_method(:foo).class - end - - result.should == UnboundMethod + result = nil + Module.new do + using refinement + result = klass.new.method(:foo).class end - end - - ruby_version_is "" ... "2.6" do - it "is not honored by Kernel#respond_to?" do - klass = Class.new - refinement = Module.new do - refine klass do - def foo; end - end - end - result = nil - Module.new do - using refinement - result = klass.new.respond_to?(:foo) - end - - result.should == false - end + result.should == Method end - ruby_version_is "2.6" do - it "is honored by Kernel#respond_to?" do - klass = Class.new - refinement = Module.new do - refine klass do - def foo; end - end - end - - result = nil - Module.new do - using refinement - result = klass.new.respond_to?(:foo) + it "is honored by Kernel#public_method" do + klass = Class.new + refinement = Module.new do + refine klass do + def foo; end end - - result.should == true end - end - ruby_version_is ""..."2.6" do - it "is not honored by &" do - refinement = Module.new do - refine String do - def to_proc(*args) - -> * { 'foo' } - end - end - end - - -> do - Module.new do - using refinement - ["hola"].map(&"upcase") - end - end.should raise_error(TypeError, /wrong argument type String \(expected Proc\)/) + result = nil + Module.new do + using refinement + result = klass.new.public_method(:foo).class end - end - - ruby_version_is "2.6" do - it "is honored by &" do - refinement = Module.new do - refine String do - def to_proc(*args) - -> * { 'foo' } - end - end - end - - result = nil - Module.new do - using refinement - result = ["hola"].map(&"upcase") - end - result.should == ['foo'] - end + result.should == Method end - end - - context "when super is called in a refinement" do - it "looks in the included to refinery module" do - refined_class = ModuleSpecs.build_refined_class + it "is honored by Kernel#instance_method" do + klass = Class.new refinement = Module.new do - refine refined_class do - include ModuleSpecs::IncludedModule - - def foo - super - end + refine klass do + def foo; end end end result = nil Module.new do using refinement - result = refined_class.new.foo + result = klass.instance_method(:foo).class end - result.should == "foo from included module" + result.should == UnboundMethod end - it "looks in the refined class" do - refined_class = ModuleSpecs.build_refined_class - + it "is honored by Kernel#respond_to?" do + klass = Class.new refinement = Module.new do - refine refined_class do - def foo - super - end + refine klass do + def foo; end end end result = nil Module.new do using refinement - result = refined_class.new.foo + result = klass.new.respond_to?(:foo) end - result.should == "foo" + result.should == true end - 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 - + it "is honored by &" do refinement = Module.new do - refine refined_class do - include a + refine String do + def to_proc(*args) + -> * { 'foo' } + end end end result = nil Module.new do using refinement - - result = refined_class.new.foo + result = ["hola"].map(&"upcase") end - result.should == [:A, :C] + result.should == ['foo'] end + 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 + context "when super is called in a refinement" do + it "looks in the refined class" do + refined_class = ModuleSpecs.build_refined_class refinement = Module.new do refine refined_class do - include a + def foo + super + end end end result = nil Module.new do using refinement - - result = subclass.new.foo + result = refined_class.new.foo end - result.should == [:A, :C] + result.should == "foo" end # super in a method of a refinement invokes the method in the refined @@ -839,179 +596,8 @@ describe "Module#refine" do using refinement_with_super -> { refined_class.new.bar - }.should raise_error(NoMethodError) - end - end - - 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 + }.should.raise(NoMethodError) 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 @@ -1024,7 +610,7 @@ describe "Module#refine" do } [1,2].orig_count.should == 2 end - -> { [1,2].orig_count }.should raise_error(NoMethodError) + -> { [1,2].orig_count }.should.raise(NoMethodError) end it 'and alias_method aliases a method within a refinement module, but not outside it' do @@ -1036,7 +622,7 @@ describe "Module#refine" do } [1,2].orig_count.should == 2 end - -> { [1,2].orig_count }.should raise_error(NoMethodError) + -> { [1,2].orig_count }.should.raise(NoMethodError) end it "and instance_methods returns a list of methods including those of the refined module" do @@ -1118,11 +704,10 @@ describe "Module#refine" do end end end - spec = self - klass = Class.new { instance_methods.should_not spec.send(:include, :refinement_only_method) } + klass = Class.new { instance_methods.should_not.include?(:refinement_only_method) } instance = klass.new - instance.methods.should_not include :refinement_only_method + instance.methods.should_not.include? :refinement_only_method instance.respond_to?(:refinement_only_method).should == false - -> { instance.method :refinement_only_method }.should raise_error(NameError) + -> { instance.method :refinement_only_method }.should.raise(NameError) end end diff --git a/spec/ruby/core/module/refinements_spec.rb b/spec/ruby/core/module/refinements_spec.rb new file mode 100644 index 0000000000..05658a8b0e --- /dev/null +++ b/spec/ruby/core/module/refinements_spec.rb @@ -0,0 +1,43 @@ +require_relative '../../spec_helper' + +describe "Module#refinements" 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 + end + + m.refinements.sort_by(&:object_id).should == ScratchPad.recorded.sort_by(&:object_id) + 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 + + refine String do + ScratchPad << self + end + 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_class_variable_spec.rb b/spec/ruby/core/module/remove_class_variable_spec.rb index ab9514adf6..fccb29f52d 100644 --- a/spec/ruby/core/module/remove_class_variable_spec.rb +++ b/spec/ruby/core/module/remove_class_variable_spec.rb @@ -18,27 +18,27 @@ describe "Module#remove_class_variable" do meta = obj.singleton_class meta.send :class_variable_set, :@@var, 1 meta.send(:remove_class_variable, :@@var).should == 1 - meta.class_variable_defined?(:@@var).should be_false + meta.class_variable_defined?(:@@var).should == false end it "raises a NameError when removing class variable declared in included module" do c = ModuleSpecs::RemoveClassVariable.new { include ModuleSpecs::MVars.dup } - -> { c.send(:remove_class_variable, :@@mvar) }.should raise_error(NameError) + -> { c.send(:remove_class_variable, :@@mvar) }.should.raise(NameError) end it "raises a NameError when passed a symbol with one leading @" do - -> { ModuleSpecs::MVars.send(:remove_class_variable, :@mvar) }.should raise_error(NameError) + -> { ModuleSpecs::MVars.send(:remove_class_variable, :@mvar) }.should.raise(NameError) end it "raises a NameError when passed a symbol with no leading @" do - -> { ModuleSpecs::MVars.send(:remove_class_variable, :mvar) }.should raise_error(NameError) + -> { ModuleSpecs::MVars.send(:remove_class_variable, :mvar) }.should.raise(NameError) end it "raises a NameError when an uninitialized class variable is given" do - -> { ModuleSpecs::MVars.send(:remove_class_variable, :@@nonexisting_class_variable) }.should raise_error(NameError) + -> { ModuleSpecs::MVars.send(:remove_class_variable, :@@nonexisting_class_variable) }.should.raise(NameError) end it "is public" do - Module.should_not have_private_instance_method(:remove_class_variable) + Module.private_instance_methods(true).should_not.include?(:remove_class_variable) end end diff --git a/spec/ruby/core/module/remove_const_spec.rb b/spec/ruby/core/module/remove_const_spec.rb index 20eb2dc3ed..4e4b9fcb45 100644 --- a/spec/ruby/core/module/remove_const_spec.rb +++ b/spec/ruby/core/module/remove_const_spec.rb @@ -7,13 +7,13 @@ describe "Module#remove_const" do ConstantSpecs::ModuleM::CS_CONST252.should == :const252 ConstantSpecs::ModuleM.send :remove_const, :CS_CONST252 - -> { ConstantSpecs::ModuleM::CS_CONST252 }.should raise_error(NameError) + -> { ConstantSpecs::ModuleM::CS_CONST252 }.should.raise(NameError) ConstantSpecs::ModuleM::CS_CONST253 = :const253 ConstantSpecs::ModuleM::CS_CONST253.should == :const253 ConstantSpecs::ModuleM.send :remove_const, "CS_CONST253" - -> { ConstantSpecs::ModuleM::CS_CONST253 }.should raise_error(NameError) + -> { ConstantSpecs::ModuleM::CS_CONST253 }.should.raise(NameError) end it "returns the value of the removed constant" do @@ -23,7 +23,7 @@ describe "Module#remove_const" do it "raises a NameError and does not call #const_missing if the constant is not defined" do ConstantSpecs.should_not_receive(:const_missing) - -> { ConstantSpecs.send(:remove_const, :Nonexistent) }.should raise_error(NameError) + -> { ConstantSpecs.send(:remove_const, :Nonexistent) }.should.raise(NameError) end it "raises a NameError and does not call #const_missing if the constant is not defined directly in the module" do @@ -34,28 +34,28 @@ describe "Module#remove_const" do -> do ConstantSpecs::ContainerA.send :remove_const, :CS_CONST255 - end.should raise_error(NameError) + end.should.raise(NameError) ensure ConstantSpecs::ModuleM.send :remove_const, "CS_CONST255" end end it "raises a NameError if the name does not start with a capital letter" do - -> { ConstantSpecs.send :remove_const, "name" }.should raise_error(NameError) + -> { ConstantSpecs.send :remove_const, "name" }.should.raise(NameError) end it "raises a NameError if the name starts with a non-alphabetic character" do - -> { ConstantSpecs.send :remove_const, "__CONSTX__" }.should raise_error(NameError) - -> { ConstantSpecs.send :remove_const, "@Name" }.should raise_error(NameError) - -> { ConstantSpecs.send :remove_const, "!Name" }.should raise_error(NameError) - -> { ConstantSpecs.send :remove_const, "::Name" }.should raise_error(NameError) + -> { ConstantSpecs.send :remove_const, "__CONSTX__" }.should.raise(NameError) + -> { ConstantSpecs.send :remove_const, "@Name" }.should.raise(NameError) + -> { ConstantSpecs.send :remove_const, "!Name" }.should.raise(NameError) + -> { ConstantSpecs.send :remove_const, "::Name" }.should.raise(NameError) end it "raises a NameError if the name contains non-alphabetic characters except '_'" do ConstantSpecs::ModuleM::CS_CONST256 = :const256 ConstantSpecs::ModuleM.send :remove_const, "CS_CONST256" - -> { ConstantSpecs.send :remove_const, "Name=" }.should raise_error(NameError) - -> { ConstantSpecs.send :remove_const, "Name?" }.should raise_error(NameError) + -> { ConstantSpecs.send :remove_const, "Name=" }.should.raise(NameError) + -> { ConstantSpecs.send :remove_const, "Name?" }.should.raise(NameError) end it "calls #to_str to convert the given name to a String" do @@ -67,18 +67,41 @@ describe "Module#remove_const" do it "raises a TypeError if conversion to a String by calling #to_str fails" do name = mock('123') - -> { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError) + -> { ConstantSpecs.send :remove_const, name }.should.raise(TypeError) name.should_receive(:to_str).and_return(123) - -> { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError) + -> { ConstantSpecs.send :remove_const, name }.should.raise(TypeError) end it "is a private method" do - Module.private_methods.should include(:remove_const) + Module.private_methods.should.include?(:remove_const) end it "returns nil when removing autoloaded constant" do ConstantSpecs.autoload :AutoloadedConstant, 'a_file' - ConstantSpecs.send(:remove_const, :AutoloadedConstant).should be_nil + ConstantSpecs.send(:remove_const, :AutoloadedConstant).should == nil + end + + it "updates the constant value" do + module ConstantSpecs::RemovedConstantUpdate + module M + FOO = 'm' + end + + module A + include M + FOO = 'a' + def self.foo + FOO + end + end + + A.foo.should == 'a' + + 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/remove_method_spec.rb b/spec/ruby/core/module/remove_method_spec.rb index b6ae02078f..39add01e36 100644 --- a/spec/ruby/core/module/remove_method_spec.rb +++ b/spec/ruby/core/module/remove_method_spec.rb @@ -21,7 +21,7 @@ describe "Module#remove_method" do end it "is a public method" do - Module.should have_public_instance_method(:remove_method, false) + Module.public_instance_methods(false).should.include?(:remove_method) end it "removes the method from a class" do @@ -43,6 +43,28 @@ describe "Module#remove_method" do x.method_to_remove.should == 1 end + it "updates the method implementation" do + m_module = Module.new do + def foo + 'm' + end + end + + a_class = Class.new do + include m_module + + def foo + 'a' + end + end + + a = a_class.new + foo = -> { a.foo } + foo.call.should == 'a' + a_class.remove_method(:foo) + foo.call.should == 'm' + end + it "removes multiple methods with 1 call" do klass = Class.new do def method_to_remove_1; 1; end @@ -66,14 +88,14 @@ describe "Module#remove_method" do end it "returns self" do - @module.send(:remove_method, :method_to_remove).should equal(@module) + @module.send(:remove_method, :method_to_remove).should.equal?(@module) end it "raises a NameError when attempting to remove method further up the inheritance tree" do Class.new(ModuleSpecs::Second) do -> { remove_method :method_to_remove - }.should raise_error(NameError) + }.should.raise(NameError) end end @@ -81,7 +103,7 @@ describe "Module#remove_method" do Class.new(ModuleSpecs::Second) do -> { remove_method :blah - }.should raise_error(NameError) + }.should.raise(NameError) end end @@ -91,19 +113,19 @@ describe "Module#remove_method" do end it "raises a FrozenError when passed a name" do - -> { @frozen.send :remove_method, :method_to_remove }.should raise_error(FrozenError) + -> { @frozen.send :remove_method, :method_to_remove }.should.raise(FrozenError) end it "raises a FrozenError when passed a missing name" do - -> { @frozen.send :remove_method, :not_exist }.should raise_error(FrozenError) + -> { @frozen.send :remove_method, :not_exist }.should.raise(FrozenError) end it "raises a TypeError when passed a not name" do - -> { @frozen.send :remove_method, Object.new }.should raise_error(TypeError) + -> { @frozen.send :remove_method, Object.new }.should.raise(TypeError) end it "does not raise exceptions when no arguments given" do - @frozen.send(:remove_method).should equal(@frozen) + @frozen.send(:remove_method).should.equal?(@frozen) end end end diff --git a/spec/ruby/core/module/ruby2_keywords_spec.rb b/spec/ruby/core/module/ruby2_keywords_spec.rb new file mode 100644 index 0000000000..e392642978 --- /dev/null +++ b/spec/ruby/core/module/ruby2_keywords_spec.rb @@ -0,0 +1,248 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Module#ruby2_keywords" do + class << self + ruby2_keywords def mark(*args) + args + end + end + + it "marks the final hash argument as keyword hash" do + last = mark(1, 2, a: "a").last + Hash.ruby2_keywords_hash?(last).should == true + end + + it "makes a copy of the hash and only marks the copy as keyword hash" do + obj = Object.new + obj.singleton_class.class_exec do + def regular(*args) + args.last + end + end + + h = {a: 1} + + last = mark(**h).last + Hash.ruby2_keywords_hash?(last).should == true + Hash.ruby2_keywords_hash?(h).should == false + + last2 = mark(**last).last # last is already marked + Hash.ruby2_keywords_hash?(last2).should == true + Hash.ruby2_keywords_hash?(last).should == true + last2.should_not.equal?(last) + Hash.ruby2_keywords_hash?(h).should == false + end + + it "makes a copy and unmark the Hash when calling a method taking (arg)" do + obj = Object.new + obj.singleton_class.class_exec do + def single(arg) + arg + end + end + + h = { a: 1 } + args = mark(**h) + marked = args.last + Hash.ruby2_keywords_hash?(marked).should == true + + after_usage = obj.single(*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 "makes a copy and unmark the Hash when calling a method taking (**kw)" do + obj = Object.new + obj.singleton_class.class_exec do + def kwargs(**kw) + kw + end + end + + h = { a: 1 } + args = mark(**h) + marked = args.last + Hash.ruby2_keywords_hash?(marked).should == true + + after_usage = obj.kwargs(*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 "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 + + def proc_call(*args) + -> *a { a.last }.call(*args) + end + 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 + end + + it "applies to the underlying method and applies across aliasing" do + obj = Object.new + + obj.singleton_class.class_exec do + def foo(*a) a.last end + alias_method :bar, :foo + ruby2_keywords :foo + + def baz(*a) a.last end + ruby2_keywords :baz + alias_method :bob, :baz + end + + last = obj.foo(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + + last = obj.bar(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + + last = obj.baz(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + + last = obj.bob(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + end + + it "returns nil" do + obj = Object.new + + obj.singleton_class.class_exec do + def foo(*a) end + + ruby2_keywords(:foo).should == nil + end + end + + it "raises NameError when passed not existing method name" do + obj = Object.new + + -> { + obj.singleton_class.class_exec do + ruby2_keywords :not_existing + end + }.should.raise(NameError, /undefined method [`']not_existing'/) + end + + it "accepts String as well" do + obj = Object.new + + obj.singleton_class.class_exec do + def foo(*a) a.last end + ruby2_keywords "foo" + end + + last = obj.foo(1, 2, a: "a") + Hash.ruby2_keywords_hash?(last).should == true + end + + it "raises TypeError when passed not Symbol or String" do + obj = Object.new + + -> { + obj.singleton_class.class_exec do + ruby2_keywords Object.new + end + }.should.raise(TypeError, /is not a symbol nor a string/) + end + + it "prints warning when a method does not accept argument splat" do + obj = Object.new + def obj.foo(a, b, c) end + + -> { + obj.singleton_class.class_exec do + ruby2_keywords :foo + end + }.should complain(/Skipping set of ruby2_keywords flag for/) + end + + it "prints warning when a method accepts keywords" 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 + + it "prints warning when a method accepts keyword splat" 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 + + 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 new file mode 100644 index 0000000000..7c159121fa --- /dev/null +++ b/spec/ruby/core/module/set_temporary_name_spec.rb @@ -0,0 +1,145 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/set_temporary_name' + +describe "Module#set_temporary_name" do + it "can assign a temporary name" do + m = Module.new + m.name.should == nil + + m.set_temporary_name("fake_name") + m.name.should == "fake_name" + + m.set_temporary_name(nil) + m.name.should == 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("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 + m = Module.new + -> { m.set_temporary_name("") }.should.raise(ArgumentError, "empty class/module name") + end + + it "can't assign a constant name as a temporary name" do + m = Module.new + -> { m.set_temporary_name("Object") }.should.raise(ArgumentError, "the temporary name must not be a constant path to avoid confusion") + end + + it "can't assign a constant path as a temporary name" do + m = Module.new + -> { m.set_temporary_name("A::B") }.should.raise(ArgumentError, "the temporary name must not be a constant path to avoid confusion") + -> { m.set_temporary_name("::A") }.should.raise(ArgumentError, "the temporary name must not be a constant path to avoid confusion") + -> { m.set_temporary_name("::A::B") }.should.raise(ArgumentError, "the temporary name must not be a constant path to avoid confusion") + end + + it "can't assign name to permanent module" do + -> { Object.set_temporary_name("fake_name") }.should.raise(RuntimeError, "can't change permanent name") + end + + 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/ + + m::N.set_temporary_name("fake_name") + m::N.name.should == "fake_name" + + m::N.set_temporary_name(nil) + m::N.name.should == 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 + m::N.name.should =~ /\A#<Module:0x\h+>::N\z/ + m::N.set_temporary_name(nil) + + 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 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/shared/class_eval.rb b/spec/ruby/core/module/shared/class_eval.rb index 224078ae54..ee2860449a 100644 --- a/spec/ruby/core/module/shared/class_eval.rb +++ b/spec/ruby/core/module/shared/class_eval.rb @@ -14,7 +14,7 @@ describe :module_class_eval, shared: true do 'foo' end end - -> {42.foo}.should raise_error(NoMethodError) + -> {42.foo}.should.raise(NoMethodError) end it "resolves constants in the caller scope" do @@ -45,50 +45,63 @@ describe :module_class_eval, shared: true do ModuleSpecs.send(@method) do |block_parameter| given = block_parameter end - given.should equal ModuleSpecs + given.should.equal? ModuleSpecs end it "uses the optional filename and lineno parameters for error messages" do ModuleSpecs.send(@method, "[__FILE__, __LINE__]", "test", 102).should == ["test", 102] end + it "uses the caller location as default filename" do + ModuleSpecs.send(@method, "[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1] + end + it "converts a non-string filename to a string using to_str" do (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) ModuleSpecs.send(@method, "1+1", file) + + (file = mock(__FILE__)).should_receive(:to_str).and_return(__FILE__) + ModuleSpecs.send(@method, "1+1", file, 15) end it "raises a TypeError when the given filename can't be converted to string using to_str" do (file = mock('123')).should_receive(:to_str).and_return(123) - -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError) + -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_consistent_error(TypeError, /can't convert MockObject into String/) end it "converts non string eval-string to string using to_str" do (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") ModuleSpecs.send(@method, o).should == 2 + + (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") + ModuleSpecs.send(@method, o, "file.rb").should == 2 + + (o = mock('1 + 1')).should_receive(:to_str).and_return("1 + 1") + ModuleSpecs.send(@method, o, "file.rb", 15).should == 2 end it "raises a TypeError when the given eval-string can't be converted to string using to_str" do o = mock('x') - -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError) + -> { ModuleSpecs.send(@method, o) }.should.raise(TypeError, "no implicit conversion of MockObject into String") (o = mock('123')).should_receive(:to_str).and_return(123) - -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError) + -> { ModuleSpecs.send(@method, o) }.should raise_consistent_error(TypeError, /can't convert MockObject into String/) end it "raises an ArgumentError when no arguments and no block are given" do - -> { ModuleSpecs.send(@method) }.should raise_error(ArgumentError) + -> { ModuleSpecs.send(@method) }.should.raise(ArgumentError, "wrong number of arguments (given 0, expected 1..3)") end it "raises an ArgumentError when more than 3 arguments are given" do -> { ModuleSpecs.send(@method, "1 + 1", "some file", 0, "bogus") - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError, "wrong number of arguments (given 4, expected 1..3)") end it "raises an ArgumentError when a block and normal arguments are given" do -> { ModuleSpecs.send(@method, "1 + 1") { 1 + 1 } - }.should raise_error(ArgumentError) + }.should.raise(ArgumentError, "wrong number of arguments (given 1, expected 0)") end # This case was found because Rubinius was caching the compiled diff --git a/spec/ruby/core/module/shared/class_exec.rb b/spec/ruby/core/module/shared/class_exec.rb index c7a9e5297f..e51af1966d 100644 --- a/spec/ruby/core/module/shared/class_exec.rb +++ b/spec/ruby/core/module/shared/class_exec.rb @@ -5,7 +5,7 @@ describe :module_class_exec, shared: true do 'foo' end end - -> {42.foo}.should raise_error(NoMethodError) + -> {42.foo}.should.raise(NoMethodError) end it "defines method in the receiver's scope" do @@ -19,11 +19,17 @@ describe :module_class_exec, shared: true do end it "raises a LocalJumpError when no block is given" do - -> { ModuleSpecs::Subclass.send(@method) }.should raise_error(LocalJumpError) + -> { ModuleSpecs::Subclass.send(@method) }.should.raise(LocalJumpError) end it "passes arguments to the block" do a = ModuleSpecs::Subclass - a.send(@method, 1) { |b| b }.should equal(1) + a.send(@method, 1) { |b| b }.should.equal?(1) + end + + describe "with optional argument" do + it "does not destructure a single array argument" do + ModuleSpecs::Subclass.send(@method, [1, 2, 3]) { |a = 99| a }.should == [1, 2, 3] + end end end diff --git a/spec/ruby/core/module/shared/set_visibility.rb b/spec/ruby/core/module/shared/set_visibility.rb index 9f31e230ca..38cc2ad260 100644 --- a/spec/ruby/core/module/shared/set_visibility.rb +++ b/spec/ruby/core/module/shared/set_visibility.rb @@ -2,7 +2,7 @@ describe :set_visibility, shared: true do it "is a private method" do - Module.should have_private_instance_method(@method, false) + Module.private_instance_methods(false).should.include?(@method) end describe "with argument" do @@ -17,26 +17,24 @@ describe :set_visibility, shared: true do def test2() end send visibility, :test1, :test2 } - mod.should send(:"have_#{visibility}_instance_method", :test1, false) - mod.should send(:"have_#{visibility}_instance_method", :test2, false) + mod.send(:"#{visibility}_instance_methods", false).should.include?(:test1) + mod.send(:"#{visibility}_instance_methods", false).should.include?(:test2) end end - ruby_version_is "3.0" do - describe "array as a single argument" do - it "sets visibility of given method names" do - visibility = @method - old_visibility = [:protected, :private].find {|vis| vis != visibility } - - mod = Module.new { - send old_visibility - def test1() end - def test2() end - send visibility, [:test1, :test2] - } - mod.should send(:"have_#{visibility}_instance_method", :test1, false) - mod.should send(:"have_#{visibility}_instance_method", :test2, false) - end + describe "array as a single argument" do + it "sets visibility of given method names" do + visibility = @method + old_visibility = [:protected, :private].find {|vis| vis != visibility } + + mod = Module.new { + send old_visibility + def test1() end + def test2() end + send visibility, [:test1, :test2] + } + mod.send(:"#{visibility}_instance_methods", false).should.include?(:test1) + mod.send(:"#{visibility}_instance_methods", false).should.include?(:test2) end end @@ -52,7 +50,7 @@ describe :set_visibility, shared: true do send(visibility, :test_method) } - child.should_not send(:"have_#{visibility}_instance_method", :test_method, false) + child.send(:"#{visibility}_instance_methods", false).should_not.include?(:test_method) end end @@ -66,8 +64,8 @@ describe :set_visibility, shared: true do def test2() end } - mod.should send(:"have_#{@method}_instance_method", :test1, false) - mod.should send(:"have_#{@method}_instance_method", :test2, false) + mod.send(:"#{@method}_instance_methods", false).should.include?(:test1) + mod.send(:"#{@method}_instance_methods", false).should.include?(:test2) end it "stops setting visibility if the body encounters other visibility setters without arguments" do @@ -80,7 +78,7 @@ describe :set_visibility, shared: true do def test1() end } - mod.should send(:"have_#{new_visibility}_instance_method", :test1, false) + mod.send(:"#{new_visibility}_instance_methods", false).should.include?(:test1) end it "continues setting visibility if the body encounters other visibility setters with arguments" do @@ -92,7 +90,7 @@ describe :set_visibility, shared: true do def test2() end } - mod.should send(:"have_#{@method}_instance_method", :test2, false) + mod.send(:"#{@method}_instance_methods", false).should.include?(:test2) end it "does not affect module_evaled method definitions when itself is outside the eval" do @@ -104,8 +102,8 @@ describe :set_visibility, shared: true do module_eval " def test2() end " } - mod.should have_public_instance_method(:test1, false) - mod.should have_public_instance_method(:test2, false) + mod.public_instance_methods(false).should.include?(:test1) + mod.public_instance_methods(false).should.include?(:test2) end it "does not affect outside method definitions when itself is inside a module_eval" do @@ -116,7 +114,7 @@ describe :set_visibility, shared: true do def test1() end } - mod.should have_public_instance_method(:test1, false) + mod.public_instance_methods(false).should.include?(:test1) end it "affects normally if itself and method definitions are inside a module_eval" do @@ -129,7 +127,7 @@ describe :set_visibility, shared: true do } } - mod.should send(:"have_#{@method}_instance_method", :test1, false) + mod.send(:"#{@method}_instance_methods", false).should.include?(:test1) end it "does not affect method definitions when itself is inside an eval and method definitions are outside" do @@ -142,7 +140,7 @@ describe :set_visibility, shared: true do def test1() end } - mod.should send(:"have_#{initialized_visibility}_instance_method", :test1, false) + mod.send(:"#{initialized_visibility}_instance_methods", false).should.include?(:test1) end it "affects evaled method definitions when itself is outside the eval" do @@ -153,7 +151,7 @@ describe :set_visibility, shared: true do eval "def test1() end" } - mod.should send(:"have_#{@method}_instance_method", :test1, false) + mod.send(:"#{@method}_instance_methods", false).should.include?(:test1) end it "affects normally if itself and following method definitions are inside a eval" do @@ -166,7 +164,7 @@ describe :set_visibility, shared: true do CODE } - mod.should send(:"have_#{@method}_instance_method", :test1, false) + mod.send(:"#{@method}_instance_methods", false).should.include?(:test1) end describe "within a closure" do @@ -179,7 +177,7 @@ describe :set_visibility, shared: true do def test1() end } - mod.should send(:"have_#{@method}_instance_method", :test1, false) + mod.send(:"#{@method}_instance_methods", false).should.include?(:test1) end end end diff --git a/spec/ruby/core/module/to_s_spec.rb b/spec/ruby/core/module/to_s_spec.rb index 29f6ecf726..83c0ae0825 100644 --- a/spec/ruby/core/module/to_s_spec.rb +++ b/spec/ruby/core/module/to_s_spec.rb @@ -42,4 +42,29 @@ describe "Module#to_s" do obj = ModuleSpecs::NamedClass.new obj.singleton_class.to_s.should =~ /\A#<Class:#<ModuleSpecs::NamedClass:0x\h+>>\z/ end + + it "always show the refinement name, even if the module is named" do + module ModuleSpecs::RefinementInspect + R = refine String do + end + end + + 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 + klass = Class.new + obj = klass.new + def obj.to_s + "to_s" + end + def obj.inspect + "inspect" + end + sclass = obj.singleton_class + sclass.to_s.should =~ /\A#<Class:#<#{Regexp.escape klass.to_s}:0x\h+>>\z/ + end end diff --git a/spec/ruby/core/module/undef_method_spec.rb b/spec/ruby/core/module/undef_method_spec.rb index c2ad200536..d77640cb7e 100644 --- a/spec/ruby/core/module/undef_method_spec.rb +++ b/spec/ruby/core/module/undef_method_spec.rb @@ -19,7 +19,7 @@ describe "Module#undef_method" do end it "is a public method" do - Module.should have_public_instance_method(:undef_method, false) + Module.public_instance_methods(false).should.include?(:undef_method) end it "requires multiple arguments" do @@ -34,8 +34,8 @@ describe "Module#undef_method" do x = klass.new klass.send(:undef_method, :method_to_undef, :another_method_to_undef) - -> { x.method_to_undef }.should raise_error(NoMethodError) - -> { x.another_method_to_undef }.should raise_error(NoMethodError) + -> { x.method_to_undef }.should.raise(NoMethodError) + -> { x.another_method_to_undef }.should.raise(NoMethodError) end it "does not undef any instance methods when argument not given" do @@ -46,11 +46,11 @@ describe "Module#undef_method" do end it "returns self" do - @module.send(:undef_method, :method_to_undef).should equal(@module) + @module.send(:undef_method, :method_to_undef).should.equal?(@module) 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(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(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(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(NameError, /undefined method [`']not_exist' for class [`']String'/) { |e| # a NameError and not a NoMethodError e.class.should == NameError } @@ -91,19 +91,19 @@ describe "Module#undef_method" do end it "raises a FrozenError when passed a name" do - -> { @frozen.send :undef_method, :method_to_undef }.should raise_error(FrozenError) + -> { @frozen.send :undef_method, :method_to_undef }.should.raise(FrozenError) end it "raises a FrozenError when passed a missing name" do - -> { @frozen.send :undef_method, :not_exist }.should raise_error(FrozenError) + -> { @frozen.send :undef_method, :not_exist }.should.raise(FrozenError) end it "raises a TypeError when passed a not name" do - -> { @frozen.send :undef_method, Object.new }.should raise_error(TypeError) + -> { @frozen.send :undef_method, Object.new }.should.raise(TypeError) end it "does not raise exceptions when no arguments given" do - @frozen.send(:undef_method).should equal(@frozen) + @frozen.send(:undef_method).should.equal?(@frozen) end end end @@ -120,7 +120,7 @@ describe "Module#undef_method with symbol" do klass.send :undef_method, :method_to_undef - -> { x.method_to_undef }.should raise_error(NoMethodError) + -> { x.method_to_undef }.should.raise(NoMethodError) end it "removes a method defined in a super class" do @@ -130,7 +130,7 @@ describe "Module#undef_method with symbol" do child_class.send :undef_method, :method_to_undef - -> { child.method_to_undef }.should raise_error(NoMethodError) + -> { child.method_to_undef }.should.raise(NoMethodError) end it "does not remove a method defined in a super class when removed from a subclass" do @@ -156,7 +156,7 @@ describe "Module#undef_method with string" do klass.send :undef_method, 'another_method_to_undef' - -> { x.another_method_to_undef }.should raise_error(NoMethodError) + -> { x.another_method_to_undef }.should.raise(NoMethodError) end it "removes a method defined in a super class" do @@ -166,7 +166,7 @@ describe "Module#undef_method with string" do child_class.send :undef_method, 'another_method_to_undef' - -> { child.another_method_to_undef }.should raise_error(NoMethodError) + -> { child.another_method_to_undef }.should.raise(NoMethodError) end it "does not remove a method defined in a super class when removed from a subclass" do diff --git a/spec/ruby/core/module/undefined_instance_methods_spec.rb b/spec/ruby/core/module/undefined_instance_methods_spec.rb new file mode 100644 index 0000000000..9f731c6adf --- /dev/null +++ b/spec/ruby/core/module/undefined_instance_methods_spec.rb @@ -0,0 +1,25 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/classes' + +describe "Module#undefined_instance_methods" do + 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.to_set.should >= Set[: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 "does not returns ancestors undefined methods" do + methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods + methods.should_not.include?(:parent_method) + methods.should_not.include?(: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 new file mode 100644 index 0000000000..40dd4a444e --- /dev/null +++ b/spec/ruby/core/module/used_refinements_spec.rb @@ -0,0 +1,85 @@ +require_relative '../../spec_helper' + +describe "Module.used_refinements" 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 + end + + m2 = Module.new do + refine String do + refinement_str = self + end + end + + 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) + end + + it "returns empty array if does not have any refinements imported" do + used_refinements = nil + + Module.new do + used_refinements = Module.used_refinements + end + + used_refinements.should == [] + end + + it "ignores refinements imported in a module that is included into the current one" do + used_refinements = nil + + m1 = Module.new do + refine Integer do + nil + end + end + + m2 = Module.new do + using m1 + end + + Module.new do + include m2 + + used_refinements = Module.used_refinements + end + + used_refinements.should == [] + end + + it "returns refinements even not defined directly in a module refinements are imported from" do + used_refinements = nil + ScratchPad.record [] + + m1 = Module.new do + refine Integer do + ScratchPad << self + end + end + + m2 = Module.new do + include m1 + end + + Module.new do + using m2 + + 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..cff0edef28 100644 --- a/spec/ruby/core/module/using_spec.rb +++ b/spec/ruby/core/module/using_spec.rb @@ -28,7 +28,7 @@ describe "Module#using" do Module.new do using refinement end - }.should_not raise_error + }.should_not.raise end it "accepts module without refinements" do @@ -38,7 +38,7 @@ describe "Module#using" do Module.new do using mod end - }.should_not raise_error + }.should_not.raise end it "does not accept class" do @@ -48,7 +48,7 @@ describe "Module#using" do Module.new do using klass end - }.should raise_error(TypeError) + }.should.raise(TypeError) end it "raises TypeError if passed something other than module" do @@ -56,7 +56,7 @@ describe "Module#using" do Module.new do using "foo" end - }.should raise_error(TypeError) + }.should.raise(TypeError) end it "returns self" do @@ -67,7 +67,7 @@ describe "Module#using" do result = using refinement end - result.should equal(mod) + result.should.equal?(mod) end it "works in classes too" do @@ -95,7 +95,7 @@ describe "Module#using" do -> { mod.foo - }.should raise_error(RuntimeError, /Module#using is not permitted in methods/) + }.should.raise(RuntimeError, /Module#using is not permitted in methods/) end it "activates refinement even for existed objects" do @@ -316,7 +316,7 @@ describe "Module#using" do using refinement def initialize - @a = "1703" + @a = +"1703" @a.instance_eval do def abc |
