diff options
Diffstat (limited to 'spec/ruby/core/module/refine_spec.rb')
| -rw-r--r-- | spec/ruby/core/module/refine_spec.rb | 482 |
1 files changed, 269 insertions, 213 deletions
diff --git a/spec/ruby/core/module/refine_spec.rb b/spec/ruby/core/module/refine_spec.rb index ca7db0c2b6..d0fc7015f8 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 @@ -11,7 +11,7 @@ describe "Module#refine" do end mod.should_not == inner_self - inner_self.should be_kind_of(Module) + inner_self.should.is_a?(Module) inner_self.name.should == nil end @@ -43,7 +43,7 @@ describe "Module#refine" do end end - inner_self.public_instance_methods.should include(:blah) + inner_self.public_instance_methods.should.include?(:blah) end it "returns created anonymous module" do @@ -59,54 +59,65 @@ describe "Module#refine" do end it "raises ArgumentError if not passed an argument" do - lambda do + -> do Module.new do refine {} end - end.should raise_error(ArgumentError) + end.should.raise(ArgumentError) end it "raises TypeError if not passed a class" do - lambda do + -> do Module.new do refine("foo") {} end - end.should raise_error(TypeError) + end.should.raise(TypeError, "wrong argument type String (expected Class or Module)") end - 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 - inner_self.public_instance_methods.should include(:blah) + foo = Class.new do + using refinement + + 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 - end.should raise_error(ArgumentError) + end.should.raise(ArgumentError) end it "applies refinements to calls in the refine block" do @@ -123,9 +134,9 @@ 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) + }.should.raise(NoMethodError) end 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(NoMethodError) end # When defining multiple refinements in the same module, @@ -191,14 +202,14 @@ describe "Module#refine" do result = nil - -> () { + -> { Module.new do using refinery_integer using refinery_array [1, 2].to_json_format end - }.should raise_error(NoMethodError) + }.should.raise(NoMethodError) end # method lookup: @@ -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(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(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(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,18 @@ 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 + klass = Class.new { instance_methods.should_not.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(NameError) + end end |
