From: perlun@... Date: 2018-05-28T12:00:45+00:00 Subject: [ruby-core:87273] [Ruby trunk Feature#12906] do/end blocks work with ensure/rescue/else Issue #12906 has been updated by perlun (Per Lundberg). Old issue, but still perhaps the right place to mention this: the new syntax ONLY works in do/end, not in {} blocks (as mentioned above.) It also does not work in "block-like" places like a for loop. So this is not valid syntax: ``` for i in 1..100 puts 'hello' rescue # Syntax error; not valid code in existing Ruby versions like 2.5.0 and 2.5.1. end ``` Suggested workaround: Use a `(1..100).each do |i|` construct instead. Then you can use a `rescue` or `ensure` within the block in Ruby 2.5 and newer. (This is actually an arguably valid use case for "fine grained error handling", in cases where the loop can fail in `n` of the `100` cases and you want the failing ones to be ignored. I agree that too-finegrained error handling can otherwise become an antipattern.) ---------------------------------------- Feature #12906: do/end blocks work with ensure/rescue/else https://bugs.ruby-lang.org/issues/12906#change-72263 * Author: josh.cheek (Josh Cheek) * Status: Closed * Priority: Normal * Assignee: nobu (Nobuyoshi Nakada) * Target version: 2.5 ---------------------------------------- When you want to rescue in a block, you must do this: ~~~ruby lambda do begin raise 'err' rescue $! # => # end end.call ~~~ I've wished on numerous occasions that I could omit the begin/end and not need the extra wrapper: ~~~ruby lambda do raise 'err' rescue $! # => # end.call ~~~ This would be consistent with how classes and methods work: ~~~ruby class C raise 'err' rescue $! # => # end send def m raise 'err' rescue $! # => # end ~~~ It's not really clear to me how to submit this since it may require some discussion, but this is the diff: ~~~diff diff --git a/parse.y b/parse.y index 54ccc52..223e5d3 100644 --- a/parse.y +++ b/parse.y @@ -3757,7 +3757,7 @@ brace_body : {$$ = dyna_push();} do_body : {$$ = dyna_push();} {$$ = cmdarg_stack >> 1; CMDARG_SET(0);} - opt_block_param compstmt + opt_block_param bodystmt { $$ = new_do_body($3, $4); dyna_pop($1); ~~~ I added tests for ensure to rubyspec, but there wasn't an obvious place to talk about rescue/else in this context (the spec for rescue only uses it in a begin/end block) It's probably fine as the spec for ensure does hit rescue, too, and they ultimately delegate to the same pieces. Not totally clear, though. I can do more with that if you need. ~~~diff diff --git a/language/ensure_spec.rb b/language/ensure_spec.rb index 13575fc..b14b0b5 100644 --- a/language/ensure_spec.rb +++ b/language/ensure_spec.rb @@ -124,3 +124,74 @@ describe "An ensure block inside a method" do @obj.explicit_return_in_method_with_ensure.should == :ensure end end + +describe "An ensure block inside a do block" do + before :each do + ScratchPad.record [] + end + + it "is executed when an exception is raised in it's corresponding do block" do + begin + lambda do + ScratchPad << :begin + raise "An exception occured!" + ensure + ScratchPad << :ensure + end.should raise_error(RuntimeError) + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + it "is executed when an exception is raised and rescued in it's corresponding do block" do + begin + lambda do + ScratchPad << :begin + raise "An exception occured!" + rescue + ScratchPad << :rescue + ensure + ScratchPad << :ensure + end.call + + ScratchPad.recorded.should == [:begin, :rescue, :ensure] + end + end + + it "is executed even when a symbol is thrown in it's corresponding do block" do + begin + catch(:symbol) do + lambda do + ScratchPad << :begin + throw(:symbol) + rescue + ScratchPad << :rescue + ensure + ScratchPad << :ensure + end.call + end + + ScratchPad.recorded.should == [:begin, :ensure] + end + end + + it "is executed when nothing is raised or thrown in it's corresponding do block" do + lambda do + ScratchPad << :begin + rescue + ScratchPad << :rescue + ensure + ScratchPad << :ensure + end.call + + ScratchPad.recorded.should == [:begin, :ensure] + end + + it "has no return value" do + lambda do + :begin + ensure + :ensure + end.call.should == :begin + end +end ~~~ -- https://bugs.ruby-lang.org/ Unsubscribe: