Class: Stockpile

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/stockpile.rb

Overview

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.

Constant Summary collapse

VERSION =

:nodoc:

"1.0"

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) {|_self| ... } ⇒ Stockpile

Creates a new Stockpile instance and connects to the connection provider using the provided options and block.

Options

The options hash contains configuration for the Stockpile and its connection manager. The following options are handled specially by the Stockpile constructor and not made available to the connection provider constructor.

manager

The connection manager that will be used for creating connections to this Stockpile. If not provided, either 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.

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|
  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

Yields:

  • (_self)

Yield Parameters:

  • _self (Stockpile)

    the object that the method was called on



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/stockpile.rb', line 149

def initialize(options = {})
  manager = options.fetch(:manager) {
    options[:default_manager] || self.class.default_manager
  }

  unless manager
    raise ArgumentError, "No connection manager provided or set as default."
  end

  clients = (Array(options[:clients]) + Array(options[:client])).flatten.uniq

  options = { narrow: Stockpile.narrow? }.merge(options.reject { |k, _|
    k == :manager || k == :clients || k == :client
  })

  @manager = manager.new(options)
  connect(*clients)
  yield self if block_given?
end

Class Attribute Details

.default_managerObject

The default Stockpile cache connection manager.



114
115
116
# File 'lib/stockpile.rb', line 114

def default_manager
  @default_manager
end

Class Method Details

.inject!(mod, options = {}) ⇒ Object

Enables module or class mod to contain a Stockpile instance and provide an adapter interface (this can be disabled). This creates a singleton method that returns a singleton Stockpile instance.

Options

method

The name of the method that manages the Stockpile instance. Defaults to cache.

adaptable

Defines an adapter method if truthy (the default). Pass a falsy value to disable. (The created adapter method will be named according to the value of method, and so defaults to cache_adapter.

Synopsis

# Using only for connection management.
module Application
  Stockpile.inject!(self, adaptable: false)
end
Application.cache # => a stockpile instance
Application.cache.connection.set('answer', 42)
Application.cache.connection.get('answer')

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

module AdaptableApplication; end
Stockpile.inject!(AdaptableApplication)

# Adapt the cache object to recognize #last_run_time;
AdaptableApplication.cache_adapter(LastRunTime)
AdaptableApplication.cache.last_run_time('adaptable_application')

# or adapt AdaptableApplication to recognize #last_run_time;
AdaptableApplication.cache_adapter(LastRunTime,
                                   AdaptableApplication)
AdaptableApplication.last_run_time('adaptable_application')

# or adapt LastRunTime to recognize #last_run_time.
AdaptableApplication.cache_adapter!(LastRunTime)
LastRunTime.last_run_time('adaptable_application')


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/stockpile.rb', line 76

def inject!(mod, options = {})
  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

  default = options.fetch(:default_manager, nil)

  msc.send(:define_method, name) do |init_options = {}|
    init_options = init_options.merge(default_manager: default)
    @__stockpile__ ||= ::Stockpile.new(init_options)
  end

  if options.fetch(:adaptable, true)
    adapter = :"#{name}_adapter"
    msc.send(:define_method, adapter) do |m, k = nil|
      o = self
      send(name).singleton_class.send(:include, m)

      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|
      send(adapter, m, m)
    end
  end
end

.narrow?Boolean

Determines if the default connection width is narrow or wide based on the environment variable STOCKPILE_CONNECTION_WIDTH.

Returns:

  • (Boolean)


24
25
26
# File 'lib/stockpile.rb', line 24

def narrow?
  (ENV['STOCKPILE_CONNECTION_WIDTH'] == 'narrow')
end