Skip to content
Golang-style defer in Ruby
Ruby
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib
test
.gitignore
.travis.yml
Gemfile
LICENSE.txt
README.md
Rakefile
deferral.gemspec

README.md

Deferral - introduce golang style "defer" in Ruby

# gem install deferral

require "deferral/toplevel"
using Deferral::TopLevel
# it makes "defer" without module name available everywhere in this file

def my_method_name
  # ...
  file = File.open("my_file", "r")
  defer { file.close }

  file.write "data..."
  # ...
end # `file.close` is called at the end of method (even when exception thrown)

# or end of blocks
def my_method_name(list)
  # ...
  list.each do |item|
    file = File.open(item, "r")
    defer { file.close }

    file.write "data..."
    # ...
  end # `file.close` is called at the end of block
end

# "deferral" imports `Deferral.defer`, not to inject top-level methods widely.
require "deferral"

# or enable everywhere! (DANGER!)
require "deferral/kernel_ext"

This gem provides a feature to release resources safely, using golang style defer method.

Deferral.defer method does:

  • accept a block argument to execute at the end of caller scope
  • execute specified blocks in reverse order when getting out from specified scope

Resource release blocks are called even when any exception occurs.

See also with_resources gem for try-with-resources style resource allocator.

Disclosure

This library is a kind of PoC to introduce safe resource allocation in Ruby world. Take care about using this library in your production environment.

This library uses/enables TracePoint, and it may collapse optimizations of your Ruby runtime.

API

  • Deferral.defer(&block)

This method registers a block to be called at the end of caller scope (end of method or block). Registered blocks will be called in reverse order (LIFO: last-in, first-out) when this method is called twice or more in a scope.

Introduce defer to top-level namespace

Top-level defer is available via 2 different ways. One is using Refinements, another is modifying Kernel in open-class way.

require "deferral/toplevel"
using Deferral::TopLevel

def my_method
  f = AnyResource.new
  defer { f.close }
  # ...
end

Refinements is a feature of Ruby to apply Module modification in just a file (by using statement). using Deferral::TopLevel introduces top level defer in safer way than modifying Kernel.

require "deferral/kernel_ext"

# now, "defer" is available everywhere...

Requiring deferral/kernel_ext modifies Kernel module globally to add deferral. It's not recommended in most cases.


Authors

License

MIT (See License.txt)

You can’t perform that action at this time.