lib/stockpile.rb in stockpile-1.0 vs lib/stockpile.rb in stockpile-1.1
- old
+ new
@@ -1,22 +1,22 @@
# coding: utf-8
require 'forwardable'
-# Stockpile is a thin wrapper around connections to a fast key-value store used
-# for caching (currently only supporting Redis).
+# Stockpile is a thin wrapper around connections to a fast key-value store
+# used for caching (currently only supporting Redis).
#
# This provides a couple of layers of functionality:
#
# * Connection management. Some third-party providers of Redis limit
# simultaneous connections; Stockpile can manage a single connection that
# gets shared by all clients using a Stockpile instance.
# * Providing an application-level cache adapter mechanism.
class Stockpile
extend Forwardable
- VERSION = "1.0" # :nodoc:
+ VERSION = "1.1" # :nodoc:
@default_manager = nil
class << self
# Determines if the default connection width is narrow or wide based on
@@ -77,36 +77,50 @@
unless mod.kind_of?(Module)
raise ArgumentError, "#{mod} is not a class or module"
end
name = options.fetch(:method, :cache).to_sym
- msc = mod.singleton_class
-
+ mklass = mod.singleton_class
default = options.fetch(:default_manager, nil)
- msc.send(:define_method, name) do |init_options = {}|
+ mklass.send(:define_method, name) do |init_options = {}|
init_options = init_options.merge(default_manager: default)
@__stockpile__ ||= ::Stockpile.new(init_options)
+ @__stockpile_triggers__ ||= []
+
+ triggers, @__stockpile_triggers__ = @__stockpile_triggers__, []
+ triggers.each(&:call)
+
+ @__stockpile__
end
if options.fetch(:adaptable, true)
adapter = :"#{name}_adapter"
- msc.send(:define_method, adapter) do |m, k = nil|
+ mklass.send(:define_method, adapter) do |m, k = nil|
o = self
- send(name).singleton_class.send(:include, m)
+ @__stockpile_triggers__ ||= []
+
+ trigger = -> { send(name).singleton_class.send(:include, m) }
+
+ if defined?(@__stockpile__) && @__stockpile__
+ trigger.call
+ else
+ @__stockpile_triggers__ << trigger
+ end
+
if k
mk = k.singleton_class
m.public_instance_methods.each do |pim|
mk.send(:define_method, pim) do |*args, &block|
o.send(name).send(pim, *args, &block)
end
end
end
end
- msc.send(:define_method, :"#{adapter}!") do |m|
+ mklass.send(:define_method, :"#{adapter}!") do |m|
send(adapter, m, m)
end
end
end
@@ -129,41 +143,50 @@
# +default_manager+ or ::Stockpile.default_manager will be used.
# An error will be raised if no connection provider is available
# through any means.
# +clients+:: Connections will be created for the provided list of clients.
# These connections must be assigned to their appropriate clients
- # after initialization. This may also be called +client+.
+ # after initialization. This may also be called +client+. These
+ # values may be provided as names (e.g., +:cache+), or as hashes
+ # of client name to client options (e.g., <tt>{ cache: {
+ # namespace: 'x' } }</tt>). See Stockpile#connect for more
+ # details on this latter format.
#
# All other options will be passed to the connection provider.
#
# === Synopsis
#
- # # Create and assign a connection to Redis.current, Resque, and Rollout.
- # # Under a narrow connection management width, all three will be the
- # # same client connection.
- # Stockpile.new(manager: Stockpile::Redis, clients: [ :redis, :resque ]) do |stockpile|
+ # # Create and assign a connection to Redis.current, Resque, and Rollout.
+ # # Under a narrow connection management width, all three will be the
+ # # same client connection.
+ # options = {
+ # manager: Stockpile::Redis,
+ # clients: [ :redis, :resque ]
+ # }
+ # Stockpile.new(options) do |stockpile|
# Redis.current = stockpile.connection_for(:redis)
# Resque.redis = stockpile.connection_for(:resque)
# # Clients will be created by name if necessary.
# $rollout = Rollout.new(stockpile.connection_for(:rollout))
# end
def initialize(options = {})
- manager = options.fetch(:manager) {
- options[:default_manager] || self.class.default_manager
- }
+ options = options.dup
+ manager = options.delete(:manager)
+ default = options.delete(:default_manager) || self.class.default_manager
- unless manager
+ unless manager || default
raise ArgumentError, "No connection manager provided or set as default."
end
- clients = (Array(options[:clients]) + Array(options[:client])).flatten.uniq
+ manager ||= default
- options = { narrow: Stockpile.narrow? }.merge(options.reject { |k, _|
- k == :manager || k == :clients || k == :client
- })
+ clients = [
+ Array(options.delete(:clients)), Array(options.delete(:client))
+ ].flatten.uniq
- @manager = manager.new(options)
+ @manager = manager.new({ narrow: Stockpile.narrow? }.merge(options))
+
connect(*clients)
yield self if block_given?
end
##
@@ -181,22 +204,60 @@
# This will connect the Stockpile instance to the cache provider, optionally
# including for a set of named clients.
#
# If the connection is using a narrow connection width, the same connection
# will be shared.
+ #
+ # === Clients
+ #
+ # +clients+ may be provided in one of several ways:
+ #
+ # * A Hash object, mapping client names to client options.
+ #
+ # connect(redis: nil, rollout: { namespace: 'rollout' })
+ # # Transforms into:
+ # # connect(redis: {}, rollout: { namespace: 'rollout' })
+ #
+ # * An (implicit) array of client names, for connections with no options
+ # provided.
+ #
+ # connect(:redis, :resque, :rollout)
+ # # Transforms into:
+ # # connect(redis: {}, resque: {}, rollout: {})
+ #
+ # * An array of Hash objects, mapping client names to client options.
+ #
+ # connect({ redis: nil },
+ # { rollout: { namespace: 'rollout' } })
+ # # Transforms into:
+ # # connect(redis: {}, rollout: { namespace: 'rollout' })
+ #
+ # * A mix of client names and Hash objects:
+ #
+ # connect(:redis, { rollout: { namespace: 'rollout' } })
+ # # Transforms into:
+ # # connect(redis: {}, rollout: { namespace: 'rollout' })
+ #
+ # ==== Client Options
+ #
+ # Stockpile cache providers will handle the parsing of options to ensure that
+ # only suitable options are passed along (most providers will ignore any
+ # options that change the target system).
def_delegator :@manager, :connect
##
# :method: connection_for
# :call-seq:
# connection_for(client_name)
+ # connection_for(client_name, options)
#
# Returns a connection for a particular client. If the connection manager is
# using a narrow connection width, this returns the same as #connection.
#
# The +client_name+ of +:all+ will always return +nil+.
#
- # If the requested client does not yet exist, the connection will be created.
+ # If the requested client does not yet exist, the connection will be created
+ # with the provided options.
def_delegator :@manager, :connection_for
##
# :method: reconnect
# :call-seq: