RAII in Ruby (Or, How to Manage Resources in Ruby)

destructor, finalizer, raii, resources, ruby

Solution

So that users don't "have to remember to do the whole begin-rescue-ensure chacha" combine `rescue`/`ensure` with `yield`.

class SomeResource
  ...
  def SomeResource.use(*resource_args)
    # create resource
    resource = SomeResource.new(*resource_args) # pass args direct to constructor
    # export it
    yield resource
  rescue
    # known error processing
    ...
  ensure
    # close up when done even if unhandled exception thrown from block
    resource.close
  end
  ...
end

Client code can use it as follows:

SomeResource.use(connection_string) do | resource |
  resource.do_something
  ... # whatever else
end
# after this point resource has been .close()d

In fact this is how `File.open` operates - making the first answer confusing at best (well it was to my work colleagues).

File.open("testfile") do |f|
  # .. process - may include throwing exceptions
end
# f is guaranteed closed after this point even if exceptions are 
# thrown during processing

Problem

I know it's by design that you can't control what happens when an object is destroyed. I am also aware of defining some class method as a finalizer. However is the ruby idiom for C++'s RAII (Resources are initialized in constructor, closed in destructor)? How do people manage resources used inside objects even when errors or exceptions happen? Using ensure works: ``` f = File.open("testfile") begin # .. process rescue # .. handle error ensure f.close unless f.nil? end ``` but users of the class have to remember to do the whole begin-rescue-ensure chacha everytime the open method needs to be called. So for example, I'll have the following class: ``` class SomeResource def initialize(connection_string) @resource_handle = ...some mojo here... end def do_something() begin @resource_handle.do_that() ... rescue ... ensure end def close @resource_handle.close end end ``` The resource_handle won't be closed if the exception is cause by some other class and the script exits. Or is the problem more of I'm still doing this too C++-like?

Original source