summaryrefslogtreecommitdiff
path: root/spec/ruby/core/module
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/core/module')
-rw-r--r--spec/ruby/core/module/alias_method_spec.rb56
-rw-r--r--spec/ruby/core/module/allocate_spec.rb14
-rw-r--r--spec/ruby/core/module/ancestors_spec.rb30
-rw-r--r--spec/ruby/core/module/append_features_spec.rb26
-rw-r--r--spec/ruby/core/module/attr_accessor_spec.rb55
-rw-r--r--spec/ruby/core/module/attr_reader_spec.rb30
-rw-r--r--spec/ruby/core/module/attr_spec.rb43
-rw-r--r--spec/ruby/core/module/attr_writer_spec.rb40
-rw-r--r--spec/ruby/core/module/autoload_spec.rb696
-rw-r--r--spec/ruby/core/module/case_compare_spec.rb4
-rw-r--r--spec/ruby/core/module/class_eval_spec.rb6
-rw-r--r--spec/ruby/core/module/class_exec_spec.rb6
-rw-r--r--spec/ruby/core/module/class_variable_defined_spec.rb14
-rw-r--r--spec/ruby/core/module/class_variable_get_spec.rb22
-rw-r--r--spec/ruby/core/module/class_variable_set_spec.rb24
-rw-r--r--spec/ruby/core/module/class_variables_spec.rb12
-rw-r--r--spec/ruby/core/module/comparison_spec.rb6
-rw-r--r--spec/ruby/core/module/const_added_spec.rb238
-rw-r--r--spec/ruby/core/module/const_defined_spec.rb57
-rw-r--r--spec/ruby/core/module/const_get_spec.rb115
-rw-r--r--spec/ruby/core/module/const_missing_spec.rb15
-rw-r--r--spec/ruby/core/module/const_set_spec.rb89
-rw-r--r--spec/ruby/core/module/const_source_location_spec.rb281
-rw-r--r--spec/ruby/core/module/constants_spec.rb26
-rw-r--r--spec/ruby/core/module/define_method_spec.rb339
-rw-r--r--spec/ruby/core/module/define_singleton_method_spec.rb6
-rw-r--r--spec/ruby/core/module/deprecate_constant_spec.rb90
-rw-r--r--spec/ruby/core/module/eql_spec.rb8
-rw-r--r--spec/ruby/core/module/equal_spec.rb8
-rw-r--r--spec/ruby/core/module/equal_value_spec.rb8
-rw-r--r--spec/ruby/core/module/extend_object_spec.rb20
-rw-r--r--spec/ruby/core/module/extended_spec.rb4
-rw-r--r--spec/ruby/core/module/fixtures/autoload_callback.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_const_source_location.rb6
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_autoload.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_autoload_after_define.rb6
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_require.rb4
-rw-r--r--spec/ruby/core/module/fixtures/autoload_during_require_current_file.rb5
-rw-r--r--spec/ruby/core/module/fixtures/autoload_exception.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_location.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_nested.rb8
-rw-r--r--spec/ruby/core/module/fixtures/autoload_o.rb1
-rw-r--r--spec/ruby/core/module/fixtures/autoload_overridden.rb3
-rw-r--r--spec/ruby/core/module/fixtures/autoload_raise.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_required_directly.rb7
-rw-r--r--spec/ruby/core/module/fixtures/autoload_required_directly_nested.rb1
-rw-r--r--spec/ruby/core/module/fixtures/autoload_required_directly_no_constant.rb2
-rw-r--r--spec/ruby/core/module/fixtures/autoload_scope.rb8
-rw-r--r--spec/ruby/core/module/fixtures/autoload_self_during_require.rb5
-rw-r--r--spec/ruby/core/module/fixtures/autoload_subclass.rb4
-rw-r--r--spec/ruby/core/module/fixtures/classes.rb50
-rw-r--r--spec/ruby/core/module/fixtures/const_added.rb4
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload.rb6
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_a.rb2
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_b.rb2
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_c.rb3
-rw-r--r--spec/ruby/core/module/fixtures/constants_autoload_d.rb4
-rw-r--r--spec/ruby/core/module/fixtures/module.rb4
-rw-r--r--spec/ruby/core/module/fixtures/multi/foo.rb6
-rw-r--r--spec/ruby/core/module/fixtures/multi/foo/bar_baz.rb11
-rw-r--r--spec/ruby/core/module/fixtures/name.rb3
-rw-r--r--spec/ruby/core/module/fixtures/refine.rb12
-rw-r--r--spec/ruby/core/module/fixtures/set_temporary_name.rb4
-rw-r--r--spec/ruby/core/module/freeze_spec.rb4
-rw-r--r--spec/ruby/core/module/gt_spec.rb6
-rw-r--r--spec/ruby/core/module/gte_spec.rb6
-rw-r--r--spec/ruby/core/module/include_spec.rb412
-rw-r--r--spec/ruby/core/module/included_modules_spec.rb6
-rw-r--r--spec/ruby/core/module/included_spec.rb6
-rw-r--r--spec/ruby/core/module/initialize_copy_spec.rb10
-rw-r--r--spec/ruby/core/module/initialize_spec.rb4
-rw-r--r--spec/ruby/core/module/instance_method_spec.rb45
-rw-r--r--spec/ruby/core/module/instance_methods_spec.rb4
-rw-r--r--spec/ruby/core/module/lt_spec.rb6
-rw-r--r--spec/ruby/core/module/lte_spec.rb6
-rw-r--r--spec/ruby/core/module/method_added_spec.rb96
-rw-r--r--spec/ruby/core/module/method_defined_spec.rb59
-rw-r--r--spec/ruby/core/module/method_removed_spec.rb4
-rw-r--r--spec/ruby/core/module/method_undefined_spec.rb4
-rw-r--r--spec/ruby/core/module/module_eval_spec.rb6
-rw-r--r--spec/ruby/core/module/module_exec_spec.rb6
-rw-r--r--spec/ruby/core/module/module_function_spec.rb173
-rw-r--r--spec/ruby/core/module/name_spec.rb151
-rw-r--r--spec/ruby/core/module/nesting_spec.rb4
-rw-r--r--spec/ruby/core/module/new_spec.rb8
-rw-r--r--spec/ruby/core/module/prepend_features_spec.rb20
-rw-r--r--spec/ruby/core/module/prepend_spec.rb516
-rw-r--r--spec/ruby/core/module/prepended_spec.rb2
-rw-r--r--spec/ruby/core/module/private_class_method_spec.rb34
-rw-r--r--spec/ruby/core/module/private_constant_spec.rb10
-rw-r--r--spec/ruby/core/module/private_instance_methods_spec.rb6
-rw-r--r--spec/ruby/core/module/private_method_defined_spec.rb64
-rw-r--r--spec/ruby/core/module/private_spec.rb20
-rw-r--r--spec/ruby/core/module/protected_instance_methods_spec.rb6
-rw-r--r--spec/ruby/core/module/protected_method_defined_spec.rb64
-rw-r--r--spec/ruby/core/module/protected_spec.rb19
-rw-r--r--spec/ruby/core/module/public_class_method_spec.rb32
-rw-r--r--spec/ruby/core/module/public_constant_spec.rb4
-rw-r--r--spec/ruby/core/module/public_instance_method_spec.rb14
-rw-r--r--spec/ruby/core/module/public_instance_methods_spec.rb6
-rw-r--r--spec/ruby/core/module/public_method_defined_spec.rb16
-rw-r--r--spec/ruby/core/module/public_spec.rb17
-rw-r--r--spec/ruby/core/module/refine_spec.rb471
-rw-r--r--spec/ruby/core/module/refinements_spec.rb43
-rw-r--r--spec/ruby/core/module/remove_class_variable_spec.rb12
-rw-r--r--spec/ruby/core/module/remove_const_spec.rb53
-rw-r--r--spec/ruby/core/module/remove_method_spec.rb63
-rw-r--r--spec/ruby/core/module/ruby2_keywords_spec.rb248
-rw-r--r--spec/ruby/core/module/set_temporary_name_spec.rb147
-rw-r--r--spec/ruby/core/module/shared/attr_added.rb34
-rw-r--r--spec/ruby/core/module/shared/class_eval.rb77
-rw-r--r--spec/ruby/core/module/shared/class_exec.rb4
-rw-r--r--spec/ruby/core/module/shared/set_visibility.rb49
-rw-r--r--spec/ruby/core/module/singleton_class_spec.rb12
-rw-r--r--spec/ruby/core/module/to_s_spec.rb60
-rw-r--r--spec/ruby/core/module/undef_method_spec.rb70
-rw-r--r--spec/ruby/core/module/undefined_instance_methods_spec.rb24
-rw-r--r--spec/ruby/core/module/used_refinements_spec.rb85
-rw-r--r--spec/ruby/core/module/using_spec.rb102
119 files changed, 4955 insertions, 1051 deletions
diff --git a/spec/ruby/core/module/alias_method_spec.rb b/spec/ruby/core/module/alias_method_spec.rb
index d3c0529418..c36dedd2d8 100644
--- a/spec/ruby/core/module/alias_method_spec.rb
+++ b/spec/ruby/core/module/alias_method_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#alias_method" do
before :each do
@@ -14,7 +14,7 @@ describe "Module#alias_method" do
@object.double(12).should == @object.public_two(12)
end
- it "creates methods that are == to eachother" do
+ it "creates methods that are == to each other" do
@class.make_alias :uno, :public_one
@object.method(:uno).should == @object.method(:public_one)
end
@@ -28,12 +28,12 @@ describe "Module#alias_method" do
it "retains method visibility" do
@class.make_alias :private_ichi, :private_one
- lambda { @object.private_one }.should raise_error(NameError)
- lambda { @object.private_ichi }.should raise_error(NameError)
+ -> { @object.private_one }.should raise_error(NameError)
+ -> { @object.private_ichi }.should raise_error(NameError)
@class.make_alias :public_ichi, :public_one
@object.public_ichi.should == @object.public_one
@class.make_alias :protected_ichi, :protected_one
- lambda { @object.protected_ichi }.should raise_error(NameError)
+ -> { @object.protected_ichi }.should raise_error(NameError)
end
it "handles aliasing a stub that changes visibility" do
@@ -42,16 +42,28 @@ describe "Module#alias_method" do
@object.was_private_one.should == 1
end
+ it "handles aliasing a method only present in a refinement" do
+ c = @class
+ Module.new do
+ refine c do
+ def uno_refined_method
+ end
+ alias_method :double_refined_method, :uno_refined_method
+ instance_method(:uno_refined_method).should == instance_method(:double_refined_method)
+ end
+ end
+ end
+
it "fails if origin method not found" do
- lambda { @class.make_alias :ni, :san }.should raise_error(NameError) { |e|
+ -> { @class.make_alias :ni, :san }.should raise_error(NameError) { |e|
# a NameError and not a NoMethodError
e.class.should == NameError
}
end
- it "raises RuntimeError if frozen" do
+ it "raises FrozenError if frozen" do
@class.freeze
- lambda { @class.make_alias :uno, :public_one }.should raise_error(RuntimeError)
+ -> { @class.make_alias :uno, :public_one }.should raise_error(FrozenError)
end
it "converts the names using #to_str" do
@@ -66,22 +78,24 @@ describe "Module#alias_method" do
end
it "raises a TypeError when the given name can't be converted using to_str" do
- lambda { @class.make_alias mock('x'), :public_one }.should raise_error(TypeError)
+ -> { @class.make_alias mock('x'), :public_one }.should raise_error(TypeError)
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- lambda { @class.alias_method :ichi, :public_one }.should raise_error(NoMethodError)
- 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_error(NoMethodError)
end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:alias_method, false)
- end
+
+ it "is a public method" do
+ Module.should have_public_instance_method(:alias_method, false)
end
- it "returns self" do
- @class.send(:alias_method, :checking_return_value, :public_one).should equal(@class)
+ describe "returned value" 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
end
it "works in module" do
@@ -90,7 +104,7 @@ describe "Module#alias_method" do
it "works on private module methods in a module that has been reopened" do
ModuleSpecs::ReopeningModule.foo.should == true
- lambda { ModuleSpecs::ReopeningModule.foo2 }.should_not raise_error(NoMethodError)
+ -> { ModuleSpecs::ReopeningModule.foo2 }.should_not raise_error(NoMethodError)
end
it "accesses a method defined on Object from Kernel" do
diff --git a/spec/ruby/core/module/allocate_spec.rb b/spec/ruby/core/module/allocate_spec.rb
deleted file mode 100644
index 306426881a..0000000000
--- a/spec/ruby/core/module/allocate_spec.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-
-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 1cd537f7a0..90c26941d1 100644
--- a/spec/ruby/core/module/ancestors_spec.rb
+++ b/spec/ruby/core/module/ancestors_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#ancestors" do
it "returns a list of modules included in self (including self)" do
@@ -7,10 +7,17 @@ describe "Module#ancestors" do
ModuleSpecs.ancestors.should == [ModuleSpecs]
ModuleSpecs::Basic.ancestors.should == [ModuleSpecs::Basic]
ModuleSpecs::Super.ancestors.should == [ModuleSpecs::Super, ModuleSpecs::Basic]
- ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should ==
- [ModuleSpecs::Parent, Object, Kernel, BasicObject]
- ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should ==
- [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, Kernel, BasicObject]
+ if defined?(Ruby::Box) && Ruby::Box.enabled?
+ ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should ==
+ [ModuleSpecs::Parent, Object, Ruby::Box::Loader, Kernel, BasicObject]
+ ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should ==
+ [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, Ruby::Box::Loader, Kernel, BasicObject]
+ else
+ ModuleSpecs.without_test_modules(ModuleSpecs::Parent.ancestors).should ==
+ [ModuleSpecs::Parent, Object, Kernel, BasicObject]
+ ModuleSpecs.without_test_modules(ModuleSpecs::Child.ancestors).should ==
+ [ModuleSpecs::Child, ModuleSpecs::Super, ModuleSpecs::Basic, ModuleSpecs::Parent, Object, Kernel, BasicObject]
+ end
end
it "returns only modules and classes" do
@@ -21,6 +28,17 @@ describe "Module#ancestors" do
ModuleSpecs::Parent.ancestors.should == ModuleSpecs::Parent.ancestors.uniq
end
+ it "returns a module that is included later into a nested module as well" do
+ m1 = Module.new
+ m2 = Module.new
+ m3 = Module.new do
+ include m2
+ end
+ m2.include m1 # should be after m3 includes m2
+
+ m3.ancestors.should == [m3, m2, m1]
+ end
+
describe "when called on a singleton class" do
it "includes the singleton classes of ancestors" do
parent = Class.new
diff --git a/spec/ruby/core/module/append_features_spec.rb b/spec/ruby/core/module/append_features_spec.rb
index ceb8c3f8eb..1724cde5d6 100644
--- a/spec/ruby/core/module/append_features_spec.rb
+++ b/spec/ruby/core/module/append_features_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#append_features" do
it "is a private method" do
@@ -12,7 +12,7 @@ describe "Module#append_features" do
end
it "raises a TypeError if calling after rebinded to Class" do
- lambda {
+ -> {
Module.instance_method(:append_features).bind(Class.new).call Module.new
}.should raise_error(TypeError)
end
@@ -37,36 +37,24 @@ describe "Module#append_features" do
end
it "raises an ArgumentError on a cyclic include" do
- lambda {
+ -> {
ModuleSpecs::CyclicAppendA.send(:append_features, ModuleSpecs::CyclicAppendA)
}.should raise_error(ArgumentError)
- lambda {
+ -> {
ModuleSpecs::CyclicAppendB.send(:append_features, ModuleSpecs::CyclicAppendA)
}.should raise_error(ArgumentError)
end
- 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
-
describe "when other is frozen" do
before :each do
@receiver = Module.new
@other = Module.new.freeze
end
- it "raises a RuntimeError before appending self" do
- lambda { @receiver.send(:append_features, @other) }.should raise_error(RuntimeError)
+ it "raises a FrozenError before appending self" do
+ -> { @receiver.send(:append_features, @other) }.should raise_error(FrozenError)
@other.ancestors.should_not include(@receiver)
end
end
diff --git a/spec/ruby/core/module/attr_accessor_spec.rb b/spec/ruby/core/module/attr_accessor_spec.rb
index c6f7aad6af..503dccc61e 100644
--- a/spec/ruby/core/module/attr_accessor_spec.rb
+++ b/spec/ruby/core/module/attr_accessor_spec.rb
@@ -1,5 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+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,10 +34,23 @@ describe "Module#attr_accessor" do
attr_accessor :spec_attr_accessor
end
- lambda { true.spec_attr_accessor = "a" }.should raise_error(RuntimeError)
+ -> { true.spec_attr_accessor = "a" }.should raise_error(FrozenError)
end
- it "converts non string/symbol/fixnum names to strings using to_str" do
+ 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_error(FrozenError)
+ obj.foo.should == 1
+ end
+
+ it "converts non string/symbol names to strings using to_str" do
(o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
c = Class.new do
attr_accessor o
@@ -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')
- lambda { Class.new { attr_accessor o } }.should raise_error(TypeError)
+ -> { Class.new { attr_accessor o } }.should raise_error(TypeError)
(o = mock('123')).should_receive(:to_str).and_return(123)
- lambda { Class.new { attr_accessor o } }.should raise_error(TypeError)
+ -> { Class.new { attr_accessor o } }.should raise_error(TypeError)
end
it "applies current visibility to methods created" do
@@ -59,34 +73,33 @@ describe "Module#attr_accessor" do
attr_accessor :foo
end
- lambda { c.new.foo }.should raise_error(NoMethodError)
- lambda { c.new.foo=1 }.should raise_error(NoMethodError)
+ -> { c.new.foo }.should raise_error(NoMethodError)
+ -> { c.new.foo=1 }.should raise_error(NoMethodError)
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- Module.should have_private_instance_method(:attr_accessor, false)
- end
+ it "is a public method" do
+ Module.should have_public_instance_method(:attr_accessor, false)
end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:attr_accessor, false)
+
+ 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
describe "on immediates" do
before :each do
- class Fixnum
+ class Integer
attr_accessor :foobar
end
end
after :each do
- if Fixnum.method_defined?(:foobar)
- Fixnum.send(:remove_method, :foobar)
+ if Integer.method_defined?(:foobar)
+ Integer.send(:remove_method, :foobar)
end
- if Fixnum.method_defined?(:foobar=)
- Fixnum.send(:remove_method, :foobar=)
+ if Integer.method_defined?(:foobar=)
+ Integer.send(:remove_method, :foobar=)
end
end
@@ -94,4 +107,6 @@ describe "Module#attr_accessor" do
1.foobar.should be_nil
end
end
+
+ it_behaves_like :module_attr_added, :attr_accessor
end
diff --git a/spec/ruby/core/module/attr_reader_spec.rb b/spec/ruby/core/module/attr_reader_spec.rb
index 65cafdba9f..37fd537ff5 100644
--- a/spec/ruby/core/module/attr_reader_spec.rb
+++ b/spec/ruby/core/module/attr_reader_spec.rb
@@ -1,5 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+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,10 +30,10 @@ describe "Module#attr_reader" do
attr_reader :spec_attr_reader
end
- lambda { true.instance_variable_set("@spec_attr_reader", "a") }.should raise_error(RuntimeError)
+ -> { true.instance_variable_set("@spec_attr_reader", "a") }.should raise_error(RuntimeError)
end
- it "converts non string/symbol/fixnum names to strings using to_str" do
+ it "converts non string/symbol names to strings using to_str" do
(o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
c = Class.new do
attr_reader o
@@ -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')
- lambda { Class.new { attr_reader o } }.should raise_error(TypeError)
+ -> { Class.new { attr_reader o } }.should raise_error(TypeError)
(o = mock('123')).should_receive(:to_str).and_return(123)
- lambda { Class.new { attr_reader o } }.should raise_error(TypeError)
+ -> { Class.new { attr_reader o } }.should raise_error(TypeError)
end
it "applies current visibility to methods created" do
@@ -55,17 +56,18 @@ describe "Module#attr_reader" do
attr_reader :foo
end
- lambda { c.new.foo }.should raise_error(NoMethodError)
+ -> { c.new.foo }.should raise_error(NoMethodError)
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- Module.should have_private_instance_method(:attr_reader, false)
- end
+ it "is a public method" do
+ Module.should have_public_instance_method(:attr_reader, false)
end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:attr_reader, false)
+
+ it "returns an array of defined method names as symbols" do
+ Class.new do
+ (attr_reader :foo, 'bar').should == [:foo, :bar]
end
end
+
+ it_behaves_like :module_attr_added, :attr_reader
end
diff --git a/spec/ruby/core/module/attr_spec.rb b/spec/ruby/core/module/attr_spec.rb
index 7128c610fe..2f9f4e26dc 100644
--- a/spec/ruby/core/module/attr_spec.rb
+++ b/spec/ruby/core/module/attr_spec.rb
@@ -1,5 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/attr_added'
describe "Module#attr" do
before :each do
@@ -54,7 +55,7 @@ describe "Module#attr" do
o.attr3 = "test3 updated"
end
- it "creates a getter and setter for the given attribute name if called with and without writeable is true" do
+ it "creates a getter and setter for the given attribute name if called with and without writable is true" do
c = Class.new do
attr :attr, true
attr :attr
@@ -89,8 +90,8 @@ describe "Module#attr" do
attr :foo, true
end
- lambda { c.new.foo }.should raise_error(NoMethodError)
- lambda { c.new.foo=1 }.should raise_error(NoMethodError)
+ -> { c.new.foo }.should raise_error(NoMethodError)
+ -> { c.new.foo=1 }.should raise_error(NoMethodError)
end
it "creates a getter but no setter for all given attribute names" do
@@ -120,37 +121,39 @@ describe "Module#attr" do
attr :foo, :bar
end
- lambda { c.new.foo }.should raise_error(NoMethodError)
- lambda { c.new.bar }.should raise_error(NoMethodError)
+ -> { c.new.foo }.should raise_error(NoMethodError)
+ -> { c.new.bar }.should raise_error(NoMethodError)
end
- it "converts non string/symbol/fixnum names to strings using to_str" do
+ it "converts non string/symbol names to strings using to_str" do
(o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
Class.new { attr o }.new.respond_to?("test").should == true
end
it "raises a TypeError when the given names can't be converted to strings using to_str" do
o = mock('o')
- lambda { Class.new { attr o } }.should raise_error(TypeError)
+ -> { Class.new { attr o } }.should raise_error(TypeError)
(o = mock('123')).should_receive(:to_str).and_return(123)
- lambda { Class.new { attr o } }.should raise_error(TypeError)
+ -> { Class.new { attr o } }.should raise_error(TypeError)
end
it "with a boolean argument emits a warning when $VERBOSE is true" do
- lambda {
- $VERBOSE = true
+ -> {
Class.new { attr :foo, true }
- }.should complain(/boolean argument is obsoleted/)
+ }.should complain(/boolean argument is obsoleted/, verbose: true)
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- Module.should have_private_instance_method(:attr, false)
- end
+ it "is a public method" do
+ Module.should have_public_instance_method(:attr, false)
end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:attr, false)
+
+ 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
+
+ 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 5979e3891b..5b863ef88c 100644
--- a/spec/ruby/core/module/attr_writer_spec.rb
+++ b/spec/ruby/core/module/attr_writer_spec.rb
@@ -1,5 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+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,10 +30,20 @@ describe "Module#attr_writer" do
attr_writer :spec_attr_writer
end
- lambda { true.spec_attr_writer = "a" }.should raise_error(RuntimeError)
+ -> { true.spec_attr_writer = "a" }.should raise_error(FrozenError)
end
- it "converts non string/symbol/fixnum names to strings using to_str" do
+ 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_error(FrozenError)
+ end
+
+ it "converts non string/symbol names to strings using to_str" do
(o = mock('test')).should_receive(:to_str).any_number_of_times.and_return("test")
c = Class.new do
attr_writer o
@@ -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')
- lambda { Class.new { attr_writer o } }.should raise_error(TypeError)
+ -> { Class.new { attr_writer o } }.should raise_error(TypeError)
(o = mock('123')).should_receive(:to_str).and_return(123)
- lambda { Class.new { attr_writer o } }.should raise_error(TypeError)
+ -> { Class.new { attr_writer o } }.should raise_error(TypeError)
end
it "applies current visibility to methods created" do
@@ -55,17 +66,18 @@ describe "Module#attr_writer" do
attr_writer :foo
end
- lambda { c.new.foo=1 }.should raise_error(NoMethodError)
+ -> { c.new.foo=1 }.should raise_error(NoMethodError)
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- Module.should have_private_instance_method(:attr_writer, false)
- end
+ it "is a public method" do
+ Module.should have_public_instance_method(:attr_writer, false)
end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:attr_writer, false)
+
+ it "returns an array of defined method names as symbols" do
+ Class.new do
+ (attr_writer :foo, 'bar').should == [:foo=, :bar=]
end
end
+
+ it_behaves_like :module_attr_added, :attr_writer
end
diff --git a/spec/ruby/core/module/autoload_spec.rb b/spec/ruby/core/module/autoload_spec.rb
index a72ae7735b..625d945686 100644
--- a/spec/ruby/core/module/autoload_spec.rb
+++ b/spec/ruby/core/module/autoload_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require 'thread'
+require_relative '../../spec_helper'
+require_relative '../../fixtures/code_loading'
+require_relative 'fixtures/classes'
describe "Module#autoload?" do
it "returns the name of the file that will be autoloaded" do
@@ -11,22 +11,41 @@ describe "Module#autoload?" do
it "returns nil if no file has been registered for a constant" do
ModuleSpecs::Autoload.autoload?(:Manualload).should be_nil
end
+
+ it "returns the name of the file that will be autoloaded if an ancestor defined that autoload" do
+ ModuleSpecs::Autoload::Parent.autoload :AnotherAutoload, "another_autoload.rb"
+ ModuleSpecs::Autoload::Child.autoload?(:AnotherAutoload).should == "another_autoload.rb"
+ end
+
+ it "returns nil if an ancestor defined that autoload but recursion is disabled" do
+ ModuleSpecs::Autoload::Parent.autoload :InheritedAutoload, "inherited_autoload.rb"
+ ModuleSpecs::Autoload::Child.autoload?(:InheritedAutoload, false).should be_nil
+ end
+
+ it "returns the name of the file that will be loaded if recursion is disabled but the autoload is defined on the class itself" do
+ ModuleSpecs::Autoload::Child.autoload :ChildAutoload, "child_autoload.rb"
+ ModuleSpecs::Autoload::Child.autoload?(:ChildAutoload, false).should == "child_autoload.rb"
+ end
end
describe "Module#autoload" do
before :all do
@non_existent = fixture __FILE__, "no_autoload.rb"
+ CodeLoadingSpecs.preload_rubygems
end
before :each do
@loaded_features = $".dup
- @frozen_module = Module.new.freeze
ScratchPad.clear
+ @remove = []
end
after :each do
$".replace @loaded_features
+ @remove.each { |const|
+ ModuleSpecs::Autoload.send :remove_const, const
+ }
end
it "registers a file to load the first time the named constant is accessed" do
@@ -39,16 +58,29 @@ describe "Module#autoload" do
ModuleSpecs::Autoload.should have_constant(:B)
end
+ it "can be overridden with a second autoload on the same constant" do
+ ModuleSpecs::Autoload.autoload :Overridden, @non_existent
+ @remove << :Overridden
+ ModuleSpecs::Autoload.autoload?(:Overridden).should == @non_existent
+
+ path = fixture(__FILE__, "autoload_overridden.rb")
+ ModuleSpecs::Autoload.autoload :Overridden, path
+ ModuleSpecs::Autoload.autoload?(:Overridden).should == path
+
+ ModuleSpecs::Autoload::Overridden.should == :overridden
+ end
+
it "loads the registered constant when it is accessed" do
ModuleSpecs::Autoload.should_not have_constant(:X)
ModuleSpecs::Autoload.autoload :X, fixture(__FILE__, "autoload_x.rb")
+ @remove << :X
ModuleSpecs::Autoload::X.should == :x
- ModuleSpecs::Autoload.send(:remove_const, :X)
end
it "loads the registered constant into a dynamically created class" do
cls = Class.new { autoload :C, fixture(__FILE__, "autoload_c.rb") }
ModuleSpecs::Autoload::DynClass = cls
+ @remove << :DynClass
ScratchPad.recorded.should be_nil
ModuleSpecs::Autoload::DynClass::C.new.loaded.should == :dynclass_c
@@ -58,6 +90,7 @@ describe "Module#autoload" do
it "loads the registered constant into a dynamically created module" do
mod = Module.new { autoload :D, fixture(__FILE__, "autoload_d.rb") }
ModuleSpecs::Autoload::DynModule = mod
+ @remove << :DynModule
ScratchPad.recorded.should be_nil
ModuleSpecs::Autoload::DynModule::D.new.loaded.should == :dynmodule_d
@@ -95,6 +128,7 @@ describe "Module#autoload" do
it "does not load the file when the constant is already set" do
ModuleSpecs::Autoload.autoload :I, fixture(__FILE__, "autoload_i.rb")
+ @remove << :I
ModuleSpecs::Autoload.const_set :I, 3
ModuleSpecs::Autoload::I.should == 3
ScratchPad.recorded.should be_nil
@@ -105,9 +139,18 @@ describe "Module#autoload" do
ModuleSpecs::Autoload::J.should == :autoload_j
end
+ it "calls main.require(path) to load the file" do
+ ModuleSpecs::Autoload.autoload :ModuleAutoloadCallsRequire, "module_autoload_not_exist.rb"
+ 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)
+ end
+
it "does not load the file if the file is manually required" do
filename = fixture(__FILE__, "autoload_k.rb")
ModuleSpecs::Autoload.autoload :KHash, filename
+ @remove << :KHash
require filename
ScratchPad.recorded.should == :loaded
@@ -127,8 +170,8 @@ describe "Module#autoload" do
ScratchPad.clear
ModuleSpecs::Autoload.autoload :S, filename
+ @remove << :S
ModuleSpecs::Autoload.autoload?(:S).should be_nil
- ModuleSpecs::Autoload.send(:remove_const, :S)
end
it "retains the autoload even if the request to require fails" do
@@ -137,7 +180,7 @@ describe "Module#autoload" do
ModuleSpecs::Autoload.autoload :NotThere, filename
ModuleSpecs::Autoload.autoload?(:NotThere).should == filename
- lambda {
+ -> {
require filename
}.should raise_error(LoadError)
@@ -158,28 +201,322 @@ describe "Module#autoload" do
ModuleSpecs::Autoload.use_ex1.should == :good
end
- it "does not load the file when referring to the constant in defined?" do
- module ModuleSpecs::Autoload::Q
- autoload :R, fixture(__FILE__, "autoload.rb")
- defined?(R).should == "constant"
+ it "considers an autoload constant as loaded when autoload is called for/from the current file" do
+ filename = fixture(__FILE__, "autoload_during_require_current_file.rb")
+ require filename
+
+ ScratchPad.recorded.should be_nil
+ end
+
+ describe "interacting with defined?" do
+ it "does not load the file when referring to the constant in defined?" do
+ module ModuleSpecs::Autoload::Dog
+ autoload :R, fixture(__FILE__, "autoload_exception.rb")
+ end
+
+ defined?(ModuleSpecs::Autoload::Dog::R).should == "constant"
+ ScratchPad.recorded.should be_nil
+
+ ModuleSpecs::Autoload::Dog.should have_constant(:R)
+ end
+
+ it "loads an autoloaded parent when referencing a nested constant" do
+ module ModuleSpecs::Autoload
+ autoload :GoodParent, fixture(__FILE__, "autoload_nested.rb")
+ end
+ @remove << :GoodParent
+
+ defined?(ModuleSpecs::Autoload::GoodParent::Nested).should == 'constant'
+ ScratchPad.recorded.should == :loaded
+ end
+
+ it "returns nil when it fails to load an autoloaded parent when referencing a nested constant" do
+ module ModuleSpecs::Autoload
+ autoload :BadParent, fixture(__FILE__, "autoload_exception.rb")
+ end
+
+ defined?(ModuleSpecs::Autoload::BadParent::Nested).should be_nil
+ ScratchPad.recorded.should == :exception
+ end
+ end
+
+ describe "the autoload is triggered when the same file is required directly" do
+ before :each do
+ module ModuleSpecs::Autoload
+ autoload :RequiredDirectly, fixture(__FILE__, "autoload_required_directly.rb")
+ end
+ @remove << :RequiredDirectly
+ @path = fixture(__FILE__, "autoload_required_directly.rb")
+ @check = -> {
+ [
+ defined?(ModuleSpecs::Autoload::RequiredDirectly),
+ ModuleSpecs::Autoload.autoload?(:RequiredDirectly)
+ ]
+ }
+ ScratchPad.record @check
+ end
+
+ it "with a full path" do
+ @check.call.should == ["constant", @path]
+ require @path
+ ScratchPad.recorded.should == [nil, nil]
+ @check.call.should == ["constant", nil]
+ end
+
+ it "with a relative path" do
+ @check.call.should == ["constant", @path]
+ $:.push File.dirname(@path)
+ begin
+ require "autoload_required_directly.rb"
+ ensure
+ $:.pop
+ end
+ ScratchPad.recorded.should == [nil, nil]
+ @check.call.should == ["constant", nil]
+ end
+
+ it "in a nested require" do
+ nested = fixture(__FILE__, "autoload_required_directly_nested.rb")
+ nested_require = -> {
+ result = nil
+ ScratchPad.record -> {
+ result = @check.call
+ }
+ require nested
+ result
+ }
+ ScratchPad.record nested_require
+
+ @check.call.should == ["constant", @path]
+ require @path
+ ScratchPad.recorded.should == [nil, nil]
+ @check.call.should == ["constant", nil]
+ end
+
+ it "does not raise an error if the autoload constant was not defined" do
+ module ModuleSpecs::Autoload
+ autoload :RequiredDirectlyNoConstant, fixture(__FILE__, "autoload_required_directly_no_constant.rb")
+ end
+ @path = fixture(__FILE__, "autoload_required_directly_no_constant.rb")
+ @remove << :RequiredDirectlyNoConstant
+ @check = -> {
+ [
+ defined?(ModuleSpecs::Autoload::RequiredDirectlyNoConstant),
+ ModuleSpecs::Autoload.constants(false).include?(:RequiredDirectlyNoConstant),
+ ModuleSpecs::Autoload.const_defined?(:RequiredDirectlyNoConstant),
+ ModuleSpecs::Autoload.autoload?(:RequiredDirectlyNoConstant)
+ ]
+ }
+ ScratchPad.record @check
+ @check.call.should == ["constant", true, true, @path]
+ $:.push File.dirname(@path)
+ begin
+ require "autoload_required_directly_no_constant.rb"
+ ensure
+ $:.pop
+ end
+ ScratchPad.recorded.should == [nil, true, false, nil]
+ @check.call.should == [nil, true, false, nil]
+ end
+ end
+
+ describe "after the autoload is triggered by require" do
+ before :each do
+ @path = tmp("autoload.rb")
+ end
+
+ after :each do
+ rm_r @path
+ end
+
+ it "the mapping feature to autoload is removed, and a new autoload with the same path is considered" do
+ ModuleSpecs::Autoload.autoload :RequireMapping1, @path
+ touch(@path) { |f| f.puts "ModuleSpecs::Autoload::RequireMapping1 = 1" }
+ ModuleSpecs::Autoload::RequireMapping1.should == 1
+
+ $LOADED_FEATURES.delete(@path)
+ ModuleSpecs::Autoload.autoload :RequireMapping2, @path[0...-3]
+ @remove << :RequireMapping2
+ touch(@path) { |f| f.puts "ModuleSpecs::Autoload::RequireMapping2 = 2" }
+ ModuleSpecs::Autoload::RequireMapping2.should == 2
+ end
+ end
+
+ def check_before_during_thread_after(const, &check)
+ before = check.call
+ to_autoload_thread, from_autoload_thread = Queue.new, Queue.new
+ ScratchPad.record -> {
+ from_autoload_thread.push check.call
+ to_autoload_thread.pop
+ }
+ t = Thread.new {
+ in_loading_thread = from_autoload_thread.pop
+ in_other_thread = check.call
+ to_autoload_thread.push :done
+ [in_loading_thread, in_other_thread]
+ }
+ in_loading_thread, in_other_thread = nil
+ begin
+ ModuleSpecs::Autoload.const_get(const)
+ ensure
+ in_loading_thread, in_other_thread = t.value
+ end
+ after = check.call
+ [before, in_loading_thread, in_other_thread, after]
+ end
+
+ describe "during the autoload before the constant is assigned" do
+ before :each do
+ @path = fixture(__FILE__, "autoload_during_autoload.rb")
+ ModuleSpecs::Autoload.autoload :DuringAutoload, @path
+ @remove << :DuringAutoload
+ raise unless ModuleSpecs::Autoload.autoload?(:DuringAutoload) == @path
+ end
+
+ it "returns nil in autoload thread and 'constant' otherwise for defined?" do
+ results = check_before_during_thread_after(: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(: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(: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(:DuringAutoload) {
+ ModuleSpecs::Autoload.autoload?(:DuringAutoload)
+ }
+ results.should == [@path, nil, @path, nil]
end
- ModuleSpecs::Autoload::Q.should have_constant(:R)
end
- it "does not remove the constant from the constant table if load fails" do
+ 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.autoload?(:Fail).should == @non_existent
+
+ -> { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
- lambda { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
ModuleSpecs::Autoload.should have_constant(:Fail)
+ ModuleSpecs::Autoload.const_defined?(:Fail).should == true
+ ModuleSpecs::Autoload.autoload?(:Fail).should == @non_existent
+
+ -> { ModuleSpecs::Autoload::Fail }.should raise_error(LoadError)
end
- it "does not remove the constant from the constant table if the loaded files does not define it" do
- ModuleSpecs::Autoload.autoload :O, fixture(__FILE__, "autoload_o.rb")
- ModuleSpecs::Autoload.should have_constant(:O)
+ it "does not remove the constant from Module#constants if load raises a RuntimeError and keeps it as an autoload" do
+ path = fixture(__FILE__, "autoload_raise.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :Raise, path
+
+ ModuleSpecs::Autoload.const_defined?(:Raise).should == true
+ ModuleSpecs::Autoload.should have_constant(:Raise)
+ ModuleSpecs::Autoload.autoload?(:Raise).should == path
- lambda { ModuleSpecs::Autoload::O }.should raise_error(NameError)
+ -> { ModuleSpecs::Autoload::Raise }.should raise_error(RuntimeError)
+ ScratchPad.recorded.should == [:raise]
+
+ ModuleSpecs::Autoload.should have_constant(:Raise)
+ ModuleSpecs::Autoload.const_defined?(:Raise).should == true
+ ModuleSpecs::Autoload.autoload?(:Raise).should == path
+
+ -> { ModuleSpecs::Autoload::Raise }.should raise_error(RuntimeError)
+ ScratchPad.recorded.should == [:raise, :raise]
+ end
+
+ it "removes the constant from Module#constants if the loaded file does not define it" do
+ path = fixture(__FILE__, "autoload_o.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :O, path
+
+ ModuleSpecs::Autoload.const_defined?(:O).should == true
ModuleSpecs::Autoload.should have_constant(:O)
+ ModuleSpecs::Autoload.autoload?(:O).should == path
+
+ -> { ModuleSpecs::Autoload::O }.should raise_error(NameError)
+
+ ModuleSpecs::Autoload.const_defined?(:O).should == false
+ ModuleSpecs::Autoload.should_not have_constant(:O)
+ ModuleSpecs::Autoload.autoload?(:O).should == nil
+ -> { ModuleSpecs::Autoload.const_get(:O) }.should raise_error(NameError)
+ end
+
+ it "does not try to load the file again if the loaded file did not define the constant" do
+ path = fixture(__FILE__, "autoload_o.rb")
+ ScratchPad.record []
+ ModuleSpecs::Autoload.autoload :NotDefinedByFile, path
+
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ ScratchPad.recorded.should == [:loaded]
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ ScratchPad.recorded.should == [:loaded]
+
+ Thread.new {
+ -> { ModuleSpecs::Autoload::NotDefinedByFile }.should raise_error(NameError)
+ }.join
+ ScratchPad.recorded.should == [:loaded]
end
it "returns 'constant' on referring the constant with defined?()" do
@@ -201,6 +538,7 @@ describe "Module#autoload" do
it "does not load the file when accessing the constants table of the module" do
ModuleSpecs::Autoload.autoload :P, @non_existent
ModuleSpecs::Autoload.const_defined?(:P).should be_true
+ ModuleSpecs::Autoload.const_defined?("P").should be_true
end
it "loads the file when opening a module that is the autoloaded constant" do
@@ -211,43 +549,188 @@ describe "Module#autoload" do
X = get_value
end
end
+ @remove << :U
ModuleSpecs::Autoload::U::V::X.should == :autoload_uvx
end
- it "loads the file that defines subclass XX::YY < YY and YY is a top level constant" do
-
+ it "loads the file that defines subclass XX::CS_CONST_AUTOLOAD < CS_CONST_AUTOLOAD and CS_CONST_AUTOLOAD is a top level constant" do
module ModuleSpecs::Autoload::XX
- autoload :YY, fixture(__FILE__, "autoload_subclass.rb")
+ autoload :CS_CONST_AUTOLOAD, fixture(__FILE__, "autoload_subclass.rb")
end
- ModuleSpecs::Autoload::XX::YY.superclass.should == YY
+ ModuleSpecs::Autoload::XX::CS_CONST_AUTOLOAD.superclass.should == CS_CONST_AUTOLOAD
end
+ describe "after autoloading searches for the constant like the original lookup" do
+ it "in lexical scopes if both declared and defined in parent" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ DeclaredAndDefinedInParent = :declared_and_defined_in_parent
+ }
+ autoload :DeclaredAndDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
+ class LexicalScope
+ 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)
+ end
+ DeclaredAndDefinedInParent.should == :declared_and_defined_in_parent
+ end
+ end
- it "looks up the constant in the scope where it is referred" do
- module ModuleSpecs
- module Autoload
- autoload :QQ, fixture(__FILE__, "autoload_scope.rb")
- class PP
- QQ.new.should be_kind_of(ModuleSpecs::Autoload::PP::QQ)
+ it "in lexical scopes if declared in parent and defined in current" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ class LexicalScope
+ DeclaredInParentDefinedInCurrent = :declared_in_parent_defined_in_current
+ end
+ }
+ autoload :DeclaredInParentDefinedInCurrent, fixture(__FILE__, "autoload_callback.rb")
+
+ class LexicalScope
+ DeclaredInParentDefinedInCurrent.should == :declared_in_parent_defined_in_current
+ LexicalScope::DeclaredInParentDefinedInCurrent.should == :declared_in_parent_defined_in_current
+ end
+
+ # Basically, the parent autoload constant remains in a "undefined" state
+ self.autoload?(:DeclaredInParentDefinedInCurrent).should == nil
+ const_defined?(:DeclaredInParentDefinedInCurrent).should == false
+ -> { DeclaredInParentDefinedInCurrent }.should raise_error(NameError)
+
+ ModuleSpecs::Autoload::LexicalScope.send(:remove_const, :DeclaredInParentDefinedInCurrent)
+ end
+ end
+
+ 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
- end
- it "looks up the constant when in a meta class scope" do
- module ModuleSpecs
- module Autoload
- autoload :R, fixture(__FILE__, "autoload_r.rb")
+ it "looks up in parent scope after failed autoload" do
+ @remove << :DeclaredInCurrentDefinedInParent
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ DeclaredInCurrentDefinedInParent = :declared_in_current_defined_in_parent
+ }
+
+ class LexicalScope
+ autoload :DeclaredInCurrentDefinedInParent, fixture(__FILE__, "autoload_callback.rb")
+ -> { DeclaredInCurrentDefinedInParent }.should_not raise_error(NameError)
+ # Basically, the autoload constant remains in a "undefined" state
+ self.autoload?(:DeclaredInCurrentDefinedInParent).should == nil
+ const_defined?(:DeclaredInCurrentDefinedInParent).should == false
+ -> { const_get(:DeclaredInCurrentDefinedInParent) }.should raise_error(NameError)
+ end
+
+ DeclaredInCurrentDefinedInParent.should == :declared_in_current_defined_in_parent
+ end
+ end
+
+ it "in the included modules" do
+ @remove << :DefinedInIncludedModule
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ module DefinedInIncludedModule
+ Incl = :defined_in_included_module
+ end
+ include DefinedInIncludedModule
+ }
+ autoload :Incl, fixture(__FILE__, "autoload_callback.rb")
+ Incl.should == :defined_in_included_module
+ end
+ end
+
+ it "in the included modules of the superclass" do
+ @remove << :DefinedInSuperclassIncludedModule
+ module ModuleSpecs::Autoload
+ class LookupAfterAutoloadSuper
+ end
+ class LookupAfterAutoloadChild < LookupAfterAutoloadSuper
+ end
+
+ ScratchPad.record -> {
+ module DefinedInSuperclassIncludedModule
+ InclS = :defined_in_superclass_included_module
+ end
+ LookupAfterAutoloadSuper.include DefinedInSuperclassIncludedModule
+ }
+
+ class LookupAfterAutoloadChild
+ autoload :InclS, fixture(__FILE__, "autoload_callback.rb")
+ InclS.should == :defined_in_superclass_included_module
+ end
+ end
+ end
+
+ it "in the prepended modules" do
+ @remove << :DefinedInPrependedModule
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ module DefinedInPrependedModule
+ Prep = :defined_in_prepended_module
+ end
+ include DefinedInPrependedModule
+ }
+ autoload :Prep, fixture(__FILE__, "autoload_callback.rb")
+ Prep.should == :defined_in_prepended_module
+ end
+ end
+
+ it "in a meta class scope" do
+ module ModuleSpecs::Autoload
+ ScratchPad.record -> {
+ class MetaScope
+ end
+ }
+ autoload :MetaScope, fixture(__FILE__, "autoload_callback.rb")
class << self
def r
- R.new
+ MetaScope.new
end
end
end
+ ModuleSpecs::Autoload.r.should be_kind_of(ModuleSpecs::Autoload::MetaScope)
end
- ModuleSpecs::Autoload.r.should be_kind_of(ModuleSpecs::Autoload::R)
+ end
+
+ it "should trigger the autoload when using `private_constant`" do
+ @remove << :DynClass
+ module ModuleSpecs::Autoload
+ autoload :DynClass, fixture(__FILE__, "autoload_c.rb")
+ private_constant :DynClass
+
+ ScratchPad.recorded.should be_nil
+
+ DynClass::C.new.loaded.should == :dynclass_c
+ ScratchPad.recorded.should == :loaded
+ end
+
+ -> { ModuleSpecs::Autoload::DynClass }.should raise_error(NameError, /private constant/)
end
# [ruby-core:19127] [ruby-core:29941]
@@ -260,10 +743,45 @@ describe "Module#autoload" do
end
end
end
+ @remove << :W
ModuleSpecs::Autoload::W::Y.should be_kind_of(Class)
ScratchPad.recorded.should == :loaded
- ModuleSpecs::Autoload::W.send(:remove_const, :Y)
+ end
+
+ it "does not call #require a second time and does not warn if already loading the same feature with #require" do
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_not_receive(:require)
+
+ module ModuleSpecs::Autoload
+ autoload :AutoloadDuringRequire, fixture(__FILE__, "autoload_during_require.rb")
+ end
+
+ -> {
+ Kernel.require fixture(__FILE__, "autoload_during_require.rb")
+ }.should_not complain(verbose: true)
+ ModuleSpecs::Autoload::AutoloadDuringRequire.should be_kind_of(Class)
+ end
+
+ it "does not call #require a second time and does not warn if feature sets and trigger autoload on itself" do
+ main = TOPLEVEL_BINDING.eval("self")
+ main.should_not_receive(:require)
+
+ -> {
+ Kernel.require fixture(__FILE__, "autoload_self_during_require.rb")
+ }.should_not complain(verbose: true)
+ ModuleSpecs::Autoload::AutoloadSelfDuringRequire.should be_kind_of(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)
+ ensure
+ $LOAD_PATH.shift
+ end
end
it "calls #to_path on non-string filenames" do
@@ -273,26 +791,27 @@ describe "Module#autoload" do
end
it "raises an ArgumentError when an empty filename is given" do
- lambda { ModuleSpecs.autoload :A, "" }.should raise_error(ArgumentError)
+ -> { ModuleSpecs.autoload :A, "" }.should raise_error(ArgumentError)
end
it "raises a NameError when the constant name starts with a lower case letter" do
- lambda { ModuleSpecs.autoload "a", @non_existent }.should raise_error(NameError)
+ -> { ModuleSpecs.autoload "a", @non_existent }.should raise_error(NameError)
end
it "raises a NameError when the constant name starts with a number" do
- lambda { ModuleSpecs.autoload "1two", @non_existent }.should raise_error(NameError)
+ -> { ModuleSpecs.autoload "1two", @non_existent }.should raise_error(NameError)
end
it "raises a NameError when the constant name has a space in it" do
- lambda { ModuleSpecs.autoload "a name", @non_existent }.should raise_error(NameError)
+ -> { ModuleSpecs.autoload "a name", @non_existent }.should raise_error(NameError)
end
it "shares the autoload request across dup'ed copies of modules" do
require fixture(__FILE__, "autoload_s.rb")
+ @remove << :S
filename = fixture(__FILE__, "autoload_t.rb")
mod1 = Module.new { autoload :T, filename }
- lambda {
+ -> {
ModuleSpecs::Autoload::S = mod1
}.should complain(/already initialized constant/)
mod2 = mod1.dup
@@ -301,7 +820,7 @@ describe "Module#autoload" do
mod2.autoload?(:T).should == filename
mod1::T.should == :autoload_t
- lambda { mod2::T }.should raise_error(NameError)
+ -> { mod2::T }.should raise_error(NameError)
end
it "raises a TypeError if opening a class with a different superclass than the class defined in the autoload file" do
@@ -309,29 +828,30 @@ describe "Module#autoload" do
class ModuleSpecs::Autoload::ZZ
end
- lambda do
+ -> do
class ModuleSpecs::Autoload::Z < ModuleSpecs::Autoload::ZZ
end
end.should raise_error(TypeError)
end
- it "raises a TypeError if not passed a String or object respodning to #to_path for the filename" do
+ it "raises a TypeError if not passed a String or object responding to #to_path for the filename" do
name = mock("autoload_name.rb")
- lambda { ModuleSpecs::Autoload.autoload :Str, name }.should raise_error(TypeError)
+ -> { ModuleSpecs::Autoload.autoload :Str, name }.should raise_error(TypeError)
end
it "calls #to_path on non-String filename arguments" do
name = mock("autoload_name.rb")
name.should_receive(:to_path).and_return("autoload_name.rb")
- lambda { ModuleSpecs::Autoload.autoload :Str, name }.should_not raise_error
+ -> { ModuleSpecs::Autoload.autoload :Str, name }.should_not raise_error
end
describe "on a frozen module" do
- it "raises a RuntimeError before setting the name" do
- lambda { @frozen_module.autoload :Foo, @non_existent }.should raise_error(RuntimeError)
- @frozen_module.should_not have_constant(:Foo)
+ it "raises a FrozenError before setting the name" do
+ frozen_module = Module.new.freeze
+ -> { frozen_module.autoload :Foo, @non_existent }.should raise_error(FrozenError)
+ frozen_module.should_not have_constant(:Foo)
end
end
@@ -354,6 +874,7 @@ describe "Module#autoload" do
describe "(concurrently)" do
it "blocks a second thread while a first is doing the autoload" do
ModuleSpecs::Autoload.autoload :Concur, fixture(__FILE__, "autoload_concur.rb")
+ @remove << :Concur
start = false
@@ -396,56 +917,53 @@ describe "Module#autoload" do
t2_val.should == t1_val
t2_exc.should be_nil
-
- ModuleSpecs::Autoload.send(:remove_const, :Concur)
end
- ruby_bug "#10892", ""..."2.3" do
- it "blocks others threads while doing an autoload" do
- file_path = fixture(__FILE__, "repeated_concurrent_autoload.rb")
- autoload_path = file_path.sub(/\.rb\Z/, '')
- mod_count = 30
- thread_count = 16
+ # https://bugs.ruby-lang.org/issues/10892
+ it "blocks others threads while doing an autoload" do
+ file_path = fixture(__FILE__, "repeated_concurrent_autoload.rb")
+ autoload_path = file_path.sub(/\.rb\Z/, '')
+ mod_count = 30
+ thread_count = 16
+
+ mod_names = []
+ mod_count.times do |i|
+ mod_name = :"Mod#{i}"
+ Object.autoload mod_name, autoload_path
+ mod_names << mod_name
+ end
- mod_names = []
- mod_count.times do |i|
- mod_name = :"Mod#{i}"
- Object.autoload mod_name, autoload_path
- mod_names << mod_name
- end
+ barrier = ModuleSpecs::CyclicBarrier.new thread_count
+ ScratchPad.record ModuleSpecs::ThreadSafeCounter.new
- barrier = ModuleSpecs::CyclicBarrier.new thread_count
- ScratchPad.record ModuleSpecs::ThreadSafeCounter.new
-
- threads = (1..thread_count).map do
- Thread.new do
- mod_names.each do |mod_name|
- break false unless barrier.enabled?
-
- was_last_one_in = barrier.await # wait for all threads to finish the iteration
- # clean up so we can autoload the same file again
- $LOADED_FEATURES.delete(file_path) if was_last_one_in && $LOADED_FEATURES.include?(file_path)
- barrier.await # get ready for race
-
- begin
- Object.const_get(mod_name).foo
- rescue NoMethodError
- barrier.disable!
- break false
- end
+ threads = (1..thread_count).map do
+ Thread.new do
+ mod_names.each do |mod_name|
+ break false unless barrier.enabled?
+
+ was_last_one_in = barrier.await # wait for all threads to finish the iteration
+ # clean up so we can autoload the same file again
+ $LOADED_FEATURES.delete(file_path) if was_last_one_in && $LOADED_FEATURES.include?(file_path)
+ barrier.await # get ready for race
+
+ begin
+ Object.const_get(mod_name).foo
+ rescue NoMethodError
+ barrier.disable!
+ break false
end
end
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 NoMethodError because of partially loaded module
+ threads.all? {|t| t.value}.should be_true
- # check that the autoloaded file was evaled exactly once
- ScratchPad.recorded.get.should == mod_count
+ # check that the autoloaded file was evaled exactly once
+ ScratchPad.recorded.get.should == mod_count
- mod_names.each do |mod_name|
- Object.send(:remove_const, mod_name)
- end
+ mod_names.each do |mod_name|
+ Object.send(:remove_const, mod_name)
end
end
diff --git a/spec/ruby/core/module/case_compare_spec.rb b/spec/ruby/core/module/case_compare_spec.rb
index 92f2c2065b..49ac359f6f 100644
--- a/spec/ruby/core/module/case_compare_spec.rb
+++ b/spec/ruby/core/module/case_compare_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#===" do
it "returns true when the given Object is an instance of self or of self's descendants" do
diff --git a/spec/ruby/core/module/class_eval_spec.rb b/spec/ruby/core/module/class_eval_spec.rb
index 90deef9c2f..c6665d5aff 100644
--- a/spec/ruby/core/module/class_eval_spec.rb
+++ b/spec/ruby/core/module/class_eval_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/class_eval', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/class_eval'
describe "Module#class_eval" do
it_behaves_like :module_class_eval, :class_eval
diff --git a/spec/ruby/core/module/class_exec_spec.rb b/spec/ruby/core/module/class_exec_spec.rb
index f9c12cfa48..4acd0169ad 100644
--- a/spec/ruby/core/module/class_exec_spec.rb
+++ b/spec/ruby/core/module/class_exec_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/class_exec', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/class_exec'
describe "Module#class_exec" do
it_behaves_like :module_class_exec, :class_exec
diff --git a/spec/ruby/core/module/class_variable_defined_spec.rb b/spec/ruby/core/module/class_variable_defined_spec.rb
index d47329455f..c0f2072a37 100644
--- a/spec/ruby/core/module/class_variable_defined_spec.rb
+++ b/spec/ruby/core/module/class_variable_defined_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#class_variable_defined?" do
it "returns true if a class variable with the given name is defined in self" do
@@ -42,16 +42,16 @@ describe "Module#class_variable_defined?" do
it "raises a NameError when the given name is not allowed" do
c = Class.new
- lambda {
+ -> {
c.class_variable_defined?(:invalid_name)
}.should raise_error(NameError)
- lambda {
+ -> {
c.class_variable_defined?("@invalid_name")
}.should raise_error(NameError)
end
- it "converts a non string/symbol/fixnum name to string using to_str" do
+ it "converts a non string/symbol name to string using to_str" do
c = Class.new { class_variable_set :@@class_var, "test" }
(o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var")
c.class_variable_defined?(o).should == true
@@ -60,12 +60,12 @@ describe "Module#class_variable_defined?" 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')
- lambda {
+ -> {
c.class_variable_defined?(o)
}.should raise_error(TypeError)
o.should_receive(:to_str).and_return(123)
- lambda {
+ -> {
c.class_variable_defined?(o)
}.should raise_error(TypeError)
end
diff --git a/spec/ruby/core/module/class_variable_get_spec.rb b/spec/ruby/core/module/class_variable_get_spec.rb
index 7068801cc0..e5d06731ec 100644
--- a/spec/ruby/core/module/class_variable_get_spec.rb
+++ b/spec/ruby/core/module/class_variable_get_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#class_variable_get" do
it "returns the value of the class variable with the given name" do
@@ -15,14 +15,14 @@ describe "Module#class_variable_get" do
it "raises a NameError for a class variable named '@@'" do
c = Class.new
- lambda { c.send(:class_variable_get, "@@") }.should raise_error(NameError)
- lambda { 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_error(NameError)
end
it "raises a NameError for a class variables with the given name defined in an extended module" do
c = Class.new
c.extend ModuleSpecs::MVars
- lambda {
+ -> {
c.send(:class_variable_get, "@@mvar")
}.should raise_error(NameError)
end
@@ -49,18 +49,18 @@ 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|
- lambda { c.send(:class_variable_get, cvar) }.should raise_error(NameError)
+ -> { c.send(:class_variable_get, cvar) }.should raise_error(NameError)
end
end
it "raises a NameError when the given name is not allowed" do
c = Class.new
- lambda { c.send(:class_variable_get, :invalid_name) }.should raise_error(NameError)
- lambda { 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_error(NameError)
end
- it "converts a non string/symbol/fixnum name to string using to_str" do
+ it "converts a non string/symbol name to string using to_str" do
c = Class.new { class_variable_set :@@class_var, "test" }
(o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var")
c.send(:class_variable_get, o).should == "test"
@@ -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')
- lambda { c.send(:class_variable_get, o) }.should raise_error(TypeError)
+ -> { c.send(:class_variable_get, o) }.should raise_error(TypeError)
o.should_receive(:to_str).and_return(123)
- lambda { c.send(:class_variable_get, o) }.should raise_error(TypeError)
+ -> { c.send(:class_variable_get, o) }.should raise_error(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 6d36298f5f..63f32f5389 100644
--- a/spec/ruby/core/module/class_variable_set_spec.rb
+++ b/spec/ruby/core/module/class_variable_set_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#class_variable_set" do
it "sets the class variable with the given name to the given value" do
@@ -25,27 +25,27 @@ describe "Module#class_variable_set" do
c.send(:class_variable_get, "@@mvar").should == :new_mvar
end
- it "raises a RuntimeError when self is frozen" do
- lambda {
+ it "raises a FrozenError when self is frozen" do
+ -> {
Class.new.freeze.send(:class_variable_set, :@@test, "test")
- }.should raise_error(RuntimeError)
- lambda {
+ }.should raise_error(FrozenError)
+ -> {
Module.new.freeze.send(:class_variable_set, :@@test, "test")
- }.should raise_error(RuntimeError)
+ }.should raise_error(FrozenError)
end
it "raises a NameError when the given name is not allowed" do
c = Class.new
- lambda {
+ -> {
c.send(:class_variable_set, :invalid_name, "test")
}.should raise_error(NameError)
- lambda {
+ -> {
c.send(:class_variable_set, "@invalid_name", "test")
}.should raise_error(NameError)
end
- it "converts a non string/symbol/fixnum name to string using to_str" do
+ it "converts a non string/symbol name to string using to_str" do
(o = mock('@@class_var')).should_receive(:to_str).and_return("@@class_var")
c = Class.new
c.send(:class_variable_set, o, "test")
@@ -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')
- lambda { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError)
+ -> { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError)
o.should_receive(:to_str).and_return(123)
- lambda { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError)
+ -> { c.send(:class_variable_set, o, "test") }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/module/class_variables_spec.rb b/spec/ruby/core/module/class_variables_spec.rb
index 066052cd01..e155f1deac 100644
--- a/spec/ruby/core/module/class_variables_spec.rb
+++ b/spec/ruby/core/module/class_variables_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#class_variables" do
it "returns an Array with the names of class variables of self" do
@@ -23,4 +23,12 @@ describe "Module#class_variables" do
c.extend ModuleSpecs::MVars
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/comparison_spec.rb b/spec/ruby/core/module/comparison_spec.rb
index e7608d64b9..86ee5db22a 100644
--- a/spec/ruby/core/module/comparison_spec.rb
+++ b/spec/ruby/core/module/comparison_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#<=>" do
it "returns -1 if self is a subclass of or includes the given module" do
@@ -16,7 +16,7 @@ describe "Module#<=>" do
(ModuleSpecs::Super <=> ModuleSpecs::Super).should == 0
end
- it "returns +1 if self is a superclas of or included by the given module" do
+ it "returns +1 if self is a superclass of or included by the given module" do
(ModuleSpecs::Parent <=> ModuleSpecs::Child).should == +1
(ModuleSpecs::Basic <=> ModuleSpecs::Child).should == +1
(ModuleSpecs::Super <=> ModuleSpecs::Child).should == +1
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..90cd36551a
--- /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.should have_private_instance_method(:const_added)
+ end
+
+ it "returns nil in the default implementation" do
+ Module.new do
+ const_added(:TEST).should == nil
+ end
+ end
+
+ it "for a class defined with the `class` keyword, const_added runs before inherited" do
+ ScratchPad.record []
+
+ mod = Module.new do
+ def self.const_added(_)
+ ScratchPad << :const_added
+ end
+ end
+
+ 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 0789a03e1c..8b137cd134 100644
--- a/spec/ruby/core/module/const_defined_spec.rb
+++ b/spec/ruby/core/module/const_defined_spec.rb
@@ -1,8 +1,8 @@
# encoding: utf-8
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../../../fixtures/constants', __FILE__)
-require File.expand_path('../fixtures/constant_unicode', __FILE__)
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+require_relative 'fixtures/constant_unicode'
describe "Module#const_defined?" do
it "returns true if the given Symbol names a constant defined in the receiver" do
@@ -17,11 +17,16 @@ describe "Module#const_defined?" do
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4).should be_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
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 be_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
@@ -40,6 +45,11 @@ describe "Module#const_defined?" do
ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, true).should be_true
end
+ it "coerces the inherit flag to a boolean" do
+ ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, nil).should be_false
+ ConstantSpecs::ContainerA::ChildA.const_defined?(:CS_CONST4, :true).should be_true
+ end
+
it "returns true if the given String names a constant defined in the receiver" do
ConstantSpecs.const_defined?("CS_CONST2").should == true
ConstantSpecs.const_defined?("ModuleA").should == true
@@ -55,6 +65,8 @@ describe "Module#const_defined?" do
str = "CS_CONSTλ".encode("euc-jp")
ConstantSpecs.const_set str, 1
ConstantSpecs.const_defined?(str).should be_true
+ ensure
+ ConstantSpecs.send(:remove_const, str)
end
it "returns false if the constant is not defined in the receiver, its superclass, or any included modules" do
@@ -70,10 +82,23 @@ 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_error(TypeError)
+ -> { ConstantSpecs.const_defined?([]) }.should raise_error(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_error(NoMethodError)
+ end
end
it "special cases Object and checks it's included Modules" do
@@ -105,19 +130,19 @@ describe "Module#const_defined?" do
end
it "raises a NameError if the name does not start with a capital letter" do
- lambda { ConstantSpecs.const_defined? "name" }.should raise_error(NameError)
+ -> { ConstantSpecs.const_defined? "name" }.should raise_error(NameError)
end
it "raises a NameError if the name starts with '_'" do
- lambda { ConstantSpecs.const_defined? "__CONSTX__" }.should raise_error(NameError)
+ -> { ConstantSpecs.const_defined? "__CONSTX__" }.should raise_error(NameError)
end
it "raises a NameError if the name starts with '@'" do
- lambda { ConstantSpecs.const_defined? "@Name" }.should raise_error(NameError)
+ -> { ConstantSpecs.const_defined? "@Name" }.should raise_error(NameError)
end
it "raises a NameError if the name starts with '!'" do
- lambda { ConstantSpecs.const_defined? "!Name" }.should raise_error(NameError)
+ -> { ConstantSpecs.const_defined? "!Name" }.should raise_error(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
- lambda { ConstantSpecs.const_defined? "Name=" }.should raise_error(NameError)
- lambda { ConstantSpecs.const_defined? "Name?" }.should raise_error(NameError)
+ -> { ConstantSpecs.const_defined? "Name=" }.should raise_error(NameError)
+ -> { ConstantSpecs.const_defined? "Name?" }.should raise_error(NameError)
end
it "raises a TypeError if conversion to a String by calling #to_str fails" do
name = mock('123')
- lambda { ConstantSpecs.const_defined? name }.should raise_error(TypeError)
+ -> { ConstantSpecs.const_defined? name }.should raise_error(TypeError)
name.should_receive(:to_str).and_return(123)
- lambda { ConstantSpecs.const_defined? name }.should raise_error(TypeError)
+ -> { ConstantSpecs.const_defined? name }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/module/const_get_spec.rb b/spec/ruby/core/module/const_get_spec.rb
index 6f8d5d930f..4b53cbe7b3 100644
--- a/spec/ruby/core/module/const_get_spec.rb
+++ b/spec/ruby/core/module/const_get_spec.rb
@@ -1,5 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../../../fixtures/constants', __FILE__)
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+require_relative 'fixtures/constants_autoload'
describe "Module#const_get" do
it "accepts a String or Symbol name" do
@@ -8,28 +9,28 @@ describe "Module#const_get" do
end
it "raises a NameError if no constant is defined in the search path" do
- lambda { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError)
+ -> { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError)
end
it "raises a NameError with the not found constant symbol" do
- error_inspection = lambda { |e| e.name.should == :CS_CONSTX }
- lambda { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError, &error_inspection)
+ error_inspection = -> e { e.name.should == :CS_CONSTX }
+ -> { ConstantSpecs.const_get :CS_CONSTX }.should raise_error(NameError, &error_inspection)
end
it "raises a NameError if the name does not start with a capital letter" do
- lambda { ConstantSpecs.const_get "name" }.should raise_error(NameError)
+ -> { ConstantSpecs.const_get "name" }.should raise_error(NameError)
end
it "raises a NameError if the name starts with a non-alphabetic character" do
- lambda { ConstantSpecs.const_get "__CONSTX__" }.should raise_error(NameError)
- lambda { ConstantSpecs.const_get "@CS_CONST1" }.should raise_error(NameError)
- lambda { ConstantSpecs.const_get "!CS_CONST1" }.should raise_error(NameError)
+ -> { 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)
end
it "raises a NameError if the name contains non-alphabetic characters except '_'" do
Object.const_get("CS_CONST1").should == :const1
- lambda { ConstantSpecs.const_get "CS_CONST1=" }.should raise_error(NameError)
- lambda { 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_error(NameError)
end
it "calls #to_str to convert the given name to a String" do
@@ -40,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')
- lambda { ConstantSpecs.const_get(name) }.should raise_error(TypeError)
+ -> { ConstantSpecs.const_get(name) }.should raise_error(TypeError)
name.should_receive(:to_str).and_return(123)
- lambda { ConstantSpecs.const_get(name) }.should raise_error(TypeError)
+ -> { ConstantSpecs.const_get(name) }.should raise_error(TypeError)
end
it "calls #const_missing on the receiver if unable to locate the constant" do
@@ -52,21 +53,21 @@ describe "Module#const_get" do
end
it "does not search the singleton class of a Class or Module" do
- lambda do
+ -> do
ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST14)
end.should raise_error(NameError)
- lambda { ConstantSpecs.const_get(:CS_CONST14) }.should raise_error(NameError)
+ -> { ConstantSpecs.const_get(:CS_CONST14) }.should raise_error(NameError)
end
it "does not search the containing scope" do
ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST20).should == :const20_2
- lambda do
+ -> do
ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST5)
end.should raise_error(NameError)
end
- it "raises a NameError if the constant is defined in the receiver's supperclass and the inherit flag is false" do
- lambda do
+ 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
@@ -76,39 +77,81 @@ describe "Module#const_get" do
end
it "raises a NameError when the receiver is a Module, the constant is defined at toplevel and the inherit flag is false" do
- lambda do
+ -> do
ConstantSpecs::ModuleA.const_get(:CS_CONST1, false)
end.should raise_error(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
- lambda do
+ -> do
ConstantSpecs::ContainerA::ChildA.const_get(:CS_CONST1, false)
end.should raise_error(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_error(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_error(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)
+ end
+
it "raises a NameError if only '::' is passed" do
- lambda { ConstantSpecs.const_get("::") }.should raise_error(NameError)
+ -> { ConstantSpecs.const_get("::") }.should raise_error(NameError)
end
it "raises a NameError if a Symbol has a toplevel scope qualifier" do
- lambda { ConstantSpecs.const_get(:'::CS_CONST1') }.should raise_error(NameError)
+ -> { ConstantSpecs.const_get(:'::CS_CONST1') }.should raise_error(NameError)
end
it "raises a NameError if a Symbol is a scoped constant name" do
- lambda { ConstantSpecs.const_get(:'ClassA::CS_CONST10') }.should raise_error(NameError)
+ -> { ConstantSpecs.const_get(:'ClassA::CS_CONST10') }.should raise_error(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
+ Object.const_get('CSAutoloadA').name.should == 'CSAutoloadA'
+ end
+
+ it 'does autoload a constant with a toplevel scope qualifier' do
+ Object.const_get('::CSAutoloadB').name.should == 'CSAutoloadB'
+ end
+
+ it 'does autoload a module and resolve a constant within' do
+ Object.const_get('CSAutoloadC::CONST').should == 7
+ end
+
+ it 'does autoload a non-toplevel module' 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_error(NameError)
end
describe "with statically assigned constants" do
@@ -159,50 +202,72 @@ 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
ConstantSpecs::ClassB::CS_CONST309 = :const309_1
ConstantSpecs::ClassB.const_get(:CS_CONST309).should == :const309_1
- lambda {
+ -> {
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 24f2b49edc..742218281c 100644
--- a/spec/ruby/core/module/const_missing_spec.rb
+++ b/spec/ruby/core/module/const_missing_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../../../fixtures/constants', __FILE__)
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
describe "Module#const_missing" do
it "is called when an undefined constant is referenced via literal form" do
@@ -11,7 +11,7 @@ describe "Module#const_missing" do
end
it "raises NameError and includes the name of the value that wasn't found" do
- lambda {
+ -> {
ConstantSpecs.const_missing("HelloMissing")
}.should raise_error(NameError, /ConstantSpecs::HelloMissing/)
end
@@ -24,4 +24,13 @@ describe "Module#const_missing" do
end
end
+ it "is called regardless of visibility" do
+ klass = Class.new do
+ def self.const_missing(name)
+ "Found:#{name}"
+ end
+ private_class_method :const_missing
+ end
+ klass::Hello.should == 'Found:Hello'
+ end
end
diff --git a/spec/ruby/core/module/const_set_spec.rb b/spec/ruby/core/module/const_set_spec.rb
index 6f4f6f980f..823768b882 100644
--- a/spec/ruby/core/module/const_set_spec.rb
+++ b/spec/ruby/core/module/const_set_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../../../fixtures/constants', __FILE__)
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
describe "Module#const_set" do
it "sets the constant specified by a String or Symbol to the given value" do
@@ -8,22 +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
- it "does not set the name of a module scoped by an anonymous module" 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 be_nil
+ b.name.should.end_with? '::B'
end
it "sets the name of contained modules when assigning a toplevel anonymous module" do
@@ -38,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
- lambda { ConstantSpecs.const_set "name", 1 }.should raise_error(NameError)
+ -> { ConstantSpecs.const_set "name", 1 }.should raise_error(NameError)
end
it "raises a NameError if the name starts with a non-alphabetic character" do
- lambda { ConstantSpecs.const_set "__CONSTX__", 1 }.should raise_error(NameError)
- lambda { ConstantSpecs.const_set "@Name", 1 }.should raise_error(NameError)
- lambda { ConstantSpecs.const_set "!Name", 1 }.should raise_error(NameError)
- lambda { ConstantSpecs.const_set "::Name", 1 }.should raise_error(NameError)
+ -> { 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)
end
it "raises a NameError if the name contains non-alphabetic characters except '_'" do
ConstantSpecs.const_set("CS_CONST404", :const404).should == :const404
- lambda { ConstantSpecs.const_set "Name=", 1 }.should raise_error(NameError)
- lambda { 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)
+ ensure
+ ConstantSpecs.send(:remove_const, :CS_CONST404)
end
it "calls #to_str to convert the given name to a String" do
@@ -62,14 +73,62 @@ 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')
- lambda { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError)
+ -> { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError)
name.should_receive(:to_str).and_return(123)
- lambda { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError)
+ -> { ConstantSpecs.const_set name, 1 }.should raise_error(TypeError)
+ end
+
+ describe "when overwriting an existing constant" do
+ it "warns if the previous value was a normal value" do
+ mod = Module.new
+ mod.const_set :Foo, 42
+ -> {
+ mod.const_set :Foo, 1
+ }.should complain(/already initialized constant/)
+ mod.const_get(:Foo).should == 1
+ end
+
+ it "does not warn if the previous value was an autoload" do
+ mod = Module.new
+ mod.autoload :Foo, "not-existing"
+ -> {
+ mod.const_set :Foo, 1
+ }.should_not complain
+ mod.const_get(:Foo).should == 1
+ end
+
+ 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.const_defined?(:Foo).should == false
+ mod.autoload?(:Foo).should == nil
+
+ -> {
+ mod.const_set :Foo, 1
+ }.should_not complain
+ mod.const_get(:Foo).should == 1
+ end
+
+ it "does not warn if the new value is an autoload" do
+ mod = Module.new
+ mod.const_set :Foo, 42
+ -> {
+ mod.autoload :Foo, "not-existing"
+ }.should_not complain
+ mod.const_get(:Foo).should == 42
+ end
end
describe "on a frozen module" do
@@ -78,8 +137,8 @@ describe "Module#const_set" do
@name = :Foo
end
- it "raises a RuntimeError before setting the name" do
- lambda { @frozen.const_set @name, nil }.should raise_error(RuntimeError)
+ it "raises a FrozenError before setting the name" do
+ -> { @frozen.const_set @name, nil }.should raise_error(FrozenError)
@frozen.should_not have_constant(@name)
end
end
diff --git a/spec/ruby/core/module/const_source_location_spec.rb b/spec/ruby/core/module/const_source_location_spec.rb
new file mode 100644
index 0000000000..96649ea10b
--- /dev/null
+++ b/spec/ruby/core/module/const_source_location_spec.rb
@@ -0,0 +1,281 @@
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+
+describe "Module#const_source_location" do
+ before do
+ @constants_fixture_path = File.expand_path('../../fixtures/constants.rb', __dir__)
+ end
+
+ 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
+
+ it "searches a path in a module included in the immediate class before the superclass" do
+ ConstantSpecs::ParentB::CSL_CONST302 = :const302_1
+ ConstantSpecs::ModuleF::CSL_CONST302 = :const302_2
+ ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST302).should == [__FILE__, __LINE__ - 1]
+ ensure
+ ConstantSpecs::ParentB.send(:remove_const, :CSL_CONST302)
+ ConstantSpecs::ModuleF.send(:remove_const, :CSL_CONST302)
+ end
+
+ it "searches a path in the superclass before a module included in the superclass" do
+ ConstantSpecs::ModuleE::CSL_CONST303 = :const303_1
+ ConstantSpecs::ParentB::CSL_CONST303 = :const303_2
+ ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST303).should == [__FILE__, __LINE__ - 1]
+ ensure
+ ConstantSpecs::ModuleE.send(:remove_const, :CSL_CONST303)
+ ConstantSpecs::ParentB.send(:remove_const, :CSL_CONST303)
+ end
+
+ it "searches a path in a module included in the superclass" do
+ ConstantSpecs::ModuleA::CSL_CONST304 = :const304_1
+ ConstantSpecs::ModuleE::CSL_CONST304 = :const304_2
+ ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST304).should == [__FILE__, __LINE__ - 1]
+ ensure
+ ConstantSpecs::ModuleA.send(:remove_const, :CSL_CONST304)
+ ConstantSpecs::ModuleE.send(:remove_const, :CSL_CONST304)
+ end
+
+ it "searches a path in the superclass chain" do
+ ConstantSpecs::ModuleA::CSL_CONST305 = :const305
+ ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST305).should == [__FILE__, __LINE__ - 1]
+ ensure
+ ConstantSpecs::ModuleA.send(:remove_const, :CSL_CONST305)
+ end
+
+ it "returns path to a toplevel constant when the receiver is a Class" do
+ Object::CSL_CONST306 = :const306
+ ConstantSpecs::ContainerB::ChildB.const_source_location(:CSL_CONST306).should == [__FILE__, __LINE__ - 1]
+ ensure
+ Object.send(:remove_const, :CSL_CONST306)
+ end
+
+ it "returns path to a toplevel constant when the receiver is a Module" do
+ Object::CSL_CONST308 = :const308
+ ConstantSpecs.const_source_location(:CSL_CONST308).should == [__FILE__, __LINE__ - 1]
+ ConstantSpecs::ModuleA.const_source_location(:CSL_CONST308).should == [__FILE__, __LINE__ - 2]
+ ensure
+ Object.send(:remove_const, :CSL_CONST308)
+ end
+
+ it "returns path to the updated value of a constant" do
+ ConstantSpecs::ClassB::CSL_CONST309 = :const309_1
+ ConstantSpecs::ClassB.const_source_location(:CSL_CONST309).should == [__FILE__, __LINE__ - 1]
+
+ -> {
+ 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
+
+ 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 "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 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 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 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 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 Class" do
+ ConstantSpecs::ContainerA::ChildA.const_source_location(:CS_CONST1).should == [@constants_fixture_path, CS_CONST1_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 "return empty path if constant defined in C code" do
+ Object.const_source_location(:String).should == []
+ 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 "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 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 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 "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 "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 TypeError if conversion to a String by calling #to_str fails" do
+ name = mock('123')
+ -> { ConstantSpecs.const_source_location(name) }.should raise_error(TypeError)
+
+ name.should_receive(:to_str).and_return(123)
+ -> { ConstantSpecs.const_source_location(name) }.should raise_error(TypeError)
+ 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 "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 "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 "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 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 "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 toplevel scope qualifier" do
+ ConstantSpecs.const_source_location("::CS_CONST1").should == [@constants_fixture_path, CS_CONST1_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 "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 "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_error(NameError)
+ end
+
+ it "raises a NameError if a Symbol has a toplevel scope qualifier" do
+ -> { ConstantSpecs.const_source_location(:'::CS_CONST1') }.should raise_error(NameError)
+ 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)
+ end
+
+ it "does search private constants path" do
+ ConstantSpecs.const_source_location(:CS_PRIVATE).should == [@constants_fixture_path, ConstantSpecs::CS_PRIVATE_LINE]
+ end
+
+ 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
+
+ context 'autoload' do
+ before :all do
+ ConstantSpecs.autoload :CSL_CONST1, "#{__dir__}/notexisting.rb"
+ @line = __LINE__ - 1
+ end
+
+ before :each do
+ @loaded_features = $".dup
+ end
+
+ after :each do
+ $".replace @loaded_features
+ end
+
+ it 'returns the autoload location while not resolved' do
+ ConstantSpecs.const_source_location('CSL_CONST1').should == [__FILE__, @line]
+ end
+
+ 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
+
+ ruby_bug("#20188", ""..."3.4") do
+ it 'returns the real constant location as soon as it is defined' do
+ file = fixture(__FILE__, 'autoload_const_source_location.rb')
+ ConstantSpecs.autoload :ConstSource, file
+ autoload_location = [__FILE__, __LINE__ - 1]
+
+ ConstantSpecs.const_source_location(:ConstSource).should == autoload_location
+ ConstantSpecs::ConstSource::LOCATION.should == ConstantSpecs.const_source_location(:ConstSource)
+ ConstantSpecs::BEFORE_DEFINE_LOCATION.should == autoload_location
+ ConstantSpecs.send :remove_const, :ConstSource
+ ConstantSpecs.send :remove_const, :BEFORE_DEFINE_LOCATION
+ end
+ end
+ end
+end
diff --git a/spec/ruby/core/module/constants_spec.rb b/spec/ruby/core/module/constants_spec.rb
index c331482bfb..330da1cc88 100644
--- a/spec/ruby/core/module/constants_spec.rb
+++ b/spec/ruby/core/module/constants_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../../../fixtures/constants', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
+require_relative 'fixtures/classes'
describe "Module.constants" do
it "returns an array of the names of all toplevel constants" do
@@ -13,9 +13,9 @@ describe "Module.constants" do
it "returns an array of Symbol names" do
# This in NOT an exhaustive list
- Module.constants.should include(:Array, :Bignum, :Class, :Comparable, :Dir,
+ Module.constants.should include(:Array, :Class, :Comparable, :Dir,
:Enumerable, :ENV, :Exception, :FalseClass,
- :File, :Fixnum, :Float, :Hash, :Integer, :IO,
+ :File, :Float, :Hash, :Integer, :IO,
:Kernel, :Math, :Method, :Module, :NilClass,
:Numeric, :Object, :Range, :Regexp, :String,
:Symbol, :Thread, :Time, :TrueClass)
@@ -43,37 +43,43 @@ end
describe "Module#constants" do
it "returns an array of Symbol names of all constants defined in the module and all included modules" do
ConstantSpecs::ContainerA.constants.sort.should == [
- :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
+ :CS_CONST10, :CS_CONST10_LINE, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
]
end
it "returns all constants including inherited when passed true" do
ConstantSpecs::ContainerA.constants(true).sort.should == [
- :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
+ :CS_CONST10, :CS_CONST10_LINE, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
]
end
it "returns all constants including inherited when passed some object" do
ConstantSpecs::ContainerA.constants(Object.new).sort.should == [
- :CS_CONST10, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
+ :CS_CONST10, :CS_CONST10_LINE, :CS_CONST23, :CS_CONST24, :CS_CONST5, :ChildA
]
end
it "doesn't returns inherited constants when passed false" do
ConstantSpecs::ContainerA.constants(false).sort.should == [
- :CS_CONST10, :CS_CONST23, :CS_CONST5, :ChildA
+ :CS_CONST10, :CS_CONST10_LINE, :CS_CONST23, :CS_CONST5, :ChildA
]
end
it "doesn't returns inherited constants when passed nil" do
ConstantSpecs::ContainerA.constants(nil).sort.should == [
- :CS_CONST10, :CS_CONST23, :CS_CONST5, :ChildA
+ :CS_CONST10, :CS_CONST10_LINE, :CS_CONST23, :CS_CONST5, :ChildA
]
end
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
diff --git a/spec/ruby/core/module/define_method_spec.rb b/spec/ruby/core/module/define_method_spec.rb
index 64ac5306f0..c5dfc53764 100644
--- a/spec/ruby/core/module/define_method_spec.rb
+++ b/spec/ruby/core/module/define_method_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
class DefineMethodSpecClass
end
@@ -12,7 +12,7 @@ describe "passed { |a, b = 1| } creates a method that" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(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
- lambda { @klass.new.m(1, 2, 3) }.should raise_error(ArgumentError)
+ -> { @klass.new.m(1, 2, 3) }.should raise_error(ArgumentError)
end
end
@@ -83,11 +83,28 @@ describe "Module#define_method when given an UnboundMethod" do
define_method :piggy, instance_method(:ziggy)
end
- lambda { foo.new.ziggy }.should raise_error(NoMethodError)
+ -> { foo.new.ziggy }.should raise_error(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
@@ -116,6 +133,17 @@ describe "Module#define_method when name is not a special private name" do
klass.should have_public_instance_method(: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
@@ -194,7 +222,7 @@ describe "Module#define_method" do
it "defines a new method with the given name and the given block as body in self" do
class DefineMethodSpecClass
define_method(:block_test1) { self }
- define_method(:block_test2, &lambda { self })
+ define_method(:block_test2, &-> { self })
end
o = DefineMethodSpecClass.new
@@ -202,39 +230,74 @@ 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_error(TypeError, /is not a symbol nor a string/)
+
+ -> {
+ Class.new { define_method([], -> {}) }
+ }.should raise_error(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_error(TypeError, /can't convert Object to String/)
+ end
+
it "raises a TypeError when the given method is no Method/Proc" do
- lambda {
+ -> {
Class.new { define_method(:test, "self") }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type String (expected Proc/Method/UnboundMethod)")
- lambda {
+ -> {
Class.new { define_method(:test, 1234) }
- }.should raise_error(TypeError)
+ }.should raise_error(TypeError, "wrong argument type Integer (expected Proc/Method/UnboundMethod)")
- lambda {
+ -> {
Class.new { define_method(:test, nil) }
- }.should raise_error(TypeError)
+ }.should raise_error(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
- lambda {
+ -> {
Class.new { define_method(:test) }
}.should raise_error(ArgumentError)
end
- ruby_version_is "2.3" do
- it "does not use the caller block when no block is given" do
- o = Object.new
- def o.define(name)
- self.class.class_eval do
- define_method(name)
- end
+ it "does not use the caller block when no block is given" do
+ o = Object.new
+ def o.define(name)
+ self.class.class_eval do
+ define_method(name)
end
-
- lambda {
- o.define(:foo) { raise "not used" }
- }.should raise_error(ArgumentError)
end
+
+ -> {
+ o.define(:foo) { raise "not used" }
+ }.should raise_error(ArgumentError)
end
it "does not change the arity check style of the original proc" do
@@ -244,13 +307,13 @@ describe "Module#define_method" do
end
obj = DefineMethodSpecClass.new
- lambda { obj.proc_style_test :arg }.should raise_error(ArgumentError)
+ -> { obj.proc_style_test :arg }.should raise_error(ArgumentError)
end
- it "raises a RuntimeError if frozen" do
- lambda {
+ it "raises a FrozenError if frozen" do
+ -> {
Class.new { freeze; define_method(:foo) {} }
- }.should raise_error(RuntimeError)
+ }.should raise_error(FrozenError)
end
it "accepts a Method (still bound)" do
@@ -269,7 +332,7 @@ describe "Module#define_method" do
c = klass.new
c.data = :bar
c.other_inspect.should == "data is bar"
- lambda{o.other_inspect}.should raise_error(NoMethodError)
+ ->{o.other_inspect}.should raise_error(NoMethodError)
end
it "raises a TypeError when a Method from a singleton class is defined on another class" do
@@ -281,9 +344,9 @@ describe "Module#define_method" do
end
m = c.method(:foo)
- lambda {
+ -> {
Class.new { define_method :bar, m }
- }.should raise_error(TypeError)
+ }.should raise_error(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 +356,7 @@ describe "Module#define_method" do
end
m = c.new.method(:foo)
- lambda {
+ -> {
Class.new { define_method :bar, m }
}.should raise_error(TypeError)
end
@@ -307,7 +370,7 @@ describe "Module#define_method" do
o = DefineMethodSpecClass.new
DefineMethodSpecClass.send(:undef_method, :accessor_method)
- lambda { o.accessor_method }.should raise_error(NoMethodError)
+ -> { o.accessor_method }.should raise_error(NoMethodError)
DefineMethodSpecClass.send(:define_method, :accessor_method, m)
@@ -335,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
@@ -357,15 +428,8 @@ describe "Module#define_method" do
klass.new.string_test.should == "string_test result"
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- Module.should have_private_instance_method(:define_method)
- end
- end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:define_method)
- end
+ it "is a public method" do
+ Module.should have_public_instance_method(:define_method)
end
it "returns its symbol" do
@@ -398,20 +462,72 @@ describe "Module#define_method" do
klass.new.should respond_to(:bar)
end
+
+ it "allows an UnboundMethod of a Kernel method retrieved from Object to defined on a BasicObject subclass" do
+ klass = Class.new(BasicObject) do
+ define_method :instance_of?, ::Object.instance_method(:instance_of?)
+ end
+ klass.new.instance_of?(klass).should == true
+ end
+
it "raises a TypeError when an UnboundMethod from a child class is defined on a parent class" do
- lambda {
+ -> {
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)
+ }.should raise_error(TypeError, /bind argument must be a subclass of ChildClass/)
+ ensure
+ Object.send(:remove_const, :ParentClass)
+ Object.send(:remove_const, :ChildClass)
end
it "raises a TypeError when an UnboundMethod from one class is defined on an unrelated class" do
- lambda {
+ -> {
DestinationClass = Class.new {
define_method :bar, ModuleSpecs::InstanceMeth.instance_method(:foo)
}
- }.should raise_error(TypeError)
+ }.should raise_error(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
+ c = Class.new do
+ class << self
+ def foo
+ end
+ end
+ end
+ m = c.method(:foo).unbind
+
+ -> {
+ Class.new { define_method :bar, m }
+ }.should raise_error(TypeError, /can't bind singleton method to a different class/)
+ end
+
+ it "defines a new method with public visibility when a Method passed and the class/module of the context isn't equal to the receiver of #define_method" do
+ c = Class.new do
+ private def foo
+ "public"
+ end
+ end
+
+ object = c.new
+ object.singleton_class.define_method(:bar, object.method(:foo))
+
+ object.bar.should == "public"
+ end
+
+ it "defines the new method according to the scope visibility when a Method passed and the class/module of the context is equal to the receiver of #define_method" do
+ c = Class.new do
+ def foo; end
+ end
+
+ object = c.new
+ object.singleton_class.class_eval do
+ private
+ define_method(:bar, c.new.method(:foo))
+ end
+
+ -> { object.bar }.should raise_error(NoMethodError)
end
end
@@ -428,11 +544,11 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed one argument" do
- lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1 }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed two arguments" do
- lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
end
end
@@ -448,11 +564,11 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed one argument" do
- lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1 }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed two arguments" do
- lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
end
end
@@ -464,21 +580,50 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed zero arguments and a block" do
- lambda { @klass.new.m { :computed } }.should raise_error(ArgumentError)
+ -> { @klass.new.m { :computed } }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed two arguments" do
- lambda { @klass.new.m 1, 2 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1, 2 }.should raise_error(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_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed zero arguments and a block" do
+ -> { @klass.new.m { :computed } }.should raise_error(ArgumentError)
+ end
+
+ it "raises an ArgumentError when passed two arguments" do
+ -> { @klass.new.m 1, 2 }.should raise_error(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
@@ -509,7 +654,7 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "returns the value computed by the block when passed one argument" do
@@ -537,19 +682,19 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed one argument" do
- lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1 }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed one argument and a block" do
- lambda { @klass.new.m(1) { } }.should raise_error(ArgumentError)
+ -> { @klass.new.m(1) { } }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed three arguments" do
- lambda { @klass.new.m 1, 2, 3 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1, 2, 3 }.should raise_error(ArgumentError)
end
end
@@ -561,15 +706,15 @@ describe "Module#define_method" do
end
it "raises an ArgumentError when passed zero arguments" do
- lambda { @klass.new.m }.should raise_error(ArgumentError)
+ -> { @klass.new.m }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed one argument" do
- lambda { @klass.new.m 1 }.should raise_error(ArgumentError)
+ -> { @klass.new.m 1 }.should raise_error(ArgumentError)
end
it "raises an ArgumentError when passed one argument and a block" do
- lambda { @klass.new.m(1) { } }.should raise_error(ArgumentError)
+ -> { @klass.new.m(1) { } }.should raise_error(ArgumentError)
end
it "receives an empty array as the third argument when passed two arguments" do
@@ -582,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)
@@ -607,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)
@@ -631,3 +776,71 @@ describe "Method#define_method when passed an UnboundMethod object" do
@obj.method(:n).parameters.should == @obj.method(:m).parameters
end
end
+
+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
+ t = Class.new do
+ prc = -> {
+ def nested_method_in_proc_for_define_method
+ 42
+ end
+ }
+ end
+
+ c = Class.new do
+ define_method(:test, prc)
+ end
+
+ o = c.new
+ o.test
+ o.should_not have_method :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/define_singleton_method_spec.rb b/spec/ruby/core/module/define_singleton_method_spec.rb
index 0841d5d7e2..eb5cb89ed1 100644
--- a/spec/ruby/core/module/define_singleton_method_spec.rb
+++ b/spec/ruby/core/module/define_singleton_method_spec.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../../../spec_helper', __FILE__)
+require_relative '../../spec_helper'
describe "Module#define_singleton_method" do
it "defines the given method as an class method with the given name in self" do
@@ -6,12 +6,10 @@ describe "Module#define_singleton_method" do
define_singleton_method :a do
42
end
- define_singleton_method(:b, lambda {|x| 2*x })
+ define_singleton_method(:b, -> x { 2*x })
end
klass.a.should == 42
klass.b(10).should == 20
end
-
- it "needs to be reviewed for spec completeness"
end
diff --git a/spec/ruby/core/module/deprecate_constant_spec.rb b/spec/ruby/core/module/deprecate_constant_spec.rb
index 8c7a170f1c..ec0de6782f 100644
--- a/spec/ruby/core/module/deprecate_constant_spec.rb
+++ b/spec/ruby/core/module/deprecate_constant_spec.rb
@@ -1,52 +1,70 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-
-ruby_version_is "2.3" do
- describe "Module#deprecate_constant" do
- before :each do
- @module = Module.new
- @value = :value
- @module::PUBLIC1 = @value
- @module::PUBLIC2 = @value
- @module::PRIVATE = @value
- @module.private_constant :PRIVATE
- @module.deprecate_constant :PRIVATE
- @pattern = /deprecated/
+require_relative '../../spec_helper'
+
+describe "Module#deprecate_constant" do
+ before :each do
+ @module = Module.new
+ @value = :value
+ @module::PUBLIC1 = @value
+ @module::PUBLIC2 = @value
+ @module::PRIVATE = @value
+ @module.private_constant :PRIVATE
+ @module.deprecate_constant :PRIVATE
+ end
+
+ describe "when accessing the deprecated module" do
+ it "passes the accessing" do
+ @module.deprecate_constant :PUBLIC1
+
+ value = nil
+ -> {
+ value = @module::PUBLIC1
+ }.should complain(/warning: constant .+::PUBLIC1 is deprecated/)
+ value.should equal(@value)
+
+ -> { @module::PRIVATE }.should raise_error(NameError)
end
- describe "when accessing the deprecated module" do
- it "passes the accessing" do
- @module.deprecate_constant :PUBLIC1
+ it "warns with a message" do
+ @module.deprecate_constant :PUBLIC1
+
+ -> { @module::PUBLIC1 }.should complain(/warning: constant .+::PUBLIC1 is deprecated/)
+ -> { @module.const_get :PRIVATE }.should complain(/warning: constant .+::PRIVATE is deprecated/)
+ end
- value = nil
- lambda {
- value = @module::PUBLIC1
- }.should complain(@pattern)
- value.should equal(@value)
+ it "does not warn if Warning[:deprecated] is false" do
+ @module.deprecate_constant :PUBLIC1
- lambda { @module::PRIVATE }.should raise_error(NameError)
+ 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
-
- lambda { @module::PUBLIC1 }.should complain(@pattern)
- lambda { @module.const_get :PRIVATE }.should complain(@pattern)
+ -> { @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
+ it "accepts multiple symbols and strings as constant names" do
+ @module.deprecate_constant "PUBLIC1", :PUBLIC2
- lambda { @module::PUBLIC1 }.should complain(@pattern)
- lambda { @module::PUBLIC2 }.should complain(@pattern)
- end
+ -> { @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)
- end
+ it "returns self" do
+ @module.deprecate_constant(:PUBLIC1).should equal(@module)
+ end
- it "raises a NameError when given an undefined name" do
- lambda { @module.deprecate_constant :UNDEFINED }.should raise_error(NameError)
- end
+ it "raises a NameError when given an undefined name" do
+ -> { @module.deprecate_constant :UNDEFINED }.should raise_error(NameError)
end
end
diff --git a/spec/ruby/core/module/eql_spec.rb b/spec/ruby/core/module/eql_spec.rb
index f8878d7f4e..76bb271d8d 100644
--- a/spec/ruby/core/module/eql_spec.rb
+++ b/spec/ruby/core/module/eql_spec.rb
@@ -1,7 +1,7 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/equal_value', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/equal_value'
describe "Module#eql?" do
- it_behaves_like(:module_equal, :eql?)
+ it_behaves_like :module_equal, :eql?
end
diff --git a/spec/ruby/core/module/equal_spec.rb b/spec/ruby/core/module/equal_spec.rb
index bc70a6277a..01ab06152d 100644
--- a/spec/ruby/core/module/equal_spec.rb
+++ b/spec/ruby/core/module/equal_spec.rb
@@ -1,7 +1,7 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/equal_value', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/equal_value'
describe "Module#equal?" do
- it_behaves_like(:module_equal, :equal?)
+ it_behaves_like :module_equal, :equal?
end
diff --git a/spec/ruby/core/module/equal_value_spec.rb b/spec/ruby/core/module/equal_value_spec.rb
index 12494477f9..a7191cd755 100644
--- a/spec/ruby/core/module/equal_value_spec.rb
+++ b/spec/ruby/core/module/equal_value_spec.rb
@@ -1,7 +1,7 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/equal_value', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/equal_value'
describe "Module#==" do
- it_behaves_like(:module_equal, :==)
+ it_behaves_like :module_equal, :==
end
diff --git a/spec/ruby/core/module/extend_object_spec.rb b/spec/ruby/core/module/extend_object_spec.rb
index 73c1623dce..1fd1abc0b5 100644
--- a/spec/ruby/core/module/extend_object_spec.rb
+++ b/spec/ruby/core/module/extend_object_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#extend_object" do
before :each do
@@ -16,7 +16,7 @@ describe "Module#extend_object" do
end
it "raises a TypeError if calling after rebinded to Class" do
- lambda {
+ -> {
Module.instance_method(:extend_object).bind(Class.new).call Object.new
}.should raise_error(TypeError)
end
@@ -42,18 +42,6 @@ describe "Module#extend_object" do
ScratchPad.recorded.should == :extended
end
- 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
-
describe "when given a frozen object" do
before :each do
@receiver = Module.new
@@ -61,7 +49,7 @@ describe "Module#extend_object" do
end
it "raises a RuntimeError before extending the object" do
- lambda { @receiver.send(:extend_object, @object) }.should raise_error(RuntimeError)
+ -> { @receiver.send(:extend_object, @object) }.should raise_error(RuntimeError)
@object.should_not be_kind_of(@receiver)
end
end
diff --git a/spec/ruby/core/module/extended_spec.rb b/spec/ruby/core/module/extended_spec.rb
index 0a62dd9850..c6300ffa0b 100644
--- a/spec/ruby/core/module/extended_spec.rb
+++ b/spec/ruby/core/module/extended_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#extended" do
it "is called when an object gets extended with self" do
diff --git a/spec/ruby/core/module/fixtures/autoload_callback.rb b/spec/ruby/core/module/fixtures/autoload_callback.rb
new file mode 100644
index 0000000000..51d53eb580
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_callback.rb
@@ -0,0 +1,2 @@
+block = ScratchPad.recorded
+block.call
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.rb b/spec/ruby/core/module/fixtures/autoload_during_autoload.rb
new file mode 100644
index 0000000000..5202bd8b23
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_during_autoload.rb
@@ -0,0 +1,7 @@
+block = ScratchPad.recorded
+ScratchPad.record(block.call)
+
+module ModuleSpecs::Autoload
+ class DuringAutoload
+ 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_during_require.rb b/spec/ruby/core/module/fixtures/autoload_during_require.rb
new file mode 100644
index 0000000000..6fd81592e3
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_during_require.rb
@@ -0,0 +1,4 @@
+module ModuleSpecs::Autoload
+ class AutoloadDuringRequire
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_during_require_current_file.rb b/spec/ruby/core/module/fixtures/autoload_during_require_current_file.rb
new file mode 100644
index 0000000000..5aa8595065
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_during_require_current_file.rb
@@ -0,0 +1,5 @@
+module ModuleSpecs::Autoload
+ autoload(:AutoloadCurrentFile, __FILE__)
+
+ ScratchPad.record autoload?(:AutoloadCurrentFile)
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_exception.rb b/spec/ruby/core/module/fixtures/autoload_exception.rb
new file mode 100644
index 0000000000..09acf9f537
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_exception.rb
@@ -0,0 +1,3 @@
+ScratchPad.record(:exception)
+
+raise 'intentional error to test failure conditions during autoloading'
diff --git a/spec/ruby/core/module/fixtures/autoload_location.rb b/spec/ruby/core/module/fixtures/autoload_location.rb
new file mode 100644
index 0000000000..318851b2df
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_location.rb
@@ -0,0 +1,3 @@
+module ConstantSpecs
+ CONST_LOCATION = __LINE__
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_nested.rb b/spec/ruby/core/module/fixtures/autoload_nested.rb
new file mode 100644
index 0000000000..073cec0dce
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_nested.rb
@@ -0,0 +1,8 @@
+module ModuleSpecs::Autoload
+ module GoodParent
+ class Nested
+ end
+ end
+end
+
+ScratchPad.record(:loaded)
diff --git a/spec/ruby/core/module/fixtures/autoload_o.rb b/spec/ruby/core/module/fixtures/autoload_o.rb
index 6d54ddaf12..7d88f969b2 100644
--- a/spec/ruby/core/module/fixtures/autoload_o.rb
+++ b/spec/ruby/core/module/fixtures/autoload_o.rb
@@ -1 +1,2 @@
# does not define ModuleSpecs::Autoload::O
+ScratchPad << :loaded
diff --git a/spec/ruby/core/module/fixtures/autoload_overridden.rb b/spec/ruby/core/module/fixtures/autoload_overridden.rb
new file mode 100644
index 0000000000..7062bcfabc
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_overridden.rb
@@ -0,0 +1,3 @@
+module ModuleSpecs::Autoload
+ Overridden = :overridden
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_raise.rb b/spec/ruby/core/module/fixtures/autoload_raise.rb
new file mode 100644
index 0000000000..f6051e3ba2
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_raise.rb
@@ -0,0 +1,2 @@
+ScratchPad << :raise
+raise "exception during autoload"
diff --git a/spec/ruby/core/module/fixtures/autoload_required_directly.rb b/spec/ruby/core/module/fixtures/autoload_required_directly.rb
new file mode 100644
index 0000000000..bed60a71ec
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_required_directly.rb
@@ -0,0 +1,7 @@
+block = ScratchPad.recorded
+ScratchPad.record(block.call)
+
+module ModuleSpecs::Autoload
+ class RequiredDirectly
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_required_directly_nested.rb b/spec/ruby/core/module/fixtures/autoload_required_directly_nested.rb
new file mode 100644
index 0000000000..a9f11c2188
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_required_directly_nested.rb
@@ -0,0 +1 @@
+ScratchPad.recorded.call
diff --git a/spec/ruby/core/module/fixtures/autoload_required_directly_no_constant.rb b/spec/ruby/core/module/fixtures/autoload_required_directly_no_constant.rb
new file mode 100644
index 0000000000..25e08c1129
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_required_directly_no_constant.rb
@@ -0,0 +1,2 @@
+block = ScratchPad.recorded
+ScratchPad.record(block.call)
diff --git a/spec/ruby/core/module/fixtures/autoload_scope.rb b/spec/ruby/core/module/fixtures/autoload_scope.rb
deleted file mode 100644
index 04193687b5..0000000000
--- a/spec/ruby/core/module/fixtures/autoload_scope.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module ModuleSpecs
- module Autoload
- class PP
- class QQ
- end
- end
- end
-end
diff --git a/spec/ruby/core/module/fixtures/autoload_self_during_require.rb b/spec/ruby/core/module/fixtures/autoload_self_during_require.rb
new file mode 100644
index 0000000000..f4a514a807
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/autoload_self_during_require.rb
@@ -0,0 +1,5 @@
+module ModuleSpecs::Autoload
+ autoload :AutoloadSelfDuringRequire, __FILE__
+ class AutoloadSelfDuringRequire
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/autoload_subclass.rb b/spec/ruby/core/module/fixtures/autoload_subclass.rb
index 569972118c..8027fa3fcd 100644
--- a/spec/ruby/core/module/fixtures/autoload_subclass.rb
+++ b/spec/ruby/core/module/fixtures/autoload_subclass.rb
@@ -1,10 +1,10 @@
-class YY
+class CS_CONST_AUTOLOAD
end
module ModuleSpecs
module Autoload
module XX
- class YY < YY
+ class CS_CONST_AUTOLOAD < CS_CONST_AUTOLOAD
end
end
end
diff --git a/spec/ruby/core/module/fixtures/classes.rb b/spec/ruby/core/module/fixtures/classes.rb
index f93c39683e..964f64c593 100644
--- a/spec/ruby/core/module/fixtures/classes.rb
+++ b/spec/ruby/core/module/fixtures/classes.rb
@@ -1,11 +1,14 @@
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
CONST = :plain_constant
+ class NamedClass
+ end
+
module PrivConstModule
PRIVATE_CONSTANT = 1
private_constant :PRIVATE_CONSTANT
@@ -39,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
@@ -349,6 +360,10 @@ module ModuleSpecs
end
end
+ class SubCVars < CVars
+ @@sub = :sub
+ end
+
module MVars
@@mvar = :mvar
end
@@ -373,6 +388,7 @@ module ModuleSpecs
# empty modules
module M1; end
module M2; end
+ module M3; end
module Autoload
def self.use_ex1
@@ -386,6 +402,12 @@ module ModuleSpecs
end
end
+ class Parent
+ end
+
+ class Child < Parent
+ end
+
module FromThread
module A
autoload :B, fixture(__FILE__, "autoload_empty.rb")
@@ -574,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/constants_autoload.rb b/spec/ruby/core/module/fixtures/constants_autoload.rb
new file mode 100644
index 0000000000..8e9aa8de0c
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload.rb
@@ -0,0 +1,6 @@
+autoload :CSAutoloadA, fixture(__FILE__, 'constants_autoload_a.rb')
+autoload :CSAutoloadB, fixture(__FILE__, 'constants_autoload_b.rb')
+autoload :CSAutoloadC, fixture(__FILE__, 'constants_autoload_c.rb')
+module CSAutoloadD
+ autoload :InnerModule, fixture(__FILE__, 'constants_autoload_d.rb')
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_a.rb b/spec/ruby/core/module/fixtures/constants_autoload_a.rb
new file mode 100644
index 0000000000..48d3b63681
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_a.rb
@@ -0,0 +1,2 @@
+module CSAutoloadA
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_b.rb b/spec/ruby/core/module/fixtures/constants_autoload_b.rb
new file mode 100644
index 0000000000..29cd742d03
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_b.rb
@@ -0,0 +1,2 @@
+module CSAutoloadB
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_c.rb b/spec/ruby/core/module/fixtures/constants_autoload_c.rb
new file mode 100644
index 0000000000..9d6a6bf4d7
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_c.rb
@@ -0,0 +1,3 @@
+module CSAutoloadC
+ CONST = 7
+end
diff --git a/spec/ruby/core/module/fixtures/constants_autoload_d.rb b/spec/ruby/core/module/fixtures/constants_autoload_d.rb
new file mode 100644
index 0000000000..52d550bab0
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/constants_autoload_d.rb
@@ -0,0 +1,4 @@
+module CSAutoloadD
+ module InnerModule
+ 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/multi/foo.rb b/spec/ruby/core/module/fixtures/multi/foo.rb
new file mode 100644
index 0000000000..549996f08f
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/multi/foo.rb
@@ -0,0 +1,6 @@
+module ModuleSpecs::Autoload
+ module Foo
+ autoload :Bar, 'foo/bar_baz'
+ autoload :Baz, 'foo/bar_baz'
+ end
+end
diff --git a/spec/ruby/core/module/fixtures/multi/foo/bar_baz.rb b/spec/ruby/core/module/fixtures/multi/foo/bar_baz.rb
new file mode 100644
index 0000000000..53d3849e1f
--- /dev/null
+++ b/spec/ruby/core/module/fixtures/multi/foo/bar_baz.rb
@@ -0,0 +1,11 @@
+require 'foo'
+
+module ModuleSpecs::Autoload
+ module Foo
+ class Bar
+ end
+
+ class Baz
+ end
+ 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/refine.rb b/spec/ruby/core/module/fixtures/refine.rb
index 46975361dd..e8215aa640 100644
--- a/spec/ruby/core/module/fixtures/refine.rb
+++ b/spec/ruby/core/module/fixtures/refine.rb
@@ -3,6 +3,10 @@ module ModuleSpecs
def foo; "foo" end
end
+ class ClassWithSuperFoo
+ def foo; [:C] end
+ end
+
module PrependedModule
def foo; "foo from prepended module"; end
end
@@ -10,4 +14,12 @@ module ModuleSpecs
module IncludedModule
def foo; "foo from included module"; end
end
+
+ def self.build_refined_class(for_super: false)
+ if for_super
+ Class.new(ClassWithSuperFoo)
+ else
+ Class.new(ClassWithFoo)
+ end
+ 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/freeze_spec.rb b/spec/ruby/core/module/freeze_spec.rb
index 07a0ee0837..fd76141431 100644
--- a/spec/ruby/core/module/freeze_spec.rb
+++ b/spec/ruby/core/module/freeze_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#freeze" do
it "needs to be reviewed for spec completeness"
diff --git a/spec/ruby/core/module/gt_spec.rb b/spec/ruby/core/module/gt_spec.rb
index 73a6c80b69..b8a73c9ff9 100644
--- a/spec/ruby/core/module/gt_spec.rb
+++ b/spec/ruby/core/module/gt_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#>" do
it "returns false if self is a subclass of or includes 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
- lambda { ModuleSpecs::Parent > mock('x') }.should raise_error(TypeError)
+ -> { ModuleSpecs::Parent > mock('x') }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/module/gte_spec.rb b/spec/ruby/core/module/gte_spec.rb
index 84758e2df6..18c60ba586 100644
--- a/spec/ruby/core/module/gte_spec.rb
+++ b/spec/ruby/core/module/gte_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#>=" do
it "returns true if self is a superclass of, the same as or included by given module" do
@@ -28,6 +28,6 @@ describe "Module#>=" do
end
it "raises a TypeError if the argument is not a class/module" do
- lambda { ModuleSpecs::Parent >= mock('x') }.should raise_error(TypeError)
+ -> { ModuleSpecs::Parent >= mock('x') }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/module/include_spec.rb b/spec/ruby/core/module/include_spec.rb
index e5d19fc820..210918b2e7 100644
--- a/spec/ruby/core/module/include_spec.rb
+++ b/spec/ruby/core/module/include_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#include" do
it "is a public method" do
@@ -40,11 +40,27 @@ describe "Module#include" do
end
it "raises a TypeError when the argument is not a Module" do
- lambda { ModuleSpecs::Basic.include(Class.new) }.should raise_error(TypeError)
+ -> { ModuleSpecs::Basic.include(Class.new) }.should raise_error(TypeError)
end
it "does not raise a TypeError when the argument is an instance of a subclass of Module" do
- lambda { ModuleSpecs::SubclassSpec.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError)
+ class ModuleSpecs::SubclassSpec::AClass
+ end
+ -> { ModuleSpecs::SubclassSpec::AClass.include(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError)
+ ensure
+ ModuleSpecs::SubclassSpec.send(:remove_const, :AClass)
+ end
+
+ it "raises a TypeError when the argument is a refinement" do
+ refinement = nil
+
+ Module.new do
+ refine String do
+ refinement = self
+ end
+ end
+
+ -> { ModuleSpecs::Basic.include(refinement) }.should raise_error(TypeError, "Cannot include refinement")
end
it "imports constants to modules and classes" do
@@ -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
@@ -158,31 +174,19 @@ describe "Module#include" do
end
it "detects cyclic includes" do
- lambda {
+ -> {
module ModuleSpecs::M
include ModuleSpecs::M
end
}.should raise_error(ArgumentError)
end
- ruby_version_is ''...'2.4' do
- it "accepts no-arguments" do
- lambda {
- Module.new do
- include
- end
- }.should_not raise_error
- end
- end
-
- ruby_version_is '2.4' do
- it "doesn't accept no-arguments" do
- lambda {
- Module.new do
- include
- end
- }.should raise_error(ArgumentError)
- end
+ it "doesn't accept no-arguments" do
+ -> {
+ Module.new do
+ include
+ end
+ }.should raise_error(ArgumentError)
end
it "returns the class it's included into" do
@@ -246,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
@@ -264,7 +622,7 @@ describe "Module#include?" do
end
it "raises a TypeError when no module was given" do
- lambda { ModuleSpecs::Child.include?("Test") }.should raise_error(TypeError)
- lambda { ModuleSpecs::Child.include?(ModuleSpecs::Parent) }.should raise_error(TypeError)
+ -> { ModuleSpecs::Child.include?("Test") }.should raise_error(TypeError)
+ -> { ModuleSpecs::Child.include?(ModuleSpecs::Parent) }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/module/included_modules_spec.rb b/spec/ruby/core/module/included_modules_spec.rb
index ff2dc434dd..ce94ed1285 100644
--- a/spec/ruby/core/module/included_modules_spec.rb
+++ b/spec/ruby/core/module/included_modules_spec.rb
@@ -1,12 +1,14 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+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::Basic.included_modules.should == []
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 39a08b6ed8..2fdd4325f2 100644
--- a/spec/ruby/core/module/included_spec.rb
+++ b/spec/ruby/core/module/included_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#included" do
it "is invoked when self is included in another module or class" do
@@ -39,6 +39,6 @@ describe "Module#included" do
it "works with super using a singleton class" do
ModuleSpecs::SingletonOnModuleCase::Bar.include ModuleSpecs::SingletonOnModuleCase::Foo
- ModuleSpecs::SingletonOnModuleCase::Bar.included_called?.should == true
+ ModuleSpecs::SingletonOnModuleCase::Bar.should.included_called?
end
end
diff --git a/spec/ruby/core/module/initialize_copy_spec.rb b/spec/ruby/core/module/initialize_copy_spec.rb
index 85f1bbeb17..7ae48f85a9 100644
--- a/spec/ruby/core/module/initialize_copy_spec.rb
+++ b/spec/ruby/core/module/initialize_copy_spec.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../../../spec_helper', __FILE__)
+require_relative '../../spec_helper'
describe "Module#initialize_copy" do
it "should retain singleton methods when duped" do
@@ -7,4 +7,12 @@ describe "Module#initialize_copy" do
end
mod.dup.methods(false).should == [:hello]
end
+
+ # jruby/jruby#5245, https://bugs.ruby-lang.org/issues/3461
+ it "should produce a duped module with inspectable class methods" do
+ mod = Module.new
+ def mod.hello
+ end
+ mod.dup.method(:hello).inspect.should =~ /Module.*hello/
+ end
end
diff --git a/spec/ruby/core/module/initialize_spec.rb b/spec/ruby/core/module/initialize_spec.rb
index b32e0f5d2a..99e41e4619 100644
--- a/spec/ruby/core/module/initialize_spec.rb
+++ b/spec/ruby/core/module/initialize_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#initialize" do
it "accepts a block" do
diff --git a/spec/ruby/core/module/instance_method_spec.rb b/spec/ruby/core/module/instance_method_spec.rb
index 82e397b4bc..182cdf5c54 100644
--- a/spec/ruby/core/module/instance_method_spec.rb
+++ b/spec/ruby/core/module/instance_method_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#instance_method" do
before :all 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
- lambda { Object.instance_method([]) }.should raise_error(TypeError)
- lambda { 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_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(0) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(nil) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ -> { Object.instance_method(mock('x')) }.should raise_error(TypeError, /is not a symbol nor a string/)
+ end
+
+ it "accepts String name argument" do
+ method = ModuleSpecs::InstanceMeth.instance_method(:foo)
+ method.should be_kind_of(UnboundMethod)
+ end
+
+ it "accepts Symbol name argument" do
+ method = ModuleSpecs::InstanceMeth.instance_method("foo")
+ method.should be_kind_of(UnboundMethod)
end
- it "raises a TypeError if the given name is not a string/symbol" do
- lambda { Object.instance_method(nil) }.should raise_error(TypeError)
- lambda { 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 be_kind_of(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_error(TypeError, /can't convert Object to String/)
end
it "raises a NameError if the method has been undefined" do
@@ -66,13 +87,13 @@ describe "Module#instance_method" do
child.send :undef_method, :foo
um = ModuleSpecs::InstanceMeth.instance_method(:foo)
um.should == @parent_um
- lambda do
+ -> do
child.instance_method(:foo)
end.should raise_error(NameError)
end
it "raises a NameError if the method does not exist" do
- lambda { Object.instance_method(:missing) }.should raise_error(NameError)
+ -> { Object.instance_method(:missing) }.should raise_error(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 289879cdbc..d2d38cdda2 100644
--- a/spec/ruby/core/module/instance_methods_spec.rb
+++ b/spec/ruby/core/module/instance_methods_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#instance_methods" do
it "does not return methods undefined in a superclass" do
diff --git a/spec/ruby/core/module/lt_spec.rb b/spec/ruby/core/module/lt_spec.rb
index ce0d25b5a2..d7771e07a8 100644
--- a/spec/ruby/core/module/lt_spec.rb
+++ b/spec/ruby/core/module/lt_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#<" do
it "returns true if self is a subclass of or includes 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
- lambda { ModuleSpecs::Parent < mock('x') }.should raise_error(TypeError)
+ -> { ModuleSpecs::Parent < mock('x') }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/module/lte_spec.rb b/spec/ruby/core/module/lte_spec.rb
index 8a699b4714..7a0e8496b8 100644
--- a/spec/ruby/core/module/lte_spec.rb
+++ b/spec/ruby/core/module/lte_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#<=" do
it "returns true if self is a subclass of, the same as or includes the given module" do
@@ -28,6 +28,6 @@ describe "Module#<=" do
end
it "raises a TypeError if the argument is not a class/module" do
- lambda { ModuleSpecs::Parent <= mock('x') }.should raise_error(TypeError)
+ -> { ModuleSpecs::Parent <= mock('x') }.should raise_error(TypeError)
end
end
diff --git a/spec/ruby/core/module/method_added_spec.rb b/spec/ruby/core/module/method_added_spec.rb
index 643291be17..ec92cddc1e 100644
--- a/spec/ruby/core/module/method_added_spec.rb
+++ b/spec/ruby/core/module/method_added_spec.rb
@@ -1,7 +1,11 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+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)
end
@@ -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
@@ -59,4 +59,88 @@ describe "Module#method_added" do
end
m.should_not have_method(: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
+ line = nil
+
+ Module.new do
+ def self.method_added(name)
+ location = caller_locations(1, 1)[0]
+ ScratchPad << location.lineno
+ end
+
+ line = __LINE__
+ def first
+ end
+
+ def second
+ end
+ end
+
+ ScratchPad.recorded.should == [line + 1, line + 4]
+ end
end
diff --git a/spec/ruby/core/module/method_defined_spec.rb b/spec/ruby/core/module/method_defined_spec.rb
index d6206fa57e..bc5b420e11 100644
--- a/spec/ruby/core/module/method_defined_spec.rb
+++ b/spec/ruby/core/module/method_defined_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#method_defined?" do
it "returns true if a public or private method with the given name is defined in self, self's ancestors or one of self's included modules" do
@@ -30,14 +30,14 @@ describe "Module#method_defined?" do
m.method_defined?(:module_specs_public_method_on_kernel).should be_false
end
- it "raises a TypeError when the given object is not a string/symbol/fixnum" do
+ it "raises a TypeError when the given object is not a string/symbol" do
c = Class.new
o = mock('123')
- lambda { c.method_defined?(o) }.should raise_error(TypeError)
+ -> { c.method_defined?(o) }.should raise_error(TypeError)
o.should_receive(:to_str).and_return(123)
- lambda { c.method_defined?(o) }.should raise_error(TypeError)
+ -> { c.method_defined?(o) }.should raise_error(TypeError)
end
it "converts the given name to a string using to_str" do
@@ -46,4 +46,53 @@ describe "Module#method_defined?" do
c.method_defined?(o).should == true
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
+ end
end
diff --git a/spec/ruby/core/module/method_removed_spec.rb b/spec/ruby/core/module/method_removed_spec.rb
index 4c1443cfaa..9b39eb77a6 100644
--- a/spec/ruby/core/module/method_removed_spec.rb
+++ b/spec/ruby/core/module/method_removed_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#method_removed" do
it "is a private instance method" do
diff --git a/spec/ruby/core/module/method_undefined_spec.rb b/spec/ruby/core/module/method_undefined_spec.rb
index 65001b9421..a94388fe0a 100644
--- a/spec/ruby/core/module/method_undefined_spec.rb
+++ b/spec/ruby/core/module/method_undefined_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#method_undefined" do
it "is a private instance method" do
diff --git a/spec/ruby/core/module/module_eval_spec.rb b/spec/ruby/core/module/module_eval_spec.rb
index 2bef0e9abf..e9e9fda28d 100644
--- a/spec/ruby/core/module/module_eval_spec.rb
+++ b/spec/ruby/core/module/module_eval_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/class_eval', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/class_eval'
describe "Module#module_eval" do
it_behaves_like :module_class_eval, :module_eval
diff --git a/spec/ruby/core/module/module_exec_spec.rb b/spec/ruby/core/module/module_exec_spec.rb
index 77d74a083b..47cdf7ef52 100644
--- a/spec/ruby/core/module/module_exec_spec.rb
+++ b/spec/ruby/core/module/module_exec_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/class_exec', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/class_exec'
describe "Module#module_exec" do
it_behaves_like :module_class_exec, :module_exec
diff --git a/spec/ruby/core/module/module_function_spec.rb b/spec/ruby/core/module/module_function_spec.rb
index 61509c8d03..51f647142e 100644
--- a/spec/ruby/core/module/module_function_spec.rb
+++ b/spec/ruby/core/module/module_function_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#module_function" do
it "is a private method" do
@@ -12,11 +12,11 @@ describe "Module#module_function" do
end
it "raises a TypeError if calling after rebinded to Class" do
- lambda {
+ -> {
Module.instance_method(:module_function).bind(Class.new).call
}.should raise_error(TypeError)
- lambda {
+ -> {
Module.instance_method(:module_function).bind(Class.new).call :foo
}.should raise_error(TypeError)
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
@@ -87,7 +85,7 @@ describe "Module#module_function with specific method names" do
o.respond_to?(:test).should == false
m.should have_private_instance_method(:test)
o.send(:test).should == "hello"
- lambda { o.test }.should raise_error(NoMethodError)
+ -> { o.test }.should raise_error(NoMethodError)
end
it "makes the new Module methods public" do
@@ -116,10 +114,10 @@ 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')
- lambda { Module.new { module_function(o) } }.should raise_error(TypeError)
+ -> { Module.new { module_function(o) } }.should raise_error(TypeError)
o.should_receive(:to_str).and_return(123)
- lambda { Module.new { module_function(o) } }.should raise_error(TypeError)
+ -> { Module.new { module_function(o) } }.should raise_error(TypeError)
end
it "can make accessible private methods" do # JRUBY-4214
@@ -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 0727278591..d3318e1645 100644
--- a/spec/ruby/core/module/name_spec.rb
+++ b/spec/ruby/core/module/name_spec.rb
@@ -1,21 +1,69 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/module', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/module'
describe "Module#name" do
it "is nil for an anonymous module" do
Module.new.name.should be_nil
end
- it "is nil when assigned to a constant in an anonymous module" 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 be_nil
+ m::N.name.should.end_with? '::N'
end
it "is not nil for a nested module created with the module keyword" do
m = Module.new
module m::N; end
- m::N.name.should =~ /#<Module:0x[0-9a-f]+>::N/
+ m::N.name.should =~ /\A#<Module:0x[0-9a-f]+>::N\z/
+ 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
+ end
+
+ it "changes when the module is reachable through a constant path" do
+ m = Module.new
+ module m::N; end
+ 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
+ module ModuleSpecs
+ module ModuleToRemove
+ end
+
+ mod = ModuleToRemove
+ remove_const(:ModuleToRemove)
+ mod.name.should == "ModuleSpecs::ModuleToRemove"
+ end
+ end
+
+ it "is set after it is removed from a constant under an anonymous module" do
+ m = Module.new
+ module m::Child; end
+ child = m::Child
+ m.send(:remove_const, :Child)
+ child.name.should =~ /\A#<Module:0x\h+>::Child\z/
end
it "is set when opened with the module keyword" do
@@ -26,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
@@ -38,6 +96,18 @@ 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
+ m = Module.new
+ module m::M; end
+ first_name = m::M.name.dup
+ module m::N; end
+ m::N::F = m::M
+ m::M.name.should == first_name
end
# http://bugs.ruby-lang.org/issues/6067
@@ -59,10 +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
+
+ # 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
+
+ # module with name
+ module TEST1
+ end
+
+ # anonymous module
+ TEST2 = Module.new
+ RUBY
+ end
+
+ it "is set in #const_added callback for a nested module when an outer module defined in the top-level scope" do
+ ScratchPad.record []
+
+ ModuleSpecs::NameSpecs::NamedModule = Module.new do
+ def self.const_added(name)
+ ScratchPad << const_get(name).name
+ end
+
+ module self::A
+ def self.const_added(name)
+ ScratchPad << const_get(name).name
+ end
+
+ module self::B
+ end
+ end
+ end
+
+ ScratchPad.recorded.should.one?(/#<Module.+>::A$/)
+ ScratchPad.recorded.should.one?(/#<Module.+>::A::B$/)
+ ModuleSpecs::NameSpecs.send :remove_const, :NamedModule
+ end
+
+ it "returns a frozen String" do
+ 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/nesting_spec.rb b/spec/ruby/core/module/nesting_spec.rb
index 0a86e5b8fc..d0611b3efe 100644
--- a/spec/ruby/core/module/nesting_spec.rb
+++ b/spec/ruby/core/module/nesting_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module::Nesting" do
diff --git a/spec/ruby/core/module/new_spec.rb b/spec/ruby/core/module/new_spec.rb
index 21d3f954fa..ec521360bd 100644
--- a/spec/ruby/core/module/new_spec.rb
+++ b/spec/ruby/core/module/new_spec.rb
@@ -1,11 +1,15 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module.new" do
it "creates a new anonymous Module" do
Module.new.is_a?(Module).should == true
end
+ it "creates a module without a name" do
+ Module.new.name.should be_nil
+ end
+
it "creates a new Module and passes it to the provided block" do
test_mod = nil
m = Module.new do |mod|
diff --git a/spec/ruby/core/module/prepend_features_spec.rb b/spec/ruby/core/module/prepend_features_spec.rb
index 6f341804d1..09c15c5c15 100644
--- a/spec/ruby/core/module/prepend_features_spec.rb
+++ b/spec/ruby/core/module/prepend_features_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#prepend_features" do
it "is a private method" do
@@ -23,23 +23,11 @@ describe "Module#prepend_features" do
end
it "raises an ArgumentError on a cyclic prepend" do
- lambda {
+ -> {
ModuleSpecs::CyclicPrepend.send(:prepend_features, ModuleSpecs::CyclicPrepend)
}.should raise_error(ArgumentError)
end
- 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
-
it "clears caches of the given module" do
parent = Class.new do
def bar; :bar; end
@@ -68,7 +56,7 @@ describe "Module#prepend_features" do
end
it "raises a TypeError if calling after rebinded to Class" do
- lambda {
+ -> {
Module.instance_method(:prepend_features).bind(Class.new).call Module.new
}.should raise_error(TypeError)
end
diff --git a/spec/ruby/core/module/prepend_spec.rb b/spec/ruby/core/module/prepend_spec.rb
index c0cce616a2..71e82c513e 100644
--- a/spec/ruby/core/module/prepend_spec.rb
+++ b/spec/ruby/core/module/prepend_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#prepend" do
it "is a public method" do
@@ -36,12 +36,435 @@ 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
- lambda { ModuleSpecs::Basic.prepend(Class.new) }.should raise_error(TypeError)
+ -> { ModuleSpecs::Basic.prepend(Class.new) }.should raise_error(TypeError)
end
it "does not raise a TypeError when the argument is an instance of a subclass of Module" do
- lambda { ModuleSpecs::SubclassSpec.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError)
+ class ModuleSpecs::SubclassSpec::AClass
+ end
+ -> { ModuleSpecs::SubclassSpec::AClass.prepend(ModuleSpecs::Subclass.new) }.should_not raise_error(TypeError)
+ ensure
+ ModuleSpecs::SubclassSpec.send(:remove_const, :AClass)
+ end
+
+ it "raises a TypeError when the argument is a refinement" do
+ refinement = nil
+
+ Module.new do
+ refine String do
+ refinement = self
+ end
+ end
+
+ -> { ModuleSpecs::Basic.prepend(refinement) }.should raise_error(TypeError, "Cannot prepend refinement")
end
it "imports constants" do
@@ -104,6 +527,18 @@ describe "Module#prepend" do
c.new.alias.should == :m
end
+ it "reports the class for the owner of an aliased method on the class" do
+ m = Module.new
+ c = Class.new { prepend(m); def meth; :c end; alias_method :alias, :meth }
+ c.instance_method(:alias).owner.should == c
+ end
+
+ it "reports the class for the owner of a method aliased from the prepended module" do
+ m = Module.new { def meth; :m end }
+ c = Class.new { prepend(m); alias_method :alias, :meth }
+ c.instance_method(:alias).owner.should == c
+ end
+
it "sees an instance of a prepended class as kind of the prepended module" do
m = Module.new
c = Class.new { prepend(m) }
@@ -116,16 +551,16 @@ describe "Module#prepend" do
c.dup.new.should be_kind_of(m)
end
- it "keeps the module in the chain when dupping an intermediate module" 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 == [m2dup,m1,m2]
+ 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,4].should == [m2dup,m1,m2,c2]
+ c2.ancestors[0,3].should == [m1,m2dup,c2]
c2.new.should be_kind_of(m1)
end
@@ -192,7 +627,7 @@ describe "Module#prepend" do
super << :class
end
end
- lambda { c.new.chain }.should raise_error(NoMethodError)
+ -> { c.new.chain }.should raise_error(NoMethodError)
end
it "calls prepended after prepend_features" do
@@ -211,32 +646,32 @@ 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
- lambda {
+ -> {
module ModuleSpecs::P
prepend ModuleSpecs::P
end
}.should raise_error(ArgumentError)
end
- ruby_version_is ''...'2.4' do
- it "accepts no-arguments" do
- lambda {
- Module.new do
- prepend
- end
- }.should_not raise_error
- end
- end
-
- ruby_version_is '2.4' do
- it "doesn't accept no-arguments" do
- lambda {
- Module.new do
- prepend
- end
- }.should raise_error(ArgumentError)
- end
+ it "doesn't accept no-arguments" do
+ -> {
+ Module.new do
+ prepend
+ end
+ }.should raise_error(ArgumentError)
end
it "returns the class it's included into" do
@@ -343,6 +778,33 @@ describe "Module#prepend" do
ary.should == [3, 2, 1]
end
+ it "does not prepend a second copy if the module already indirectly exists in the hierarchy" do
+ mod = Module.new do; end
+ submod = Module.new do; end
+ klass = Class.new do; end
+ klass.include(mod)
+ mod.prepend(submod)
+ klass.include(mod)
+
+ klass.ancestors.take(4).should == [klass, submod, mod, Object]
+ end
+
+ # https://bugs.ruby-lang.org/issues/17423
+ describe "when module already exists in ancestor chain" do
+ it "modifies the ancestor chain" do
+ m = Module.new do; end
+ a = Module.new do; end
+ b = Class.new do; end
+
+ b.include(a)
+ a.prepend(m)
+ b.ancestors.take(4).should == [b, m, a, Object]
+
+ b.prepend(m)
+ b.ancestors.take(5).should == [m, b, m, a, Object]
+ end
+ end
+
describe "called on a module" do
describe "included into a class"
it "does not obscure the module's methods from reflective access" do
diff --git a/spec/ruby/core/module/prepended_spec.rb b/spec/ruby/core/module/prepended_spec.rb
index ed8e473941..bd95d8fd05 100644
--- a/spec/ruby/core/module/prepended_spec.rb
+++ b/spec/ruby/core/module/prepended_spec.rb
@@ -1,6 +1,6 @@
# -*- encoding: us-ascii -*-
-require File.expand_path('../../../spec_helper', __FILE__)
+require_relative '../../spec_helper'
describe "Module#prepended" do
before :each do
diff --git a/spec/ruby/core/module/private_class_method_spec.rb b/spec/ruby/core/module/private_class_method_spec.rb
index ec10bcbf87..f899c71a57 100644
--- a/spec/ruby/core/module/private_class_method_spec.rb
+++ b/spec/ruby/core/module/private_class_method_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#private_class_method" do
before :each do
@@ -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
- lambda { ModuleSpecs::Parent.private_method_1 }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Parent.private_method_1 }.should raise_error(NoMethodError)
# Technically above we're testing the Singleton classes, class method(right?).
# Try a "real" class method set private.
- lambda { ModuleSpecs::Parent.private_method }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Parent.private_method }.should raise_error(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
- lambda { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError)
- lambda { ModuleSpecs::Child.private_method }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Child.private_method }.should raise_error(NoMethodError)
end
it "accepts more than one method at a time" do
@@ -44,12 +44,12 @@ describe "Module#private_class_method" do
ModuleSpecs::Child.private_class_method :private_method_1, :private_method_2
- lambda { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError)
- lambda { ModuleSpecs::Child.private_method_2 }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Child.private_method_1 }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Child.private_method_2 }.should raise_error(NoMethodError)
end
it "raises a NameError if class method doesn't exist" do
- lambda do
+ -> do
ModuleSpecs.private_class_method :no_method_here
end.should raise_error(NameError)
end
@@ -59,11 +59,11 @@ describe "Module#private_class_method" do
def self.foo() "foo" end
private_class_method :foo
end
- lambda { c.foo }.should raise_error(NoMethodError)
+ -> { c.foo }.should raise_error(NoMethodError)
end
it "raises a NameError when the given name is not a method" do
- lambda do
+ -> do
Class.new do
private_class_method :foo
end
@@ -71,11 +71,21 @@ describe "Module#private_class_method" do
end
it "raises a NameError when the given name is an instance method" do
- lambda do
+ -> do
Class.new do
def foo() "foo" end
private_class_method :foo
end
end.should raise_error(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_error(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 af0b1facdd..3a91b3c3cd 100644
--- a/spec/ruby/core/module/private_constant_spec.rb
+++ b/spec/ruby/core/module/private_constant_spec.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../../../spec_helper', __FILE__)
+require_relative '../../spec_helper'
describe "Module#private_constant" do
it "can only be passed constant names defined in the target (self) module" do
@@ -6,7 +6,7 @@ describe "Module#private_constant" do
cls1.const_set :Foo, true
cls2 = Class.new(cls1)
- lambda do
+ -> do
cls2.send :private_constant, :Foo
end.should raise_error(NameError)
end
@@ -16,7 +16,7 @@ describe "Module#private_constant" do
cls.const_set :Foo, true
cls.send :private_constant, "Foo"
- lambda { cls::Foo }.should raise_error(NameError)
+ -> { cls::Foo }.should raise_error(NameError)
end
it "accepts multiple names" do
@@ -26,7 +26,7 @@ describe "Module#private_constant" do
mod.send :private_constant, :Foo, :Bar
- lambda {mod::Foo}.should raise_error(NameError)
- lambda {mod::Bar}.should raise_error(NameError)
+ -> {mod::Foo}.should raise_error(NameError)
+ -> {mod::Bar}.should raise_error(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 f5c4c3a0f9..cce0f001bf 100644
--- a/spec/ruby/core/module/private_instance_methods_spec.rb
+++ b/spec/ruby/core/module/private_instance_methods_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../../../fixtures/reflection', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
# TODO: rewrite
describe "Module#private_instance_methods" do
diff --git a/spec/ruby/core/module/private_method_defined_spec.rb b/spec/ruby/core/module/private_method_defined_spec.rb
index b3e1814056..01fc8f8fb9 100644
--- a/spec/ruby/core/module/private_method_defined_spec.rb
+++ b/spec/ruby/core/module/private_method_defined_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#private_method_defined?" do
it "returns true if the named private method is defined by module or its ancestors" do
@@ -31,26 +31,26 @@ describe "Module#private_method_defined?" do
ModuleSpecs::CountsMixin.private_method_defined?(:private_3).should == true
end
- it "raises a TypeError if passed a Fixnum" do
- lambda do
+ it "raises a TypeError if passed an Integer" do
+ -> do
ModuleSpecs::CountsMixin.private_method_defined?(1)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed nil" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.private_method_defined?(nil)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed false" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.private_method_defined?(false)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed an object that does not defined #to_str" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.private_method_defined?(mock('x'))
end.should raise_error(TypeError)
end
@@ -59,7 +59,7 @@ describe "Module#private_method_defined?" do
sym = mock('symbol')
def sym.to_sym() :private_3 end
- lambda do
+ -> do
ModuleSpecs::CountsMixin.private_method_defined?(sym)
end.should raise_error(TypeError)
end
@@ -69,4 +69,52 @@ describe "Module#private_method_defined?" do
def str.to_str() 'private_3' end
ModuleSpecs::CountsMixin.private_method_defined?(str).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
+ end
end
diff --git a/spec/ruby/core/module/private_spec.rb b/spec/ruby/core/module/private_spec.rb
index 07b8f4c781..9e1a297eea 100644
--- a/spec/ruby/core/module/private_spec.rb
+++ b/spec/ruby/core/module/private_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/set_visibility', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_visibility'
describe "Module#private" do
it_behaves_like :set_visibility, :private
@@ -17,7 +17,7 @@ describe "Module#private" do
private :foo
end
- lambda { obj.foo }.should raise_error(NoMethodError)
+ -> { obj.foo }.should raise_error(NoMethodError)
end
it "makes a public Object instance method private in a new module" do
@@ -38,16 +38,18 @@ describe "Module#private" do
: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
- lambda do
+ -> do
Module.new.send(:private, :undefined)
end.should raise_error(NameError)
end
@@ -67,7 +69,7 @@ describe "Module#private" do
end
base.new.wrapped.should == 1
- lambda do
+ -> do
klass.new.wrapped
end.should raise_error(NameError)
end
diff --git a/spec/ruby/core/module/protected_instance_methods_spec.rb b/spec/ruby/core/module/protected_instance_methods_spec.rb
index d5c9e5cd33..78ce7e788f 100644
--- a/spec/ruby/core/module/protected_instance_methods_spec.rb
+++ b/spec/ruby/core/module/protected_instance_methods_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../../../fixtures/reflection', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
# TODO: rewrite
describe "Module#protected_instance_methods" do
diff --git a/spec/ruby/core/module/protected_method_defined_spec.rb b/spec/ruby/core/module/protected_method_defined_spec.rb
index af08efae81..31e24a16c1 100644
--- a/spec/ruby/core/module/protected_method_defined_spec.rb
+++ b/spec/ruby/core/module/protected_method_defined_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#protected_method_defined?" do
it "returns true if the named protected method is defined by module or its ancestors" do
@@ -31,26 +31,26 @@ describe "Module#protected_method_defined?" do
ModuleSpecs::CountsMixin.protected_method_defined?(:protected_3).should == true
end
- it "raises a TypeError if passed a Fixnum" do
- lambda do
+ it "raises a TypeError if passed an Integer" do
+ -> do
ModuleSpecs::CountsMixin.protected_method_defined?(1)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed nil" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.protected_method_defined?(nil)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed false" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.protected_method_defined?(false)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed an object that does not defined #to_str" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.protected_method_defined?(mock('x'))
end.should raise_error(TypeError)
end
@@ -59,7 +59,7 @@ describe "Module#protected_method_defined?" do
sym = mock('symbol')
def sym.to_sym() :protected_3 end
- lambda do
+ -> do
ModuleSpecs::CountsMixin.protected_method_defined?(sym)
end.should raise_error(TypeError)
end
@@ -69,4 +69,52 @@ describe "Module#protected_method_defined?" do
str.should_receive(:to_str).and_return("protected_3")
ModuleSpecs::CountsMixin.protected_method_defined?(str).should == true
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
+ end
end
diff --git a/spec/ruby/core/module/protected_spec.rb b/spec/ruby/core/module/protected_spec.rb
index 7e77138c12..9e37223e18 100644
--- a/spec/ruby/core/module/protected_spec.rb
+++ b/spec/ruby/core/module/protected_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/set_visibility', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_visibility'
describe "Module#protected" do
before :each do
@@ -18,7 +18,7 @@ describe "Module#protected" do
protected :protected_method_1
end
- lambda { ModuleSpecs::Parent.protected_method_1 }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Parent.protected_method_1 }.should raise_error(NoMethodError)
end
it "makes a public Object instance method protected in a new module" do
@@ -39,18 +39,19 @@ describe "Module#protected" do
: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
- lambda do
+ -> do
Module.new.send(:protected, :undefined)
end.should raise_error(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 2c909b1223..71b20acda5 100644
--- a/spec/ruby/core/module/public_class_method_spec.rb
+++ b/spec/ruby/core/module/public_class_method_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#public_class_method" do
before :each do
@@ -18,7 +18,7 @@ describe "Module#public_class_method" do
end
it "makes an existing class method public" do
- lambda { ModuleSpecs::Parent.public_method_1 }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Parent.public_method_1 }.should raise_error(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
- lambda { ModuleSpecs::Child.public_method_1 }.should raise_error(NoMethodError)
+ -> { ModuleSpecs::Child.public_method_1 }.should raise_error(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
- lambda { ModuleSpecs::Parent.public_method_1 }.should raise_error(NameError)
- lambda { ModuleSpecs::Parent.public_method_2 }.should raise_error(NameError)
+ -> { ModuleSpecs::Parent.public_method_1 }.should raise_error(NameError)
+ -> { ModuleSpecs::Parent.public_method_2 }.should raise_error(NameError)
ModuleSpecs::Child.public_class_method :public_method_1, :public_method_2
@@ -47,7 +47,7 @@ describe "Module#public_class_method" do
end
it "raises a NameError if class method doesn't exist" do
- lambda do
+ -> do
ModuleSpecs.public_class_method :no_method_here
end.should raise_error(NameError)
end
@@ -62,7 +62,7 @@ describe "Module#public_class_method" do
end
it "raises a NameError when the given name is not a method" do
- lambda do
+ -> do
Class.new do
public_class_method :foo
end
@@ -70,11 +70,25 @@ describe "Module#public_class_method" do
end
it "raises a NameError when the given name is an instance method" do
- lambda do
+ -> do
Class.new do
def foo() "foo" end
public_class_method :foo
end
end.should raise_error(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 cbf51a6df6..e624d45fd2 100644
--- a/spec/ruby/core/module/public_constant_spec.rb
+++ b/spec/ruby/core/module/public_constant_spec.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../../../spec_helper', __FILE__)
+require_relative '../../spec_helper'
describe "Module#public_constant" do
it "can only be passed constant names defined in the target (self) module" do
@@ -6,7 +6,7 @@ describe "Module#public_constant" do
cls1.const_set :Foo, true
cls2 = Class.new(cls1)
- lambda do
+ -> do
cls2.send :public_constant, :Foo
end.should raise_error(NameError)
end
diff --git a/spec/ruby/core/module/public_instance_method_spec.rb b/spec/ruby/core/module/public_instance_method_spec.rb
index c1e94c5fed..ba19ad0404 100644
--- a/spec/ruby/core/module/public_instance_method_spec.rb
+++ b/spec/ruby/core/module/public_instance_method_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#public_instance_method" do
it "is a public method" do
@@ -28,29 +28,29 @@ describe "Module#public_instance_method" do
end
it "raises a TypeError when given a name is not Symbol or String" do
- lambda { Module.new.public_instance_method(nil) }.should raise_error(TypeError)
+ -> { Module.new.public_instance_method(nil) }.should raise_error(TypeError)
end
it "raises a NameError when given a protected method name" do
- lambda do
+ -> do
ModuleSpecs::Basic.public_instance_method(:protected_module)
end.should raise_error(NameError)
end
it "raises a NameError if the method is private" do
- lambda do
+ -> do
ModuleSpecs::Basic.public_instance_method(:private_module)
end.should raise_error(NameError)
end
it "raises a NameError if the method has been undefined" do
- lambda do
+ -> do
ModuleSpecs::Parent.public_instance_method(:undefed_method)
end.should raise_error(NameError)
end
it "raises a NameError if the method does not exist" do
- lambda do
+ -> do
Module.new.public_instance_method(:missing)
end.should raise_error(NameError)
end
diff --git a/spec/ruby/core/module/public_instance_methods_spec.rb b/spec/ruby/core/module/public_instance_methods_spec.rb
index 14453109c3..ae7d9b5ffb 100644
--- a/spec/ruby/core/module/public_instance_methods_spec.rb
+++ b/spec/ruby/core/module/public_instance_methods_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../../../fixtures/reflection', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative '../../fixtures/reflection'
# TODO: rewrite
diff --git a/spec/ruby/core/module/public_method_defined_spec.rb b/spec/ruby/core/module/public_method_defined_spec.rb
index 329778ac15..5c9bdf1ccc 100644
--- a/spec/ruby/core/module/public_method_defined_spec.rb
+++ b/spec/ruby/core/module/public_method_defined_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#public_method_defined?" do
it "returns true if the named public method is defined by module or its ancestors" do
@@ -31,26 +31,26 @@ describe "Module#public_method_defined?" do
ModuleSpecs::CountsMixin.public_method_defined?(:public_3).should == true
end
- it "raises a TypeError if passed a Fixnum" do
- lambda do
+ it "raises a TypeError if passed an Integer" do
+ -> do
ModuleSpecs::CountsMixin.public_method_defined?(1)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed nil" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.public_method_defined?(nil)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed false" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.public_method_defined?(false)
end.should raise_error(TypeError)
end
it "raises a TypeError if passed an object that does not defined #to_str" do
- lambda do
+ -> do
ModuleSpecs::CountsMixin.public_method_defined?(mock('x'))
end.should raise_error(TypeError)
end
@@ -59,7 +59,7 @@ describe "Module#public_method_defined?" do
sym = mock('symbol')
def sym.to_sym() :public_3 end
- lambda do
+ -> do
ModuleSpecs::CountsMixin.public_method_defined?(sym)
end.should raise_error(TypeError)
end
diff --git a/spec/ruby/core/module/public_spec.rb b/spec/ruby/core/module/public_spec.rb
index 8507689752..ce31eb5d0e 100644
--- a/spec/ruby/core/module/public_spec.rb
+++ b/spec/ruby/core/module/public_spec.rb
@@ -1,6 +1,6 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
-require File.expand_path('../shared/set_visibility', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
+require_relative 'shared/set_visibility'
describe "Module#public" do
it_behaves_like :set_visibility, :public
@@ -27,17 +27,18 @@ describe "Module#public" do
: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
- lambda do
+ -> do
Module.new.send(:public, :undefined)
end.should raise_error(NameError)
end
diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb
index ca7db0c2b6..d219b98825 100644
--- a/spec/ruby/core/module/refine_spec.rb
+++ b/spec/ruby/core/module/refine_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/refine', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/refine'
describe "Module#refine" do
it "runs its block in an anonymous module" do
@@ -59,7 +59,7 @@ describe "Module#refine" do
end
it "raises ArgumentError if not passed an argument" do
- lambda do
+ -> do
Module.new do
refine {}
end
@@ -67,42 +67,53 @@ describe "Module#refine" do
end
it "raises TypeError if not passed a class" do
- lambda do
+ -> do
Module.new do
refine("foo") {}
end
- end.should raise_error(TypeError)
+ end.should raise_error(TypeError, "wrong argument type String (expected Class or Module)")
end
- ruby_version_is "" ... "2.4" do
- it "raises TypeError if passed a module" do
- lambda do
- Module.new do
- refine(Enumerable) {}
+ it "accepts a module as argument" do
+ inner_self = nil
+ Module.new do
+ refine(Enumerable) do
+ def blah
end
- end.should raise_error(TypeError)
+ inner_self = self
+ end
end
+
+ inner_self.public_instance_methods.should include(:blah)
end
- quarantine! do # https://bugs.ruby-lang.org/issues/14070
- ruby_version_is "2.4" do
- it "accepts a module as argument" do
- inner_self = nil
- Module.new do
- refine(Enumerable) do
- def blah
- end
- inner_self = self
- end
+ it "applies refinements to the module" do
+ refinement = Module.new do
+ refine(Enumerable) do
+ def foo?
+ self.any? ? "yes" : "no"
end
+ end
+ end
+
+ foo = Class.new do
+ using refinement
- inner_self.public_instance_methods.should include(:blah)
+ def initialize(items)
+ @items = items
+ end
+
+ def result
+ @items.foo?
end
end
+
+ foo.new([]).result.should == "no"
+ foo.new([1]).result.should == "yes"
end
it "raises ArgumentError if not given a block" do
- lambda do
+ -> do
Module.new do
refine String
end
@@ -123,7 +134,7 @@ describe "Module#refine" do
it "doesn't apply refinements outside the refine block" do
Module.new do
refine(String) {def foo; "foo"; end}
- -> () {
+ -> {
"hello".foo
}.should raise_error(NoMethodError)
end
@@ -134,7 +145,7 @@ describe "Module#refine" do
refine(String) {def foo; 'foo'; end}
end
- lambda {"hello".foo}.should raise_error(NoMethodError)
+ -> {"hello".foo}.should raise_error(NoMethodError)
end
# When defining multiple refinements in the same module,
@@ -191,7 +202,7 @@ describe "Module#refine" do
result = nil
- -> () {
+ -> {
Module.new do
using refinery_integer
using refinery_array
@@ -210,8 +221,10 @@ describe "Module#refine" do
# * The included modules of C
describe "method lookup" do
it "looks in the object singleton class first" do
+ refined_class = ModuleSpecs.build_refined_class
+
refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
+ refine refined_class do
def foo; "foo from refinement"; end
end
end
@@ -220,7 +233,7 @@ describe "Module#refine" do
Module.new do
using refinement
- obj = ModuleSpecs::ClassWithFoo.new
+ obj = refined_class.new
class << obj
def foo; "foo from singleton class"; end
end
@@ -230,68 +243,48 @@ describe "Module#refine" do
result.should == "foo from singleton class"
end
- it "looks in prepended modules from the refinement first" do
- refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
- include ModuleSpecs::IncludedModule
- prepend ModuleSpecs::PrependedModule
-
- def foo; "foo from refinement"; end
+ it "looks in later included modules of the refined module first" do
+ a = Module.new do
+ def foo
+ "foo from A"
end
end
- result = nil
- Module.new do
- using refinement
- result = ModuleSpecs::ClassWithFoo.new.foo
- end
-
- result.should == "foo from prepended module"
- end
-
- it "looks in refinement then" do
- refinement = Module.new do
- refine(ModuleSpecs::ClassWithFoo) do
- include ModuleSpecs::IncludedModule
-
- def foo; "foo from refinement"; end
+ include_me_later = Module.new do
+ def foo
+ "foo from IncludeMeLater"
end
end
- result = nil
- Module.new do
- using refinement
- result = ModuleSpecs::ClassWithFoo.new.foo
+ c = Class.new do
+ include a
end
- result.should == "foo from refinement"
- end
-
- it "looks in included modules from the refinement then" do
refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
- include ModuleSpecs::IncludedModule
- end
- end
+ refine c do; end
+ end
result = nil
Module.new do
using refinement
- result = ModuleSpecs::ClassWithFoo.new.foo
+ c.include include_me_later
+ result = c.new.foo
end
- result.should == "foo from included module"
+ result.should == "foo from IncludeMeLater"
end
it "looks in the class then" do
+ refined_class = ModuleSpecs.build_refined_class
+
refinement = Module.new do
- refine(ModuleSpecs::ClassWithFoo) { }
+ refine(refined_class) { }
end
result = nil
Module.new do
using refinement
- result = ModuleSpecs::ClassWithFoo.new.foo
+ result = refined_class.new.foo
end
result.should == "foo"
@@ -301,12 +294,14 @@ describe "Module#refine" do
# methods in a subclass have priority over refinements in a superclass
it "does not override methods in subclasses" do
- subclass = Class.new(ModuleSpecs::ClassWithFoo) do
+ refined_class = ModuleSpecs.build_refined_class
+
+ subclass = Class.new(refined_class) do
def foo; "foo from subclass"; end
end
refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
+ refine refined_class do
def foo; "foo from refinement"; end
end
end
@@ -321,148 +316,94 @@ describe "Module#refine" do
end
context "for methods accessed indirectly" do
- ruby_version_is "" ... "2.4" do
- it "is not honored by Kernel#send" do
- refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
- def foo; "foo from refinement"; end
- end
- end
+ it "is honored by Kernel#send" do
+ refined_class = ModuleSpecs.build_refined_class
- result = nil
- Module.new do
- using refinement
- result = ModuleSpecs::ClassWithFoo.new.send :foo
+ refinement = Module.new do
+ refine refined_class do
+ def foo; "foo from refinement"; end
end
-
- result.should == "foo"
end
- it "is not honored by BasicObject#__send__" do
- refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
- def foo; "foo from refinement"; end
- end
- end
-
- result = nil
- Module.new do
- using refinement
- result = ModuleSpecs::ClassWithFoo.new.__send__ :foo
- end
-
- result.should == "foo"
+ result = nil
+ Module.new do
+ using refinement
+ result = refined_class.new.send :foo
end
- it "is not honored by Symbol#to_proc" do
- refinement = Module.new do
- refine Integer do
- def to_s
- "(#{super})"
- end
- end
- end
-
- result = nil
- Module.new do
- using refinement
- result = [1, 2, 3].map(&:to_s)
- end
-
- result.should == ["1", "2", "3"]
- end
+ result.should == "foo from refinement"
end
- ruby_version_is "2.4" do
- it "is honored by Kernel#send" do
- refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
- def foo; "foo from refinement"; end
- end
- end
+ it "is honored by BasicObject#__send__" do
+ refined_class = ModuleSpecs.build_refined_class
- result = nil
- Module.new do
- using refinement
- result = ModuleSpecs::ClassWithFoo.new.send :foo
+ refinement = Module.new do
+ refine refined_class do
+ def foo; "foo from refinement"; end
end
-
- result.should == "foo from refinement"
end
- it "is honored by BasicObject#__send__" do
- refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
- def foo; "foo from refinement"; end
- end
- end
-
- result = nil
- Module.new do
- using refinement
- result = ModuleSpecs::ClassWithFoo.new.__send__ :foo
- end
-
- result.should == "foo from refinement"
+ result = nil
+ Module.new do
+ using refinement
+ result = refined_class.new.__send__ :foo
end
- it "is honored by Symbol#to_proc" do
- refinement = Module.new do
- refine Integer do
- def to_s
- "(#{super})"
- end
- end
- end
+ result.should == "foo from refinement"
+ end
- result = nil
- Module.new do
- using refinement
- result = [1, 2, 3].map(&:to_s)
+ it "is honored by Symbol#to_proc" do
+ refinement = Module.new do
+ refine Integer do
+ def to_s
+ "(#{super})"
+ end
end
+ end
- result.should == ["(1)", "(2)", "(3)"]
+ result = nil
+ Module.new do
+ using refinement
+ result = [1, 2, 3].map(&:to_s)
end
+
+ result.should == ["(1)", "(2)", "(3)"]
end
- ruby_version_is "" ... "2.5" do
- it "is not honored by string interpolation" do
- refinement = Module.new do
- refine Integer do
- def to_s
- "foo"
- 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 = "#{1}"
+ refinement = Module.new do
+ refine refined_class do
+ def foo; "foo from refinement"; end
end
+ end
- result.should == "1"
+ result = nil
+ Module.new do
+ using refinement
+ result = refined_class.new.public_send :foo
end
+
+ result.should == "foo from refinement"
end
- ruby_version_is "2.5" do
- it "is honored by string interpolation" do
- refinement = Module.new do
- refine Integer do
- def to_s
- "foo"
- end
+ it "is honored by string interpolation" do
+ refinement = Module.new do
+ refine Integer do
+ def to_s
+ "foo"
end
end
+ end
- result = nil
- Module.new do
- using refinement
- result = "#{1}"
- end
-
- result.should == "foo"
+ result = nil
+ Module.new do
+ using refinement
+ result = "#{1}"
end
+
+ result.should == "foo"
end
it "is honored by Kernel#binding" do
@@ -490,7 +431,7 @@ describe "Module#refine" do
result.should == "hello from refinement"
end
- it "is not honored by Kernel#method" do
+ it "is honored by Kernel#method" do
klass = Class.new
refinement = Module.new do
refine klass do
@@ -498,15 +439,33 @@ describe "Module#refine" do
end
end
- -> {
- Module.new do
- using refinement
- klass.new.method(:foo)
+ result = nil
+ Module.new do
+ using refinement
+ result = klass.new.method(:foo).class
+ end
+
+ result.should == Method
+ end
+
+ it "is honored by Kernel#public_method" do
+ klass = Class.new
+ refinement = Module.new do
+ refine klass do
+ def foo; end
end
- }.should raise_error(NameError, /undefined method `foo'/)
+ end
+
+ result = nil
+ Module.new do
+ using refinement
+ result = klass.new.public_method(:foo).class
+ end
+
+ result.should == Method
end
- it "is not honored by Kernel#respond_to?" do
+ it "is honored by Kernel#instance_method" do
klass = Class.new
refinement = Module.new do
refine klass do
@@ -517,21 +476,34 @@ describe "Module#refine" do
result = nil
Module.new do
using refinement
- result = klass.new.respond_to?(:foo)
+ result = klass.instance_method(:foo).class
end
- result.should == false
+ result.should == UnboundMethod
end
- end
- context "when super is called in a refinement" do
- it "looks in the included to refinery module" do
+ it "is honored by Kernel#respond_to?" do
+ klass = Class.new
refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
- include ModuleSpecs::IncludedModule
+ refine klass do
+ def foo; end
+ end
+ end
- def foo
- super
+ result = nil
+ Module.new do
+ using refinement
+ result = klass.new.respond_to?(:foo)
+ end
+
+ result.should == true
+ end
+
+ it "is honored by &" do
+ refinement = Module.new do
+ refine String do
+ def to_proc(*args)
+ -> * { 'foo' }
end
end
end
@@ -539,15 +511,19 @@ describe "Module#refine" do
result = nil
Module.new do
using refinement
- result = ModuleSpecs::ClassWithFoo.new.foo
+ result = ["hola"].map(&"upcase")
end
- result.should == "foo from included module"
+ result.should == ['foo']
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 ModuleSpecs::ClassWithFoo do
+ refine refined_class do
def foo
super
end
@@ -557,7 +533,7 @@ describe "Module#refine" do
result = nil
Module.new do
using refinement
- result = ModuleSpecs::ClassWithFoo.new.foo
+ result = refined_class.new.foo
end
result.should == "foo"
@@ -566,19 +542,21 @@ describe "Module#refine" do
# super in a method of a refinement invokes the method in the refined
# class even if there is another refinement which has been activated
# in the same context.
- it "looks in the refined class even if there is another active refinement" do
+ it "looks in the refined class first if called from refined method" do
+ refined_class = ModuleSpecs.build_refined_class(for_super: true)
+
refinement = Module.new do
- refine ModuleSpecs::ClassWithFoo do
+ refine refined_class do
def foo
- "foo from refinement"
+ [:R1]
end
end
end
refinement_with_super = Module.new do
- refine ModuleSpecs::ClassWithFoo do
+ refine refined_class do
def foo
- super
+ [:R2] + super
end
end
end
@@ -587,11 +565,75 @@ describe "Module#refine" do
Module.new do
using refinement
using refinement_with_super
- result = ModuleSpecs::ClassWithFoo.new.foo
+ result = refined_class.new.foo
end
- result.should == "foo"
+ result.should == [:R2, :C]
end
+
+ it "looks only in the refined class even if there is another active refinement" do
+ refined_class = ModuleSpecs.build_refined_class(for_super: true)
+
+ refinement = Module.new do
+ refine refined_class do
+ def bar
+ "you cannot see me from super because I belong to another active R"
+ end
+ end
+ end
+
+ refinement_with_super = Module.new do
+ refine refined_class do
+ def bar
+ super
+ end
+ end
+ end
+
+
+ Module.new do
+ using refinement
+ using refinement_with_super
+ -> {
+ refined_class.new.bar
+ }.should raise_error(NoMethodError)
+ end
+ end
+ end
+
+ it 'and alias aliases a method within a refinement module, but not outside it' do
+ Module.new do
+ using Module.new {
+ refine Array do
+ alias :orig_count :count
+ end
+ }
+ [1,2].orig_count.should == 2
+ end
+ -> { [1,2].orig_count }.should raise_error(NoMethodError)
+ end
+
+ it 'and alias_method aliases a method within a refinement module, but not outside it' do
+ Module.new do
+ using Module.new {
+ refine Array do
+ alias_method :orig_count, :count
+ end
+ }
+ [1,2].orig_count.should == 2
+ end
+ -> { [1,2].orig_count }.should raise_error(NoMethodError)
+ end
+
+ it "and instance_methods returns a list of methods including those of the refined module" do
+ methods = Array.instance_methods
+ methods_2 = []
+ Module.new do
+ refine Array do
+ methods_2 = instance_methods
+ end
+ end
+ methods.should == methods_2
end
# Refinements are inherited by module inclusion.
@@ -654,4 +696,19 @@ describe "Module#refine" do
result.should == "hello from refinement"
end
end
+
+ it 'does not list methods defined only in refinement' do
+ refine_object = Module.new do
+ refine Object do
+ def refinement_only_method
+ end
+ end
+ end
+ spec = self
+ klass = Class.new { instance_methods.should_not spec.send(:include, :refinement_only_method) }
+ instance = klass.new
+ 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)
+ 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 adf2c1d1f8..ab9514adf6 100644
--- a/spec/ruby/core/module/remove_class_variable_spec.rb
+++ b/spec/ruby/core/module/remove_class_variable_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#remove_class_variable" do
it "removes class variable" do
@@ -23,19 +23,19 @@ describe "Module#remove_class_variable" do
it "raises a NameError when removing class variable declared in included module" do
c = ModuleSpecs::RemoveClassVariable.new { include ModuleSpecs::MVars.dup }
- lambda { c.send(:remove_class_variable, :@@mvar) }.should raise_error(NameError)
+ -> { c.send(:remove_class_variable, :@@mvar) }.should raise_error(NameError)
end
it "raises a NameError when passed a symbol with one leading @" do
- lambda { ModuleSpecs::MVars.send(:remove_class_variable, :@mvar) }.should raise_error(NameError)
+ -> { ModuleSpecs::MVars.send(:remove_class_variable, :@mvar) }.should raise_error(NameError)
end
it "raises a NameError when passed a symbol with no leading @" do
- lambda { ModuleSpecs::MVars.send(:remove_class_variable, :mvar) }.should raise_error(NameError)
+ -> { ModuleSpecs::MVars.send(:remove_class_variable, :mvar) }.should raise_error(NameError)
end
it "raises a NameError when an uninitialized class variable is given" do
- lambda { ModuleSpecs::MVars.send(:remove_class_variable, :@@nonexisting_class_variable) }.should raise_error(NameError)
+ -> { ModuleSpecs::MVars.send(:remove_class_variable, :@@nonexisting_class_variable) }.should raise_error(NameError)
end
it "is public" do
diff --git a/spec/ruby/core/module/remove_const_spec.rb b/spec/ruby/core/module/remove_const_spec.rb
index 0c1b87598e..35a9d65105 100644
--- a/spec/ruby/core/module/remove_const_spec.rb
+++ b/spec/ruby/core/module/remove_const_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../../../fixtures/constants', __FILE__)
+require_relative '../../spec_helper'
+require_relative '../../fixtures/constants'
describe "Module#remove_const" do
it "removes the constant specified by a String or Symbol from the receiver's constant table" do
@@ -7,13 +7,13 @@ describe "Module#remove_const" do
ConstantSpecs::ModuleM::CS_CONST252.should == :const252
ConstantSpecs::ModuleM.send :remove_const, :CS_CONST252
- lambda { ConstantSpecs::ModuleM::CS_CONST252 }.should raise_error(NameError)
+ -> { ConstantSpecs::ModuleM::CS_CONST252 }.should raise_error(NameError)
ConstantSpecs::ModuleM::CS_CONST253 = :const253
ConstantSpecs::ModuleM::CS_CONST253.should == :const253
ConstantSpecs::ModuleM.send :remove_const, "CS_CONST253"
- lambda { ConstantSpecs::ModuleM::CS_CONST253 }.should raise_error(NameError)
+ -> { ConstantSpecs::ModuleM::CS_CONST253 }.should raise_error(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)
- lambda { ConstantSpecs.send(:remove_const, :Nonexistent) }.should raise_error(NameError)
+ -> { ConstantSpecs.send(:remove_const, :Nonexistent) }.should raise_error(NameError)
end
it "raises a NameError and does not call #const_missing if the constant is not defined directly in the module" do
@@ -32,7 +32,7 @@ describe "Module#remove_const" do
ConstantSpecs::ContainerA::CS_CONST255.should == :const255
ConstantSpecs::ContainerA.should_not_receive(:const_missing)
- lambda do
+ -> do
ConstantSpecs::ContainerA.send :remove_const, :CS_CONST255
end.should raise_error(NameError)
ensure
@@ -41,21 +41,21 @@ describe "Module#remove_const" do
end
it "raises a NameError if the name does not start with a capital letter" do
- lambda { ConstantSpecs.send :remove_const, "name" }.should raise_error(NameError)
+ -> { ConstantSpecs.send :remove_const, "name" }.should raise_error(NameError)
end
it "raises a NameError if the name starts with a non-alphabetic character" do
- lambda { ConstantSpecs.send :remove_const, "__CONSTX__" }.should raise_error(NameError)
- lambda { ConstantSpecs.send :remove_const, "@Name" }.should raise_error(NameError)
- lambda { ConstantSpecs.send :remove_const, "!Name" }.should raise_error(NameError)
- lambda { ConstantSpecs.send :remove_const, "::Name" }.should raise_error(NameError)
+ -> { 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)
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"
- lambda { ConstantSpecs.send :remove_const, "Name=" }.should raise_error(NameError)
- lambda { 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)
end
it "calls #to_str to convert the given name to a String" do
@@ -67,10 +67,10 @@ describe "Module#remove_const" do
it "raises a TypeError if conversion to a String by calling #to_str fails" do
name = mock('123')
- lambda { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError)
+ -> { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError)
name.should_receive(:to_str).and_return(123)
- lambda { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError)
+ -> { ConstantSpecs.send :remove_const, name }.should raise_error(TypeError)
end
it "is a private method" do
@@ -81,4 +81,27 @@ describe "Module#remove_const" do
ConstantSpecs.autoload :AutoloadedConstant, 'a_file'
ConstantSpecs.send(:remove_const, :AutoloadedConstant).should be_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 d82e0c65ca..94b255df62 100644
--- a/spec/ruby/core/module/remove_method_spec.rb
+++ b/spec/ruby/core/module/remove_method_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
module ModuleSpecs
class Parent
@@ -20,15 +20,8 @@ describe "Module#remove_method" do
@module = Module.new { def method_to_remove; end }
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- Module.should have_private_instance_method(:remove_method, false)
- end
- end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:remove_method, false)
- end
+ it "is a public method" do
+ Module.should have_public_instance_method(:remove_method, false)
end
it "removes the method from a class" do
@@ -50,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
@@ -77,19 +92,19 @@ describe "Module#remove_method" do
end
it "raises a NameError when attempting to remove method further up the inheritance tree" do
- lambda {
- class Third < ModuleSpecs::Second
+ Class.new(ModuleSpecs::Second) do
+ -> {
remove_method :method_to_remove
- end
- }.should raise_error(NameError)
+ }.should raise_error(NameError)
+ end
end
it "raises a NameError when attempting to remove a missing method" do
- lambda {
- class Third < ModuleSpecs::Second
+ Class.new(ModuleSpecs::Second) do
+ -> {
remove_method :blah
- end
- }.should raise_error(NameError)
+ }.should raise_error(NameError)
+ end
end
describe "on frozen instance" do
@@ -97,16 +112,16 @@ describe "Module#remove_method" do
@frozen = @module.dup.freeze
end
- it "raises a RuntimeError when passed a name" do
- lambda { @frozen.send :remove_method, :method_to_remove }.should raise_error(RuntimeError)
+ it "raises a FrozenError when passed a name" do
+ -> { @frozen.send :remove_method, :method_to_remove }.should raise_error(FrozenError)
end
- it "raises a RuntimeError when passed a missing name" do
- lambda { @frozen.send :remove_method, :not_exist }.should raise_error(RuntimeError)
+ it "raises a FrozenError when passed a missing name" do
+ -> { @frozen.send :remove_method, :not_exist }.should raise_error(FrozenError)
end
it "raises a TypeError when passed a not name" do
- lambda { @frozen.send :remove_method, Object.new }.should raise_error(TypeError)
+ -> { @frozen.send :remove_method, Object.new }.should raise_error(TypeError)
end
it "does not raise exceptions when no arguments given" do
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..652f9f7083
--- /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_error(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_error(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..46605ed675
--- /dev/null
+++ b/spec/ruby/core/module/set_temporary_name_spec.rb
@@ -0,0 +1,147 @@
+require_relative '../../spec_helper'
+require_relative 'fixtures/set_temporary_name'
+
+ruby_version_is "3.3" do
+ describe "Module#set_temporary_name" do
+ it "can assign a temporary name" do
+ m = Module.new
+ m.name.should be_nil
+
+ m.set_temporary_name("fake_name")
+ m.name.should == "fake_name"
+
+ m.set_temporary_name(nil)
+ m.name.should be_nil
+ end
+
+ it "returns self" do
+ m = Module.new
+ m.set_temporary_name("fake_name").should.equal? m
+ end
+
+ it "can assign a temporary name which is not a valid constant path" do
+ m = Module.new
+
+ m.set_temporary_name("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_error(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_error(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_error(ArgumentError, "the temporary name must not be a constant path to avoid confusion")
+ -> { m.set_temporary_name("::A") }.should raise_error(ArgumentError, "the temporary name must not be a constant path to avoid confusion")
+ -> { m.set_temporary_name("::A::B") }.should raise_error(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_error(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 be_nil
+ end
+
+ it "discards a temporary name when an outer anonymous module gets a permanent name" do
+ m = Module.new
+ module m::N; end
+
+ m::N.set_temporary_name("fake_name")
+ m::N.name.should == "fake_name"
+
+ ModuleSpecs::SetTemporaryNameSpec::M = m
+ m::N.name.should == "ModuleSpecs::SetTemporaryNameSpec::M::N"
+ ModuleSpecs::SetTemporaryNameSpec.send :remove_const, :M
+ end
+
+ it "can update the name when assigned to a constant" do
+ m = Module.new
+ m::N = Module.new
+ 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
+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 6bb9668fee..b1d5cb3814 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
- lambda {42.foo}.should raise_error(NoMethodError)
+ -> {42.foo}.should raise_error(NoMethodError)
end
it "resolves constants in the caller scope" do
@@ -52,43 +52,58 @@ describe :module_class_eval, shared: true do
ModuleSpecs.send(@method, "[__FILE__, __LINE__]", "test", 102).should == ["test", 102]
end
+ ruby_version_is "3.3" do
+ it "uses the caller location as default filename" do
+ ModuleSpecs.send(@method, "[__FILE__, __LINE__]").should == ["(eval at #{__FILE__}:#{__LINE__})", 1]
+ end
+ 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)
- lambda { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, "1+1", file) }.should raise_error(TypeError, /can't convert MockObject to 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')
- lambda { ModuleSpecs.send(@method, o) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, "no implicit conversion of MockObject into String")
(o = mock('123')).should_receive(:to_str).and_return(123)
- lambda { ModuleSpecs.send(@method, o) }.should raise_error(TypeError)
+ -> { ModuleSpecs.send(@method, o) }.should raise_error(TypeError, /can't convert MockObject to String/)
end
it "raises an ArgumentError when no arguments and no block are given" do
- lambda { ModuleSpecs.send(@method) }.should raise_error(ArgumentError)
+ -> { ModuleSpecs.send(@method) }.should raise_error(ArgumentError, "wrong number of arguments (given 0, expected 1..3)")
end
it "raises an ArgumentError when more than 3 arguments are given" do
- lambda {
+ -> {
ModuleSpecs.send(@method, "1 + 1", "some file", 0, "bogus")
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 4, expected 1..3)")
end
it "raises an ArgumentError when a block and normal arguments are given" do
- lambda {
+ -> {
ModuleSpecs.send(@method, "1 + 1") { 1 + 1 }
- }.should raise_error(ArgumentError)
+ }.should raise_error(ArgumentError, "wrong number of arguments (given 1, expected 0)")
end
# This case was found because Rubinius was caching the compiled
@@ -112,4 +127,48 @@ describe :module_class_eval, shared: true do
a.attribute.should == "A"
b.attribute.should == "B"
end
+
+ it "activates refinements from the eval scope" do
+ refinery = Module.new do
+ refine ModuleSpecs::NamedClass do
+ def foo
+ "bar"
+ end
+ end
+ end
+
+ mid = @method
+ result = nil
+
+ Class.new do
+ using refinery
+
+ result = send(mid, "ModuleSpecs::NamedClass.new.foo")
+ end
+
+ result.should == "bar"
+ end
+
+ it "activates refinements from the eval scope with block" do
+ refinery = Module.new do
+ refine ModuleSpecs::NamedClass do
+ def foo
+ "bar"
+ end
+ end
+ end
+
+ mid = @method
+ result = nil
+
+ Class.new do
+ using refinery
+
+ result = send(mid) do
+ ModuleSpecs::NamedClass.new.foo
+ end
+ end
+
+ result.should == "bar"
+ end
end
diff --git a/spec/ruby/core/module/shared/class_exec.rb b/spec/ruby/core/module/shared/class_exec.rb
index c5c18b0a34..c7a9e5297f 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
- lambda {42.foo}.should raise_error(NoMethodError)
+ -> {42.foo}.should raise_error(NoMethodError)
end
it "defines method in the receiver's scope" do
@@ -19,7 +19,7 @@ describe :module_class_exec, shared: true do
end
it "raises a LocalJumpError when no block is given" do
- lambda { ModuleSpecs::Subclass.send(@method) }.should raise_error(LocalJumpError)
+ -> { ModuleSpecs::Subclass.send(@method) }.should raise_error(LocalJumpError)
end
it "passes arguments to the block" do
diff --git a/spec/ruby/core/module/shared/set_visibility.rb b/spec/ruby/core/module/shared/set_visibility.rb
index c39d59e05d..a1586dd2bd 100644
--- a/spec/ruby/core/module/shared/set_visibility.rb
+++ b/spec/ruby/core/module/shared/set_visibility.rb
@@ -5,6 +5,55 @@ describe :set_visibility, shared: true do
Module.should have_private_instance_method(@method, false)
end
+ describe "with argument" do
+ describe "one or more arguments" 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
+ 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.should send(:"have_#{visibility}_instance_method", :test1, false)
+ mod.should send(:"have_#{visibility}_instance_method", :test2, false)
+ end
+ end
+
+ it "does not clone method from the ancestor when setting to the same visibility in a child" do
+ visibility = @method
+ parent = Module.new {
+ def test_method; end
+ send(visibility, :test_method)
+ }
+
+ child = Module.new {
+ include parent
+ send(visibility, :test_method)
+ }
+
+ child.should_not send(:"have_#{visibility}_instance_method", :test_method, false)
+ end
+ end
+
describe "without arguments" do
it "sets visibility to following method definitions" do
visibility = @method
diff --git a/spec/ruby/core/module/singleton_class_spec.rb b/spec/ruby/core/module/singleton_class_spec.rb
index 177dfc224f..052755b73b 100644
--- a/spec/ruby/core/module/singleton_class_spec.rb
+++ b/spec/ruby/core/module/singleton_class_spec.rb
@@ -1,27 +1,27 @@
-require File.expand_path('../../../spec_helper', __FILE__)
+require_relative '../../spec_helper'
describe "Module#singleton_class?" do
it "returns true for singleton classes" do
xs = self.singleton_class
- xs.singleton_class?.should == true
+ xs.should.singleton_class?
end
it "returns false for other classes" do
c = Class.new
- c.singleton_class?.should == false
+ c.should_not.singleton_class?
end
describe "with singleton values" do
it "returns false for nil's singleton class" do
- NilClass.singleton_class?.should == false
+ NilClass.should_not.singleton_class?
end
it "returns false for true's singleton class" do
- TrueClass.singleton_class?.should == false
+ TrueClass.should_not.singleton_class?
end
it "returns false for false's singleton class" do
- FalseClass.singleton_class?.should == false
+ FalseClass.should_not.singleton_class?
end
end
end
diff --git a/spec/ruby/core/module/to_s_spec.rb b/spec/ruby/core/module/to_s_spec.rb
index de924a8739..83c0ae0825 100644
--- a/spec/ruby/core/module/to_s_spec.rb
+++ b/spec/ruby/core/module/to_s_spec.rb
@@ -1,18 +1,70 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
describe "Module#to_s" do
+ it 'returns the name of the module if it has a name' do
+ Enumerable.to_s.should == 'Enumerable'
+ String.to_s.should == 'String'
+ end
+
it "returns the full constant path leading to the module" do
ModuleSpecs::LookupMod.to_s.should == "ModuleSpecs::LookupMod"
end
it "works with an anonymous module" do
m = Module.new
- m.to_s.should =~ /#<Module:0x[0-9a-f]+>/
+ m.to_s.should =~ /\A#<Module:0x\h+>\z/
end
it "works with an anonymous class" do
c = Class.new
- c.to_s.should =~ /#<Class:0x[0-9a-f]+>/
+ c.to_s.should =~ /\A#<Class:0x\h+>\z/
+ end
+
+ it 'for the singleton class of an object of an anonymous class' do
+ klass = Class.new
+ obj = klass.new
+ sclass = obj.singleton_class
+ sclass.to_s.should == "#<Class:#{obj}>"
+ sclass.to_s.should =~ /\A#<Class:#<#{klass}:0x\h+>>\z/
+ sclass.to_s.should =~ /\A#<Class:#<#<Class:0x\h+>:0x\h+>>\z/
+ end
+
+ it 'for a singleton class of a module includes the module name' do
+ ModuleSpecs.singleton_class.to_s.should == '#<Class:ModuleSpecs>'
+ end
+
+ it 'for a metaclass includes the class name' do
+ ModuleSpecs::NamedClass.singleton_class.to_s.should == '#<Class:ModuleSpecs::NamedClass>'
+ end
+
+ it 'for objects includes class name and object ID' 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 54c3d37c7f..d4efcd51cb 100644
--- a/spec/ruby/core/module/undef_method_spec.rb
+++ b/spec/ruby/core/module/undef_method_spec.rb
@@ -1,5 +1,5 @@
-require File.expand_path('../../../spec_helper', __FILE__)
-require File.expand_path('../fixtures/classes', __FILE__)
+require_relative '../../spec_helper'
+require_relative 'fixtures/classes'
module ModuleSpecs
class Parent
@@ -18,15 +18,8 @@ describe "Module#undef_method" do
@module = Module.new { def method_to_undef; end }
end
- ruby_version_is ''...'2.5' do
- it "is a private method" do
- Module.should have_private_instance_method(:undef_method, false)
- end
- end
- ruby_version_is '2.5' do
- it "is a public method" do
- Module.should have_public_instance_method(:undef_method, false)
- end
+ it "is a public method" do
+ Module.should have_public_instance_method(:undef_method, false)
end
it "requires multiple arguments" do
@@ -41,8 +34,8 @@ describe "Module#undef_method" do
x = klass.new
klass.send(:undef_method, :method_to_undef, :another_method_to_undef)
- lambda { x.method_to_undef }.should raise_error(NoMethodError)
- lambda { x.another_method_to_undef }.should raise_error(NoMethodError)
+ -> { x.method_to_undef }.should raise_error(NoMethodError)
+ -> { x.another_method_to_undef }.should raise_error(NoMethodError)
end
it "does not undef any instance methods when argument not given" do
@@ -56,8 +49,37 @@ describe "Module#undef_method" do
@module.send(:undef_method, :method_to_undef).should equal(@module)
end
- it "raises a NameError when passed a missing name" do
- lambda { @module.send :undef_method, :not_exist }.should raise_error(NameError) { |e|
+ 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|
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+
+ 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|
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+
+ it "raises a NameError when passed a missing name for a singleton class" do
+ klass = Class.new
+ 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 =~ /[`']#<Class:#<#<Class:/
+
+ # a NameError and not a NoMethodError
+ e.class.should == NameError
+ }
+ end
+
+ 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|
# a NameError and not a NoMethodError
e.class.should == NameError
}
@@ -68,16 +90,16 @@ describe "Module#undef_method" do
@frozen = @module.dup.freeze
end
- it "raises a RuntimeError when passed a name" do
- lambda { @frozen.send :undef_method, :method_to_undef }.should raise_error(RuntimeError)
+ it "raises a FrozenError when passed a name" do
+ -> { @frozen.send :undef_method, :method_to_undef }.should raise_error(FrozenError)
end
- it "raises a RuntimeError when passed a missing name" do
- lambda { @frozen.send :undef_method, :not_exist }.should raise_error(RuntimeError)
+ it "raises a FrozenError when passed a missing name" do
+ -> { @frozen.send :undef_method, :not_exist }.should raise_error(FrozenError)
end
it "raises a TypeError when passed a not name" do
- lambda { @frozen.send :undef_method, Object.new }.should raise_error(TypeError)
+ -> { @frozen.send :undef_method, Object.new }.should raise_error(TypeError)
end
it "does not raise exceptions when no arguments given" do
@@ -98,7 +120,7 @@ describe "Module#undef_method with symbol" do
klass.send :undef_method, :method_to_undef
- lambda { x.method_to_undef }.should raise_error(NoMethodError)
+ -> { x.method_to_undef }.should raise_error(NoMethodError)
end
it "removes a method defined in a super class" do
@@ -108,7 +130,7 @@ describe "Module#undef_method with symbol" do
child_class.send :undef_method, :method_to_undef
- lambda { child.method_to_undef }.should raise_error(NoMethodError)
+ -> { child.method_to_undef }.should raise_error(NoMethodError)
end
it "does not remove a method defined in a super class when removed from a subclass" do
@@ -134,7 +156,7 @@ describe "Module#undef_method with string" do
klass.send :undef_method, 'another_method_to_undef'
- lambda { x.another_method_to_undef }.should raise_error(NoMethodError)
+ -> { x.another_method_to_undef }.should raise_error(NoMethodError)
end
it "removes a method defined in a super class" do
@@ -144,7 +166,7 @@ describe "Module#undef_method with string" do
child_class.send :undef_method, 'another_method_to_undef'
- lambda { child.another_method_to_undef }.should raise_error(NoMethodError)
+ -> { child.another_method_to_undef }.should raise_error(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..d33ee93fc1
--- /dev/null
+++ b/spec/ruby/core/module/undefined_instance_methods_spec.rb
@@ -0,0 +1,24 @@
+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.should include(:parent_method, :another_parent_method)
+ end
+
+ it "returns methods from an included module that are undefined in the class" do
+ methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods
+ methods.should include(:super_included_method)
+ end
+
+ it "does not returns ancestors undefined methods" do
+ methods = ModuleSpecs::UndefinedInstanceMethods::Grandchild.undefined_instance_methods
+ methods.should_not include(:parent_method, :another_parent_method)
+ end
+end
diff --git a/spec/ruby/core/module/used_refinements_spec.rb b/spec/ruby/core/module/used_refinements_spec.rb
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 2e7e15b748..a908363c96 100644
--- a/spec/ruby/core/module/using_spec.rb
+++ b/spec/ruby/core/module/using_spec.rb
@@ -1,4 +1,4 @@
-require File.expand_path('../../../spec_helper', __FILE__)
+require_relative '../../spec_helper'
describe "Module#using" do
it "imports class refinements from module into the current class/module" do
@@ -24,7 +24,7 @@ describe "Module#using" do
end
end
- -> () {
+ -> {
Module.new do
using refinement
end
@@ -34,7 +34,7 @@ describe "Module#using" do
it "accepts module without refinements" do
mod = Module.new
- -> () {
+ -> {
Module.new do
using mod
end
@@ -44,7 +44,7 @@ describe "Module#using" do
it "does not accept class" do
klass = Class.new
- -> () {
+ -> {
Module.new do
using klass
end
@@ -52,7 +52,7 @@ describe "Module#using" do
end
it "raises TypeError if passed something other than module" do
- -> () {
+ -> {
Module.new do
using "foo"
end
@@ -93,7 +93,7 @@ describe "Module#using" do
end
end
- -> () {
+ -> {
mod.foo
}.should raise_error(RuntimeError, /Module#using is not permitted in methods/)
end
@@ -243,6 +243,96 @@ describe "Module#using" do
mod.call_foo(c).should == "foo from refinement"
end
+ it "is active for module defined via Module.new {}" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+
+ Module.new do
+ using refinement
+
+ Module.new do
+ result = 1.foo
+ end
+ end
+
+ result.should == "foo from refinement"
+ end
+
+ it "is active for class defined via Class.new {}" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ result = nil
+
+ Module.new do
+ using refinement
+
+ Class.new do
+ result = 1.foo
+ end
+ end
+
+ result.should == "foo from refinement"
+ end
+
+ it "is active for block called via instance_exec" do
+ refinement = Module.new do
+ refine Integer do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ c = Class.new do
+ using refinement
+
+ def abc
+ block = -> {
+ 1.foo
+ }
+
+ self.instance_exec(&block)
+ end
+ end
+
+ c.new.abc.should == "foo from refinement"
+ end
+
+ it "is active for block called via instance_eval" do
+ refinement = Module.new do
+ refine String do
+ def foo; "foo from refinement"; end
+ end
+ end
+
+ c = Class.new do
+ using refinement
+
+ def initialize
+ @a = +"1703"
+
+ @a.instance_eval do
+ def abc
+ "#{self}: #{self.foo}"
+ end
+ end
+ end
+
+ def abc
+ @a.abc
+ end
+ end
+
+ c.new.abc.should == "1703: foo from refinement"
+ end
+
it "is not active if `using` call is not evaluated" do
result = nil