Class: Flipper::Adapter

Inherits:
Object
  • Object
show all
Defined in:
lib/flipper/adapter.rb

Overview

Internal: Adapter wrapper that wraps vanilla adapter instances. Adds things like local caching and convenience methods for adding/reading features from the adapter.

So what is this local cache crap?

The main goal of the local cache is to prevent multiple queries to an adapter for the same key for a given amount of time (per request, per background job, etc.).

To facilitate with this, there is an included local cache middleware that enables local caching for the length of a web request. The local cache is enabled and cleared before each request and cleared and reset to original value after each request.

Examples

To see an example adapter that this would wrap, checkout the [memory adapter included with flipper](github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/memory.rb).

Constant Summary collapse

InstrumentationName =

Private: The name of instrumentation events.

"adapter_operation.#{InstrumentationNamespace}"
FeaturesKey =

Private: The name of the key that stores the set of known features.

'features'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(adapter, options = {}) ⇒ Adapter

Internal: Initializes a new adapter instance.

adapter - Vanilla adapter instance to wrap. Just needs to respond to

read, write, delete, set_members, set_add, and set_delete.

options - The Hash of options.

:local_cache - Where to store the local cache data (default: {}).
               Must respond to fetch(key, block), delete(key)
               and clear.
:instrumenter - What to use to instrument all the things.


77
78
79
80
81
82
# File 'lib/flipper/adapter.rb', line 77

def initialize(adapter, options = {})
  @adapter = adapter
  @name = adapter.class.name.split('::').last.downcase.to_sym
  @local_cache = options[:local_cache] || {}
  @instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop)
end

Instance Attribute Details

#adapterObject (readonly)

Private: What adapter is being wrapped and will ultimately be used.



55
56
57
# File 'lib/flipper/adapter.rb', line 55

def adapter
  @adapter
end

#instrumenterObject (readonly)

Private: What is used to instrument all the things.



64
65
66
# File 'lib/flipper/adapter.rb', line 64

def instrumenter
  @instrumenter
end

#local_cacheObject (readonly)

Private: What is used to store the local cache.



61
62
63
# File 'lib/flipper/adapter.rb', line 61

def local_cache
  @local_cache
end

#nameObject (readonly)

Private: The name of the adapter. Based on the class name.



58
59
60
# File 'lib/flipper/adapter.rb', line 58

def name
  @name
end

Class Method Details

.wrap(object, options = {}) ⇒ Object

Internal: Wraps vanilla adapter instance for use internally in flipper.

object - Either an instance of Flipper::Adapter or a vanilla adapter instance

Examples

adapter = Flipper::Adapters::Memory.new
instance = Flipper::Adapter.new(adapter)

Flipper::Adapter.wrap(instance)
# => Flipper::Adapter instance

Flipper::Adapter.wrap(adapter)
# => Flipper::Adapter instance

Returns Flipper::Adapter instance



46
47
48
49
50
51
52
# File 'lib/flipper/adapter.rb', line 46

def self.wrap(object, options = {})
  if object.is_a?(Flipper::Adapter)
    object
  else
    new(object, options)
  end
end

Instance Method Details

#delete(key) ⇒ Object

Public: Deletes a key.



108
109
110
# File 'lib/flipper/adapter.rb', line 108

def delete(key)
  perform_delete(:delete, key)
end

#eql?(other) ⇒ Boolean Also known as: ==

Public: Determines equality for an adapter instance when compared to another object.

Returns:

  • (Boolean)


129
130
131
# File 'lib/flipper/adapter.rb', line 129

def eql?(other)
  self.class.eql?(other.class) && adapter == other.adapter
end

#feature_add(name) ⇒ Object

Internal: Adds a known feature to the set of features.



140
141
142
# File 'lib/flipper/adapter.rb', line 140

def feature_add(name)
  set_add(FeaturesKey, name.to_s)
end

#featuresObject

Public: Returns all the features that the adapter knows of.



135
136
137
# File 'lib/flipper/adapter.rb', line 135

def features
  set_members(FeaturesKey)
end

#inspectObject

Public: Pretty string version for debugging.



145
146
147
148
149
150
151
# File 'lib/flipper/adapter.rb', line 145

def inspect
  attributes = [
    "name=#{name.inspect}",
    "use_local_cache=#{@use_local_cache.inspect}"
  ]
  "#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
end

#perform_delete(operation, key) ⇒ Object

Private



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/flipper/adapter.rb', line 193

def perform_delete(operation, key)
  payload = {
    :key => key,
    :operation => operation,
    :adapter_name => @name,
  }

  result = @instrumenter.instrument(InstrumentationName, payload) { |payload|
    payload[:result] = @adapter.send(operation, key)
  }

  if using_local_cache?
    local_cache.delete(key.to_s)
  end

  result
end

#perform_read(operation, key) ⇒ Object

Private



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/flipper/adapter.rb', line 154

def perform_read(operation, key)
  if using_local_cache?
    local_cache.fetch(key.to_s) {
      local_cache[key.to_s] = @adapter.send(operation, key)
    }
  else
    payload = {
      :key => key,
      :operation => operation,
      :adapter_name => @name,
    }

    @instrumenter.instrument(InstrumentationName, payload) { |payload|
      payload[:result] = @adapter.send(operation, key)
    }
  end
end

#perform_update(operation, key, value) ⇒ Object

Private



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/flipper/adapter.rb', line 173

def perform_update(operation, key, value)
  payload = {
    :key => key,
    :value => value,
    :operation => operation,
    :adapter_name => @name,
  }

  result = @instrumenter.instrument(InstrumentationName, payload) { |payload|
    payload[:result] = @adapter.send(operation, key, value)
  }

  if using_local_cache?
    local_cache.delete(key.to_s)
  end

  result
end

#read(key) ⇒ Object

Public: Reads a key.



98
99
100
# File 'lib/flipper/adapter.rb', line 98

def read(key)
  perform_read(:read, key)
end

#set_add(key, value) ⇒ Object

Public: Adds a value to a set.



118
119
120
# File 'lib/flipper/adapter.rb', line 118

def set_add(key, value)
  perform_update(:set_add, key, value.to_s)
end

#set_delete(key, value) ⇒ Object

Public: Deletes a value from a set.



123
124
125
# File 'lib/flipper/adapter.rb', line 123

def set_delete(key, value)
  perform_update(:set_delete, key, value.to_s)
end

#set_members(key) ⇒ Object

Public: Returns the members of a set.



113
114
115
# File 'lib/flipper/adapter.rb', line 113

def set_members(key)
  perform_read(:set_members, key)
end

#use_local_cache=(value) ⇒ Object

Public: Turns local caching on/off.

value - The Boolean that decides if local caching is on.



87
88
89
90
# File 'lib/flipper/adapter.rb', line 87

def use_local_cache=(value)
  local_cache.clear
  @use_local_cache = value
end

#using_local_cache?Boolean

Public: Returns true for using local cache, false for not.

Returns:

  • (Boolean)


93
94
95
# File 'lib/flipper/adapter.rb', line 93

def using_local_cache?
  @use_local_cache == true
end

#write(key, value) ⇒ Object

Public: Set a key to a value.



103
104
105
# File 'lib/flipper/adapter.rb', line 103

def write(key, value)
  perform_update(:write, key, value.to_s)
end