= stockpile code :: https://github.com/halostatue/stockpile/ bugs :: https://github.com/halostatue/stockpile/issues continuous integration :: {}[https://travis-ci.org/halostatue/stockpile] == Description Stockpile is a simple key-value store connection manager framework. Stockpile itself does not implement a connection manager, but places expectations for implemented connection managers. So far, only Redis has been implemented (stockpile-redis). Stockpile also provides an adapter so that its functionality can be accessed from within a module. Release 1.1 fixes an issue with early initialization of an injected Stockpile instance during adaptation ({stockpile#2}[https://githbub.com/halostatue/stockpile/issues/2]). Several small improvements to Stockpile.new, Stockpile#connect, and Stockpile#connection_for have been documented. == Features * Stockpile manages key-value store connections. There are two variants: * *wide* (Stockpile.new, the default), where additional client connections are new instances of the client library; * *narrow* (Stockpile.new(narrow: true)), where additional client connections use the same client library instance. * Stockpile can also be injected into a module (Stockpile.inject!(self, options = {})), which gives the module cache management and adapter methods (.cache and .cache_adapter, by default). == Requirements The desired key-value store must already be installed and/or specified in your Gemfile. == Synopsis wide = Stockpile.new # A Stockpile instance. wide.connection.set('hello', 'world') # => 'OK' wide.connection.get('hello') # => 'world' # Connections are independent from one another. wide.connection_for(:other) != wide.connection # => true # Or set ENV['STOCKPILE_CONNECTION_WDITH'] = 'narrow' narrow = Stockpile.new(narrow: true) # A 'narrow' Stockpile to Redis. narrow.connection_for(:other) == narrow.connection # => true # Special Redis::Namespace handling for Resque. Assumes that redis-namespace # has been installed, as well. narrow.connection_for(:resque) != narrow.connection # => true narrow.connection_for(:resque).redis == narrow.connection # => true # Standard namespace handling. narrow.connection_for(:other, namespace: 'other') != narrow.connection # => true narrow.connection_for(:other, namespace: 'other').redis != narrow.connection # => true # Show a Stockpile with no adapter capabilities, but name the method # stockpile, not cache. This will still usefully manage connections. module Cacher Stockpile.inject!(self, method: :stockpile, adaptable: false) end Cacher.respond_to?(:stockpile) # => true Cacher.respond_to?(:stockpile_adapter) # => false Cacher.stockpile.connection.set('hello', 'world') # => 'OK' Cacher.stockpile.connection.get('hello') # => 'world' # Now a Stockpile with adapter capabilities. module Jobber module LastRunTime def last_run_time(key, value = nil) if value connection.hset(__method__, key, value.utc.iso8601) else value = connection.hget(__method__, key) Time.parse(value) if value end end end Stockpile.inject!(self) end Jobber.respond_to?(:cache) # => true Jobber.respond_to?(:cache_adapter) # => true # Four ways: # 1. Adapt Jobber.cache to recognize #last_run_time. Jobber.cache_adapter(Jobber::LastRunTime) Jobber.cache.last_run_time('hello', t = Time.now) # => true Jobber.cache.last_run_time('hello') # => approximately t # 2. Adapt Jobber.cache and another module to recognize #last_run_time. module Foo; end Jobber.cache_adapter(Jobber::LastRunTime, Foo) Foo.last_run_time('hello', t = Time.now) # => true Foo.last_run_time('hello') # => approximately t # 3. Adapt Jobber.cache and Jobber to recognize #last_run_time. Jobber.cache_adapter(Jobber::LastRunTime, Jobber) Jobber.last_run_time('hello', t = Time.now) # => true Jobber.last_run_time('hello') # => approximately t # 4. Adapt Jobber.cache and Jobber::LastRunTime to recognize #last_run_time. Jobber.cache_adapter!(Jobber::LastRunTime) # or Jobber.cache_adapter(Jobber::LastRunTime, Jobber::LastRunTime) Jobber::LastRunTime.last_run_time('hello', t = Time.now) # => true Jobber::LastRunTime.last_run_time('hello') # => approximately t == Background Stockpile is the evolution of concepts I have applied to Rails applications over the last few years when working with Redis, and avoids the following common but suboptimal patterns: * Developers use +REDIS+ or $redis to initialize and access their Redis instances. This could be fixed by using +Redis.current+, but that still exposes implementation details unnecessarily. * Redis methods are often exposed directly in controllers or models, as render json: $redis.hget('last_run_time', params[:method]) We don’t like seeing direct database access methods in our controllers, so why do we put up with this for Redis? * Each Redis client manages its own connections, and at least one client reconnection is forgotten when using a forking server like Unicorn. * Some providers of Redis services restrict the number of simultaneous connections to a given Redis instance. With Rails caching, an application cache, and Resque there are at least three simultaneous connections to Redis for a given Rails server instancne, unless the same connection is reused. === Sample Rails Application I will be adapting a sample Rails application to demonstrate how Stockpile can be used in Rails. A link to it will be provided here when it is complete. == Install Stockpile is not intended to be installed by itself, as it does not implement a key-value store specific connection manager. Instead, install a store-specific gem which depends on Stockpile. gem 'stockpile-redis', '~> 1.1' Or manually install: % gem install stockpile-redis and require Stockpile in your code: require 'stockpile/redis' == Stockpile Semantic Versioning Stockpile uses a {Semantic Versioning}[http://semver.org/] scheme with one change: * When PATCH is zero (+0+), it will be omitted from version references. :include: Contributing.rdoc :include: Licence.rdoc