with_resources: Add "with" statement in your Ruby script
# gem install with_resources
require "with_resources"
WithResourecs.with(->(){
sock = TCPSocket.open("dest.example.com", port)
httpclient = MyHTTPClient.new(sock)
}) do |sock, httpclient|
# ...
# httpclient.close # will be called automatically
# sock.close
end
require "with_resources/toplevel"
using WithResources::TopLevel
# it makes "with" available in this file
with(->(){ a = One.new; b = Another.new(a) }) do |a, b|
# ...
end
# or enable everywhere! (DANGER!)
require "with_resources/kernel_ext"This gem provides a feature to allocate/release resource objects safely. This feature is widely known as 'try-with-resources' (Java), 'with' statement (Python), 'using' statement (C#) and many others.
WithResources.with method does:
- accept a lambda argument to allocate resources
- accept a block to be called
- release allocated resources automatically after block is processed in reverse order of allocated order
All allocated resources will be released even when any errors are raised in block, in obj.close or in allocating another resources.
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.
API
WithResources.with(lambda_to_allocate, release_method: :close, &block)
All values assigned into local variable in lambda_to_allocate will be passed to block as block arguments. (Don't re-assign values into same local variable, neither undefine local variable, in that labmda.)
It can accept release_method keyword argument to specify the method name to release resources. The specified method will be called in release stage, without any arguments. It's impossible to specify different method names for resources.
Introduce with to top-level namespace
Top level with is available via 2 different ways. One is using Refinements, another is modifying Kernel in open-class way.
require "with_resources/toplevel"
using WithResources::TopLevel
with(->(){ r = create_resource() }) do |r|
# ...
endRefinements is a feature of Ruby to apply Module modification in just a file (by using statement).
using WithResources::TopLevel introduces top level with in safer way than modifying Kernel.
require "with_resource/kernel_ext"
# now, "with" is available everywhere...Requiring with_resource/kernel_ext modifies Kernel module globally to add with. It's not recommended in most cases.
Performance
Because of some magical hacks, with has performance overhead.
The benchmark score below shows the difference of performances between standard begin-ensure and with (elasped seconds by 10,000 times). Benchmark script is available at misc/bench.rb.
user system total real
begin 0.040000 0.000000 0.040000 ( 0.037753)
with 1.400000 0.010000 1.410000 ( 1.423388)
It's not so huge overhead in actual application, but it might be better not to use with in heavy loops.
Authors
- Satoshi Tagomori tagomoris@gmail.com
License
MIT (See License.txt)