require_relative '../spec_helper' require_relative 'fixtures/next' describe "The next statement from within the block" do before :each do ScratchPad.record [] end it "ends block execution" do a = [] -> { a << 1 next a << 2 }.call a.should == [1] end it "causes block to return nil if invoked without arguments" do -> { 123; next; 456 }.call.should == nil end it "causes block to return nil if invoked with an empty expression" do -> { next (); 456 }.call.should be_nil end it "returns the argument passed" do -> { 123; next 234; 345 }.call.should == 234 end it "returns to the invoking method" do NextSpecs.yielding_method(nil) { next }.should == :method_return_value end it "returns to the invoking method, with the specified value" do NextSpecs.yielding_method(nil) { next nil; fail("next didn't end the block execution") }.should == :method_return_value NextSpecs.yielding_method(1) { next 1 fail("next didn't end the block execution") }.should == :method_return_value NextSpecs.yielding_method([1, 2, 3]) { next 1, 2, 3 fail("next didn't end the block execution") }.should == :method_return_value end it "returns to the currently yielding method in case of chained calls" do class ChainedNextTest def self.meth_with_yield(&b) yield.should == :next_return_value :method_return_value end def self.invoking_method(&b) meth_with_yield(&b) end def self.enclosing_method invoking_method do next :next_return_value :wrong_return_value end end end ChainedNextTest.enclosing_method.should == :method_return_value end it "causes ensure blocks to run" do [1].each do |i| begin ScratchPad << :begin next ensure ScratchPad << :ensure end end ScratchPad.recorded.should == [:begin, :ensure] end it "skips following code outside an exception block" do 3.times do |i| begin ScratchPad << :begin next if i == 0 break if i == 2 ScratchPad << :begin_end ensure ScratchPad << :ensure end ScratchPad << :after end ScratchPad.recorded.should == [ :begin, :ensure, :begin, :begin_end, :ensure, :after, :begin, :ensure] end it "passes the value returned by a method with omitted parenthesis and passed block" do obj = NextSpecs::Block.new -> { next obj.method :value do |x| x end }.call.should == :value end end describe "The next statement" do describe "in a method" do it "is invalid and raises a SyntaxError" do -> { eval("def m; next; end") }.should raise_error(SyntaxError) end end end describe "The next statement" do before :each do ScratchPad.record [] end describe "in a while loop" do describe "when not passed an argument" do it "causes ensure blocks to run" do NextSpecs.while_next(false) ScratchPad.recorded.should == [:begin, :ensure] end it "causes ensure blocks to run when nested in an block" do NextSpecs.while_within_iter(false) ScratchPad.recorded.should == [:begin, :ensure] end end describe "when passed an argument" do it "causes ensure blocks to run" do NextSpecs.while_next(true) ScratchPad.recorded.should == [:begin, :ensure] end it "causes ensure blocks to run when nested in an block" do NextSpecs.while_within_iter(true) ScratchPad.recorded.should == [:begin, :ensure] end end it "causes nested ensure blocks to run" do x = true while x begin ScratchPad << :outer_begin x = false begin ScratchPad << :inner_begin next ensure ScratchPad << :inner_ensure end ensure ScratchPad << :outer_ensure end end ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure] end it "causes ensure blocks to run when mixed with break" do x = 1 while true begin ScratchPad << :begin break if x > 1 x += 1 next ensure ScratchPad << :ensure end end ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure] end end describe "in an until loop" do describe "when not passed an argument" do it "causes ensure blocks to run" do NextSpecs.until_next(false) ScratchPad.recorded.should == [:begin, :ensure] end it "causes ensure blocks to run when nested in an block" do NextSpecs.until_within_iter(false) ScratchPad.recorded.should == [:begin, :ensure] end end describe "when passed an argument" do it "causes ensure blocks to run" do NextSpecs.until_next(true) ScratchPad.recorded.should == [:begin, :ensure] end it "causes ensure blocks to run when nested in an block" do NextSpecs.until_within_iter(true) ScratchPad.recorded.should == [:begin, :ensure] end end it "causes nested ensure blocks to run" do x = false until x begin ScratchPad << :outer_begin x = true begin ScratchPad << :inner_begin next ensure ScratchPad << :inner_ensure end ensure ScratchPad << :outer_ensure end end ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure] end it "causes ensure blocks to run when mixed with break" do x = 1 until false begin ScratchPad << :begin break if x > 1 x += 1 next ensure ScratchPad << :ensure end end ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure] end end describe "in a loop" do describe "when not passed an argument" do it "causes ensure blocks to run" do NextSpecs.loop_next(false) ScratchPad.recorded.should == [:begin, :ensure] end it "causes ensure blocks to run when nested in an block" do NextSpecs.loop_within_iter(false) ScratchPad.recorded.should == [:begin, :ensure] end end describe "when passed an argument" do it "causes ensure blocks to run" do NextSpecs.loop_next(true) ScratchPad.recorded.should == [:begin, :ensure] end it "causes ensure blocks to run when nested in an block" do NextSpecs.loop_within_iter(true) ScratchPad.recorded.should == [:begin, :ensure] end end it "causes nested ensure blocks to run" do x = 1 loop do break if x == 2 begin ScratchPad << :outer_begin begin ScratchPad << :inner_begin x += 1 next ensure ScratchPad << :inner_ensure end ensure ScratchPad << :outer_ensure end end ScratchPad.recorded.should == [:outer_begin, :inner_begin, :inner_ensure, :outer_ensure] end it "causes ensure blocks to run when mixed with break" do x = 1 loop do begin ScratchPad << :begin break if x > 1 x += 1 next ensure ScratchPad << :ensure end end ScratchPad.recorded.should == [:begin, :ensure, :begin, :ensure] end end end describe "Assignment via next" do it "assigns objects" do def r(val); a = yield(); val.should == a; end r(nil){next} r(nil){next nil} r(1){next 1} r([]){next []} r([1]){next [1]} r([nil]){next [nil]} r([[]]){next [[]]} r([]){next [*[]]} r([1]){next [*[1]]} r([1,2]){next [*[1,2]]} end it "assigns splatted objects" do def r(val); a = yield(); val.should == a; end r([]){next *nil} r([1]){next *1} r([]){next *[]} r([1]){next *[1]} r([nil]){next *[nil]} r([[]]){next *[[]]} r([]){next *[*[]]} r([1]){next *[*[1]]} r([1,2]){next *[*[1,2]]} end it "assigns objects to a splatted reference" do def r(val); *a = yield(); val.should == a; end r([nil]){next} r([nil]){next nil} r([1]){next 1} r([]){next []} r([1]){next [1]} r([nil]){next [nil]} r([[]]){next [[]]} r([1,2]){next [1,2]} r([]){next [*[]]} r([1]){next [*[1]]} r([1,2]){next [*[1,2]]} end it "assigns splatted objects to a splatted reference via a splatted yield" do def r(val); *a = *yield(); val.should == a; end r([]){next *nil} r([1]){next *1} r([]){next *[]} r([1]){next *[1]} r([nil]){next *[nil]} r([[]]){next *[[]]} r([1,2]){next *[1,2]} r([]){next *[*[]]} r([1]){next *[*[1]]} r([1,2]){next *[*[1,2]]} end it "assigns objects to multiple variables" do def r(val); a,b,*c = yield(); val.should == [a,b,c]; end r([nil,nil,[]]){next} r([nil,nil,[]]){next nil} r([1,nil,[]]){next 1} r([nil,nil,[]]){next []} r([1,nil,[]]){next [1]} r([nil,nil,[]]){next [nil]} r([[],nil,[]]){next [[]]} r([1,2,[]]){next [1,2]} r([nil,nil,[]]){next [*[]]} r([1,nil,[]]){next [*[1]]} r([1,2,[]]){next [*[1,2]]} end it "assigns splatted objects to multiple variables" do def r(val); a,b,*c = *yield(); val.should == [a,b,c]; end r([nil,nil,[]]){next *nil} r([1,nil,[]]){next *1} r([nil,nil,[]]){next *[]} r([1,nil,[]]){next *[1]} r([nil,nil,[]]){next *[nil]} r([[],nil,[]]){next *[[]]} r([1,2,[]]){next *[1,2]} r([nil,nil,[]]){next *[*[]]} r([1,nil,[]]){next *[*[1]]} r([1,2,[]]){next *[*[1,2]]} end end