require 'test/unit' # TestCase corresponding to PragDave's tentative description of # { } vs. Proc.new vs. lambda with regard to break, next & return class TestSemantics < Test::Unit::TestCase DUMMY_BLOCK_YIELD_RETVAL = :dummy_yield_retval def dummy_block_yield yield DUMMY_BLOCK_YIELD_RETVAL end def dummy_block_yield2(&block) yield DUMMY_BLOCK_YIELD_RETVAL end def dummy_Proc_call(aproc) aproc.call DUMMY_BLOCK_YIELD_RETVAL end def dummy_block_call(&block) block.call DUMMY_BLOCK_YIELD_RETVAL end # block def test_break_block # a block invoked using yield is always invoked in iterator context assert_equal(true, dummy_block_yield{ break true }) assert_equal(true, dummy_block_yield2{ break true }) # if the last parameter in a method def. is prefixed with an # ampersand.... closure context assert_raises(LocalJumpError){ dummy_block_call{ break true } } end def test_return_block_1 # a block invoked using yield is always invoked in iterator context assert_raises(LocalJumpError) { dummy_block_yield{ return true } } end def test_return_block_2 # a block invoked using yield is always invoked in iterator context assert_raises(LocalJumpError) { dummy_block_yield2{ return true } } end def test_return_block_3 # BUG # a return from a block that is no longer in active scope ... # raises a LocalJumpError assert_raises(LocalJumpError){ dummy_block_call{ return true } } end def test_next_block # a block invoked using yield is always invoked in iterator context assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_yield{ next true }) assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_yield2{ next true }) # if the last parameter in a method def. is prefixed with an # ampersand.... closure context assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_call{ next true }) end # Proc.new def test_break_Proc_new #a block invoked using yield is always invoked in iterator context assert_equal(true, dummy_block_yield(&Proc.new{ break true })) assert_equal(true, dummy_block_yield2(&Proc.new{ break true })) # in a closure context, break is illegal assert_raises(LocalJumpError) { dummy_block_call(&Proc.new{ break true }) } assert_raises(LocalJumpError) { dummy_Proc_call(Proc.new{ break true }) } end def test_return_Proc_new # a block invoked using yield is always invoked in iterator context assert_raises(LocalJumpError) { dummy_block_yield(&Proc.new{ return true }) } end def test_return_Proc_new2 # a block invoked using yield is always invoked in iterator context assert_raises(LocalJumpError) { dummy_block_yield2(&Proc.new{ return true }) } end def test_return_Proc_new3 # BUG # ... and the rules above apply assert_raises(LocalJumpError) { dummy_block_call(&Proc.new{ return true }) } assert_raises(LocalJumpError) { dummy_Proc_call(Proc.new{ return true }) } end def test_next_Proc_new # a block invoked using yield is always invoked in iterator context assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_yield(&Proc.new{ next true })) assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_yield2(&Proc.new{ next true })) # ! Proc.new => closure context assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_call(&Proc.new{ next true })) assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_Proc_call(Proc.new{ next true })) end # lambda def test_break_lambda # a block invoked using yield is always invoked in iterator context assert_equal(true, dummy_block_yield(&lambda{ break true })) assert_equal(true, dummy_block_yield2(&lambda{ break true })) end def test_break_lambda2 # BUG # using Kernel#proc or Kernel#lambda a Proc obj. flagged as being in # iterator context is generated assert_equal(true, dummy_block_call(&lambda{ break true })) end def test_break_lambda3 # BUG # using Kernel#proc or Kernel#lambda a Proc obj. flagged as being in # iterator context is generated assert_equal(true, dummy_Proc_call(lambda{ break true })) end def test_break_lambda4 # BUG # using Kernel#proc or Kernel#lambda a Proc obj. flagged as being in # iterator context is generated assert_equal(true, dummy_Proc_call(lambda{ break true })) end def test_return_lambda # a block invoked using yield is always invoked in iterator context assert_raises(LocalJumpError) { dummy_block_yield(&lambda{ return true }) } end def test_return_lambda2 # a block invoked using yield is always invoked in iterator context assert_raises(LocalJumpError) { dummy_block_yield2(&lambda{ return true }) } end def test_return_lambda3 # a return simply returns from the block to the caller of the block assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_call(&lambda{ return true })) assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_Proc_call(lambda{ return true })) end def test_next_lambda # a block invoked using yield is always invoked in iterator context assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_yield(&lambda{ next true })) assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_yield2(&lambda{ next true })) # using Kernel#proc or Kernel#lambda a Proc obj. flagged as being in # iterator context is generated assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_block_call(&lambda{ next true })) assert_equal(DUMMY_BLOCK_YIELD_RETVAL, dummy_Proc_call(lambda{ next true })) end end # you might be missing some neurons if you read this far...