Issue #15567 has been updated by ioquatix (Samuel Williams).


I revisited this situation again.

This behaviour is very confusing:

~~~ ruby
def transaction
	begin
		puts "Begin Transaction"
		yield
	rescue
		puts "Abort Transaction"
	else
		puts "Commit Transaction"
	end
end

catch(:ball) do
	transaction do
		throw :ball
	end
end
~~~

In particular, trying to handle this by using `ensure ... unless $!` can fail in some situations due to scope of $!

~~~ ruby
def transaction
	begin
		puts "Begin Transaction"
		yield
	rescue
		puts "Abort Transaction"
	ensure
		puts "Commit Transaction" unless $!
	end
end

catch(:ball) do
	begin
		raise "Problem"
	rescue
		transaction do
			throw :ball
		end
	end
end
~~~

It seems like we must involve some elaborate state tracking in order to get the correct behaviour (calling either abort or commit predictably). So, I believe we should reconsider this feature, or some evolution of it.

----------------------------------------
Feature #15567: Allow ensure to match specific situations
https://bugs.ruby-lang.org/issues/15567#change-92236

* Author: ioquatix (Samuel Williams)
* Status: Rejected
* Priority: Normal
* Assignee: ioquatix (Samuel Williams)
----------------------------------------
There are some situations where `rescue Exception` or `ensure` are not sufficient to correctly, efficiently and easily handle abnormal flow control.

Take the following program for example:

```
def doot
	yield
ensure
	# Did the function run to completion?
	return "abnormal" if $!
end

puts doot{throw :foo}
puts doot{raise "Boom"}
puts doot{"Hello World"}

catch(:foo) do
	puts doot{throw :foo}
end
```

Using `rescue Exception` is not sufficient as it is not invoked by `throw`.

Using `ensure` is inefficient because it's triggered every time, even though exceptional case might never happen or happen very infrequently.

I propose some way to limit the scope of the ensure block:

```
def doot
	yield
ensure when raise, throw
	return "abnormal"
end
```

The scope should be one (or more) of `raise`, `throw`, `return`, `next`, `break`, `redo`, `retry` (everything in `enum ruby_tag_type` except all except for `RUBY_TAG_FATAL`).

Additionally, it might be nice to support the inverted pattern, i.e.

```
def doot
	yield
ensure when not return
	return "abnormal"
end
```

Inverted patterns allow user to specify the behaviour without having problems if future scopes are introduced.

`return` in this case matches both explicit and implicit.




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>