ruby 39 lines · 8 steps

Guaranteeing cleanup with begin/ensure in Ruby

Two small patterns that use ensure to run cleanup code whether the work succeeds or blows up.

Explained by highlit
1module FileResource
2 # Opens a file, yields it to the caller, and guarantees the
3 # handle is closed even if the block raises an exception.
4 def self.open(path, mode = "r")
5 handle = File.open(path, mode)
6 begin
7 yield handle
8 ensure
9 handle.close unless handle.closed?
10 end
11 end
12end
13 
14class Transaction
15 def initialize
16 @committed = false
17 @actions = []
18 end
19 
20 def perform(&action)
21 @actions << action
22 begin
23 action.call
24 yield if block_given?
25 @committed = true
26 ensure
27 rollback unless @committed
28 end
29 end
30 
31 private
32 
33 def rollback
34 @actions.reverse_each do |a|
35 a.respond_to?(:undo) ? a.undo : nil
36 end
37 @actions.clear
38 end
39end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1An ensure clause runs on both normal exit and exception, making it the right home for cleanup.
  2. 2Yielding a resource to a caller's block lets the method own acquisition and release while the caller owns usage.
  3. 3Tracking a success flag lets ensure distinguish a clean finish from a failure that needs undoing.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Guaranteeing cleanup with begin/ensure in Ruby — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code