class BatchKit::ResourceManager

Defines a manager for resource types, such as database connections etc. Resource types are registered with this class, which then adds acquisition methods to the ResourceHelper module. These acquisition methods add the acquired objects to a collection managed by the objects of the class that includes the ResourceHelper, and modify the returned resource objects so that they automatically de-register themselves if they are disposed of explicitly.

Public Class Methods

disposal_method(rsrc) click to toggle source

Returns an unbound method object that represents the method that should be called to dispose of rsrc.

# File lib/batch-kit/resources.rb, line 20
def disposal_method(rsrc)
    disp_mthd = resource_types[rsrc.class] || resource_types.find{ |rt, _| rt === rsrc }.last rescue nil
    disp_mthd or raise ArgumentError, "No registered resource class matches '#{rsrc.class}'"
end
register(rsrc_cls, helper_mthd, options = {}, &body) click to toggle source

Register a resource type for automated resource management.

@param rsrc_cls [Class] The class of resource to be managed. This

must be the type of the object that will be returned when an
instance of this resource is acquired.

@param helper_mthd [Symbol] The name of the resource acquisition

helper method that should be added to the ResourceHelper module.

@param options [Hash] An options class. @option options [Symbol] :acquisition_method For cases where an

existing method can be called directly on the +rsrc_cls+ to
obtain a resource (rather than passing in a block containing
resource acquisition steps), the name of that method. Defaults
to :open.

@option options [Symbol] :disposal_method The name of the method

to be called on the resource to dispose of it. Defaults to
:close.
# File lib/batch-kit/resources.rb, line 42
def register(rsrc_cls, helper_mthd, options = {}, &body)
    if ResourceHelper.method_defined?(helper_mthd)
        raise ArgumentError, "Resource acquisition method #{helper_mthd} is already registered"
    end
    unless body
        open_mthd = options.fetch(:acquisition_method, :open)
        body = lambda{ |*args| rsrc_cls.send(open_mthd, *args) }
    end
    disp_mthd = options.fetch(:disposal_method, :close)

    if rsrc_cls.method_defined?(disp_mthd)
        if (m = resource_types[rsrc_cls]) && m.name != disp_mthd
            raise ArgumentError, "Resource class #{rsrc_cls} has already been registered" +
                " with a different disposal method (##{m.name})"
        else
            resource_types[rsrc_cls] = rsrc_cls.instance_method(disp_mthd)
        end
    else
        raise ArgumentError, "No method named '#{disp_mthd}' is defined on #{rsrc_cls}"
    end

    # Define the helper method on the ResourceHelper module. This is
    # necessary (as opposed to just calling the block from the
    # acquisition methd) in order to ensure that self etc are set
    # correctly
    ResourceHelper.class_eval{ define_method(helper_mthd, &body) }

    # Now wrap an aspect around the method to handle the tracking of
    # resources acquired, and event notifications
    add_aspect(rsrc_cls, helper_mthd, disp_mthd)
    Events.publish(self, 'resource.registered', rsrc_cls, helper_mthd)
end

Private Class Methods

add_aspect(rsrc_cls, helper_mthd, disp_mthd) click to toggle source

Define the helper method to acquire a resource, publish events about the resource lifecycle, and track the usage of the resource to ensure we know about unreleased resources and can clean then up at the appropriate time when the owning object is done with them.

# File lib/batch-kit/resources.rb, line 88
def add_aspect(rsrc_cls, helper_mthd, disp_mthd)
    mthd = ResourceHelper.instance_method(helper_mthd)
    ResourceHelper.class_eval do
        define_method helper_mthd do |*args|
            if Events.publish(rsrc_cls, 'resource.pre_acquire', *args)
                result = nil
                begin
                    result = mthd.bind(self).call(*args)
                    unless rsrc_cls === result
                        raise ArgumentError, "Returned resource is of type #{
                            result.class.name}, not #{rsrc_cls}"
                    end
                    # Override disposal method on this acquired instance
                    # to call #dispose_resource instead
                    defn = self
                    result.define_singleton_method(disp_mthd) do
                        defn.dispose_resource(self)
                    end
                    add_resource(result)
                    Events.publish(rsrc_cls, 'resource.acquired', result)
                    result
                rescue Exception => ex
                    Events.publish(rsrc_cls, 'resource.acquisition_failed', ex)
                    raise
                end
            end
        end
    end
end
resource_types() click to toggle source
# File lib/batch-kit/resources.rb, line 79
def resource_types
    @resource_types ||= {}
end