diff options
Diffstat (limited to 'spec/ruby/language/super_spec.rb')
| -rw-r--r-- | spec/ruby/language/super_spec.rb | 281 |
1 files changed, 200 insertions, 81 deletions
diff --git a/spec/ruby/language/super_spec.rb b/spec/ruby/language/super_spec.rb index 3d3f5d6f74..f595d49395 100644 --- a/spec/ruby/language/super_spec.rb +++ b/spec/ruby/language/super_spec.rb @@ -1,76 +1,76 @@ -require File.expand_path('../../spec_helper', __FILE__) -require File.expand_path('../fixtures/super', __FILE__) +require_relative '../spec_helper' +require_relative 'fixtures/super' describe "The super keyword" do it "calls the method on the calling class" do - Super::S1::A.new.foo([]).should == ["A#foo","A#bar"] - Super::S1::A.new.bar([]).should == ["A#bar"] - Super::S1::B.new.foo([]).should == ["B#foo","A#foo","B#bar","A#bar"] - Super::S1::B.new.bar([]).should == ["B#bar","A#bar"] + SuperSpecs::S1::A.new.foo([]).should == ["A#foo","A#bar"] + SuperSpecs::S1::A.new.bar([]).should == ["A#bar"] + SuperSpecs::S1::B.new.foo([]).should == ["B#foo","A#foo","B#bar","A#bar"] + SuperSpecs::S1::B.new.bar([]).should == ["B#bar","A#bar"] end it "searches the full inheritance chain" do - Super::S2::B.new.foo([]).should == ["B#foo","A#baz"] - Super::S2::B.new.baz([]).should == ["A#baz"] - Super::S2::C.new.foo([]).should == ["B#foo","C#baz","A#baz"] - Super::S2::C.new.baz([]).should == ["C#baz","A#baz"] + SuperSpecs::S2::B.new.foo([]).should == ["B#foo","A#baz"] + SuperSpecs::S2::B.new.baz([]).should == ["A#baz"] + SuperSpecs::S2::C.new.foo([]).should == ["B#foo","C#baz","A#baz"] + SuperSpecs::S2::C.new.baz([]).should == ["C#baz","A#baz"] end it "searches class methods" do - Super::S3::A.new.foo([]).should == ["A#foo"] - Super::S3::A.foo([]).should == ["A.foo"] - Super::S3::A.bar([]).should == ["A.bar","A.foo"] - Super::S3::B.new.foo([]).should == ["A#foo"] - Super::S3::B.foo([]).should == ["B.foo","A.foo"] - Super::S3::B.bar([]).should == ["B.bar","A.bar","B.foo","A.foo"] + SuperSpecs::S3::A.new.foo([]).should == ["A#foo"] + SuperSpecs::S3::A.foo([]).should == ["A.foo"] + SuperSpecs::S3::A.bar([]).should == ["A.bar","A.foo"] + SuperSpecs::S3::B.new.foo([]).should == ["A#foo"] + SuperSpecs::S3::B.foo([]).should == ["B.foo","A.foo"] + SuperSpecs::S3::B.bar([]).should == ["B.bar","A.bar","B.foo","A.foo"] end it "calls the method on the calling class including modules" do - Super::MS1::A.new.foo([]).should == ["ModA#foo","ModA#bar"] - Super::MS1::A.new.bar([]).should == ["ModA#bar"] - Super::MS1::B.new.foo([]).should == ["B#foo","ModA#foo","ModB#bar","ModA#bar"] - Super::MS1::B.new.bar([]).should == ["ModB#bar","ModA#bar"] + SuperSpecs::MS1::A.new.foo([]).should == ["ModA#foo","ModA#bar"] + SuperSpecs::MS1::A.new.bar([]).should == ["ModA#bar"] + SuperSpecs::MS1::B.new.foo([]).should == ["B#foo","ModA#foo","ModB#bar","ModA#bar"] + SuperSpecs::MS1::B.new.bar([]).should == ["ModB#bar","ModA#bar"] end it "searches the full inheritance chain including modules" do - Super::MS2::B.new.foo([]).should == ["ModB#foo","A#baz"] - Super::MS2::B.new.baz([]).should == ["A#baz"] - Super::MS2::C.new.baz([]).should == ["C#baz","A#baz"] - Super::MS2::C.new.foo([]).should == ["ModB#foo","C#baz","A#baz"] + SuperSpecs::MS2::B.new.foo([]).should == ["ModB#foo","A#baz"] + SuperSpecs::MS2::B.new.baz([]).should == ["A#baz"] + SuperSpecs::MS2::C.new.baz([]).should == ["C#baz","A#baz"] + SuperSpecs::MS2::C.new.foo([]).should == ["ModB#foo","C#baz","A#baz"] end it "can resolve to different methods in an included module method" do - Super::MultiSuperTargets::A.new.foo.should == :BaseA - Super::MultiSuperTargets::B.new.foo.should == :BaseB + SuperSpecs::MultiSuperTargets::A.new.foo.should == :BaseA + SuperSpecs::MultiSuperTargets::B.new.foo.should == :BaseB end it "searches class methods including modules" do - Super::MS3::A.new.foo([]).should == ["A#foo"] - Super::MS3::A.foo([]).should == ["ModA#foo"] - Super::MS3::A.bar([]).should == ["ModA#bar","ModA#foo"] - Super::MS3::B.new.foo([]).should == ["A#foo"] - Super::MS3::B.foo([]).should == ["B.foo","ModA#foo"] - Super::MS3::B.bar([]).should == ["B.bar","ModA#bar","B.foo","ModA#foo"] + SuperSpecs::MS3::A.new.foo([]).should == ["A#foo"] + SuperSpecs::MS3::A.foo([]).should == ["ModA#foo"] + SuperSpecs::MS3::A.bar([]).should == ["ModA#bar","ModA#foo"] + SuperSpecs::MS3::B.new.foo([]).should == ["A#foo"] + SuperSpecs::MS3::B.foo([]).should == ["B.foo","ModA#foo"] + SuperSpecs::MS3::B.bar([]).should == ["B.bar","ModA#bar","B.foo","ModA#foo"] end it "searches BasicObject from a module for methods defined there" do - Super::IncludesFromBasic.new.__send__(:foobar).should == 43 + SuperSpecs::IncludesFromBasic.new.__send__(:foobar).should == 43 end it "searches BasicObject through another module for methods defined there" do - Super::IncludesIntermediate.new.__send__(:foobar).should == 42 + SuperSpecs::IncludesIntermediate.new.__send__(:foobar).should == 42 end it "calls the correct method when the method visibility is modified" do - Super::MS4::A.new.example.should == 5 + SuperSpecs::MS4::A.new.example.should == 5 end it "calls the correct method when the superclass argument list is different from the subclass" do - Super::S4::A.new.foo([]).should == ["A#foo"] - Super::S4::B.new.foo([],"test").should == ["B#foo(a,test)", "A#foo"] + SuperSpecs::S4::A.new.foo([]).should == ["A#foo"] + SuperSpecs::S4::B.new.foo([],"test").should == ["B#foo(a,test)", "A#foo"] end - it "raises an error error when super method does not exist" do + it "raises an error when super method does not exist" do sup = Class.new sub_normal = Class.new(sup) do def foo @@ -83,8 +83,8 @@ describe "The super keyword" do end end - lambda {sub_normal.new.foo}.should raise_error(NoMethodError, /super/) - lambda {sub_zsuper.new.foo}.should raise_error(NoMethodError, /super/) + -> {sub_normal.new.foo}.should.raise(NoMethodError, /super/) + -> {sub_zsuper.new.foo}.should.raise(NoMethodError, /super/) end it "uses given block even if arguments are passed explicitly" do @@ -102,16 +102,66 @@ describe "The super keyword" do c2.new.m(:dump) { :value }.should == :value end + it "can pass an explicit block" do + c1 = Class.new do + def m(v) + yield(v) + end + end + c2 = Class.new(c1) do + def m(v) + block = -> w { yield(w + 'b') } + super(v, &block) + end + end + + c2.new.m('a') { |x| x + 'c' }.should == 'abc' + end + + it "can pass no block using &nil" do + c1 = Class.new do + def m(v) + block_given? + end + end + c2 = Class.new(c1) do + def m(v) + super(v, &nil) + end + end + + c2.new.m('a') { raise }.should == false + end + + it "uses block argument given to method when used in a block" do + c1 = Class.new do + def m + yield + end + end + c2 = Class.new(c1) do + def m(v) + ary = [] + 1.times do + ary << super() + end + ary + end + end + + c2.new.m(:dump) { :value }.should == [ :value ] + end + it "calls the superclass method when in a block" do - Super::S6.new.here.should == :good + SuperSpecs::S6.new.here.should == :good end it "calls the superclass method when initial method is defined_method'd" do - Super::S7.new.here.should == :good + SuperSpecs::S7.new.here.should == :good end it "can call through a define_method multiple times (caching check)" do - obj = Super::S7.new + obj = SuperSpecs::S7.new 2.times do obj.here.should == :good @@ -150,25 +200,44 @@ describe "The super keyword" do end end - lambda { klass.new.a(:a_called) }.should raise_error(RuntimeError) + -> { klass.new.a(:a_called) }.should.raise(RuntimeError) + end + + it "is able to navigate to super, when a method is defined dynamically on the singleton class" do + foo_class = Class.new do + def bar + "bar" + end + end + + mixin_module = Module.new do + def bar + "super_" + super + end + end + + foo = foo_class.new + foo.singleton_class.define_method(:bar, mixin_module.instance_method(:bar)) + + foo.bar.should == "super_bar" end # Rubinius ticket github#157 it "calls method_missing when a superclass method is not found" do - Super::MM_B.new.is_a?(Hash).should == false + SuperSpecs::MM_B.new.is_a?(Hash).should == false end # Rubinius ticket github#180 it "respects the original module a method is aliased from" do - Super::Alias3.new.name3.should == [:alias2, :alias1] + SuperSpecs::Alias3.new.name3.should == [:alias2, :alias1] end it "sees the included version of a module a method is alias from" do - Super::AliasWithSuper::Trigger.foo.should == [:b, :a] + SuperSpecs::AliasWithSuper::Trigger.foo.should == [:b, :a] end it "find super from a singleton class" do - obj = Super::SingletonCase::Foo.new + obj = SuperSpecs::SingletonCase::Foo.new def obj.foobar(array) array << :singleton super @@ -177,87 +246,123 @@ describe "The super keyword" do end it "finds super on other objects if a singleton class aliased the method" do - orig_obj = Super::SingletonAliasCase::Foo.new + orig_obj = SuperSpecs::SingletonAliasCase::Foo.new orig_obj.alias_on_singleton orig_obj.new_foobar([]).should == [:foo, :base] - Super::SingletonAliasCase::Foo.new.foobar([]).should == [:foo, :base] + SuperSpecs::SingletonAliasCase::Foo.new.foobar([]).should == [:foo, :base] end it "passes along modified rest args when they weren't originally empty" do - Super::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"] + SuperSpecs::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"] end it "passes along modified rest args when they were originally empty" do - Super::RestArgsWithSuper::B.new.a.should == ["foo"] + SuperSpecs::RestArgsWithSuper::B.new.a.should == ["foo"] + end + + # https://bugs.ruby-lang.org/issues/14279 + it "passes along reassigned rest args" do + SuperSpecs::ZSuperWithRestReassigned::B.new.a("bar").should == ["foo"] + end + + # https://bugs.ruby-lang.org/issues/14279 + it "wraps into array and passes along reassigned rest args with non-array scalar value" do + SuperSpecs::ZSuperWithRestReassignedWithScalar::B.new.a("bar").should == ["foo"] end it "invokes methods from a chain of anonymous modules" do - Super::AnonymousModuleIncludedTwice.new.a([]).should == ["anon", "anon", "non-anon"] + SuperSpecs::AnonymousModuleIncludedTwice.new.a([]).should == ["anon", "anon", "non-anon"] end it "without explicit arguments can accept a block but still pass the original arguments" do - Super::ZSuperWithBlock::B.new.a.should == 14 + SuperSpecs::ZSuperWithBlock::B.new.a.should == 14 end it "passes along block via reference to method expecting a reference" do - Super::ZSuperWithBlock::B.new.b.should == [14, 15] + SuperSpecs::ZSuperWithBlock::B.new.b.should == [14, 15] end it "passes along a block via reference to a method that yields" do - Super::ZSuperWithBlock::B.new.c.should == 16 + SuperSpecs::ZSuperWithBlock::B.new.c.should == 16 end it "without explicit arguments passes optional arguments that have a default value" do - Super::ZSuperWithOptional::B.new.m(1, 2).should == 14 + SuperSpecs::ZSuperWithOptional::B.new.m(1, 2).should == 14 end it "without explicit arguments passes optional arguments that have a non-default value" do - Super::ZSuperWithOptional::B.new.m(1, 2, 3).should == 3 + SuperSpecs::ZSuperWithOptional::B.new.m(1, 2, 3).should == 3 end it "without explicit arguments passes optional arguments that have a default value but were modified" do - Super::ZSuperWithOptional::C.new.m(1, 2).should == 100 + SuperSpecs::ZSuperWithOptional::C.new.m(1, 2).should == 100 end it "without explicit arguments passes optional arguments that have a non-default value but were modified" do - Super::ZSuperWithOptional::C.new.m(1, 2, 3).should == 100 + SuperSpecs::ZSuperWithOptional::C.new.m(1, 2, 3).should == 100 end it "without explicit arguments passes rest arguments" do - Super::ZSuperWithRest::B.new.m(1, 2, 3).should == [1, 2, 3] + SuperSpecs::ZSuperWithRest::B.new.m(1, 2, 3).should == [1, 2, 3] end it "without explicit arguments passes rest arguments including any modifications" do - Super::ZSuperWithRest::B.new.m_modified(1, 2, 3).should == [1, 14, 3] + SuperSpecs::ZSuperWithRest::B.new.m_modified(1, 2, 3).should == [1, 14, 3] end it "without explicit arguments passes arguments and rest arguments" do - Super::ZSuperWithRestAndOthers::B.new.m(1, 2, 3, 4, 5).should == [3, 4, 5] + SuperSpecs::ZSuperWithRestAndOthers::B.new.m(1, 2, 3, 4, 5).should == [3, 4, 5] + SuperSpecs::ZSuperWithRestAndOthers::B.new.m(1, 2).should == [] + end + + it "without explicit arguments passes arguments, rest arguments, and post arguments" do + SuperSpecs::ZSuperWithRestAndPost::B.new.m(1, 2, 3, 4, 5).should == [1, 2, 3] + SuperSpecs::ZSuperWithRestOthersAndPost::B.new.m(1, 2, 3, 4, 5).should == [2, 3, 4] + SuperSpecs::ZSuperWithRestAndPost::B.new.m(1, 2).should == [] + SuperSpecs::ZSuperWithRestOthersAndPost::B.new.m(1, 2).should == [] + end + + it "without explicit arguments passes arguments, rest arguments including modifications, and post arguments" do + SuperSpecs::ZSuperWithRestAndPost::B.new.m_modified(1, 2, 3, 4, 5).should == [1, 14, 3] + SuperSpecs::ZSuperWithRestOthersAndPost::B.new.m_modified(1, 2, 3, 4, 5).should == [2, 14, 4] + SuperSpecs::ZSuperWithRestAndPost::B.new.m_modified(1, 2).should == [nil, 14] + SuperSpecs::ZSuperWithRestOthersAndPost::B.new.m_modified(1, 2).should == [nil, 14] end it "without explicit arguments passes arguments and rest arguments including any modifications" do - Super::ZSuperWithRestAndOthers::B.new.m_modified(1, 2, 3, 4, 5).should == [3, 14, 5] + SuperSpecs::ZSuperWithRestAndOthers::B.new.m_modified(1, 2, 3, 4, 5).should == [3, 14, 5] end it "without explicit arguments that are '_'" do - Super::ZSuperWithUnderscores::B.new.m(1, 2).should == [1, 2] + SuperSpecs::ZSuperWithUnderscores::B.new.m(1, 2).should == [1, 2] + SuperSpecs::ZSuperWithUnderscores::B.new.m3(1, 2, 3).should == [1, 2, 3] + SuperSpecs::ZSuperWithUnderscores::B.new.m4(1, 2, 3, 4).should == [1, 2, 3, 4] + SuperSpecs::ZSuperWithUnderscores::B.new.m_default(1).should == [1] + SuperSpecs::ZSuperWithUnderscores::B.new.m_default.should == [0] + SuperSpecs::ZSuperWithUnderscores::B.new.m_pre_default_rest_post(1, 2, 3, 4, 5, 6, 7).should == [1, 2, 3, 4, 5, 6, 7] + SuperSpecs::ZSuperWithUnderscores::B.new.m_rest(1, 2).should == [1, 2] + SuperSpecs::ZSuperWithUnderscores::B.new.m_kwrest(a: 1).should == {a: 1} end it "without explicit arguments that are '_' including any modifications" do - Super::ZSuperWithUnderscores::B.new.m_modified(1, 2).should == [14, 2] + SuperSpecs::ZSuperWithUnderscores::B.new.m_modified(1, 2).should == [14, 2] + end + + it "should pass method arguments when called within a closure" do + SuperSpecs::ZSuperInBlock::B.new.m(arg: 1).should == 1 end describe 'when using keyword arguments' do before :each do - @req = Super::Keywords::RequiredArguments.new - @opts = Super::Keywords::OptionalArguments.new - @etc = Super::Keywords::PlaceholderArguments.new + @req = SuperSpecs::Keywords::RequiredArguments.new + @opts = SuperSpecs::Keywords::OptionalArguments.new + @etc = SuperSpecs::Keywords::PlaceholderArguments.new - @req_and_opts = Super::Keywords::RequiredAndOptionalArguments.new - @req_and_etc = Super::Keywords::RequiredAndPlaceholderArguments.new - @opts_and_etc = Super::Keywords::OptionalAndPlaceholderArguments.new + @req_and_opts = SuperSpecs::Keywords::RequiredAndOptionalArguments.new + @req_and_etc = SuperSpecs::Keywords::RequiredAndPlaceholderArguments.new + @opts_and_etc = SuperSpecs::Keywords::OptionalAndPlaceholderArguments.new - @req_and_opts_and_etc = Super::Keywords::RequiredAndOptionalAndPlaceholderArguments.new + @req_and_opts_and_etc = SuperSpecs::Keywords::RequiredAndOptionalAndPlaceholderArguments.new end it 'does not pass any arguments to the parent when none are given' do @@ -293,15 +398,15 @@ describe "The super keyword" do describe 'when using regular and keyword arguments' do before :each do - @req = Super::RegularAndKeywords::RequiredArguments.new - @opts = Super::RegularAndKeywords::OptionalArguments.new - @etc = Super::RegularAndKeywords::PlaceholderArguments.new + @req = SuperSpecs::RegularAndKeywords::RequiredArguments.new + @opts = SuperSpecs::RegularAndKeywords::OptionalArguments.new + @etc = SuperSpecs::RegularAndKeywords::PlaceholderArguments.new - @req_and_opts = Super::RegularAndKeywords::RequiredAndOptionalArguments.new - @req_and_etc = Super::RegularAndKeywords::RequiredAndPlaceholderArguments.new - @opts_and_etc = Super::RegularAndKeywords::OptionalAndPlaceholderArguments.new + @req_and_opts = SuperSpecs::RegularAndKeywords::RequiredAndOptionalArguments.new + @req_and_etc = SuperSpecs::RegularAndKeywords::RequiredAndPlaceholderArguments.new + @opts_and_etc = SuperSpecs::RegularAndKeywords::OptionalAndPlaceholderArguments.new - @req_and_opts_and_etc = Super::RegularAndKeywords::RequiredAndOptionalAndPlaceholderArguments.new + @req_and_opts_and_etc = SuperSpecs::RegularAndKeywords::RequiredAndOptionalAndPlaceholderArguments.new end it 'passes only required regular arguments to the parent when no optional keyword arguments are given' do @@ -337,7 +442,7 @@ describe "The super keyword" do describe 'when using splat and keyword arguments' do before :each do - @all = Super::SplatAndKeywords::AllArguments.new + @all = SuperSpecs::SplatAndKeywords::AllArguments.new end it 'does not pass any arguments to the parent when none are given' do @@ -356,4 +461,18 @@ describe "The super keyword" do @all.foo('a', b: 'b').should == [['a'], {b: 'b'}] end end + + it "works in method definitions using **nil" do + parent = Class.new do + def m(*args, **kwargs) + [args, kwargs] + end + end + child = Class.new(parent) do + def m(*args, **nil) + super + end + end + child.new.m(1, 2).should == [[1, 2], {}] + end end |
