1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
require 'set'
require 'redis'
require 'flipper'
module Flipper
module Adapters
class Redis
include ::Flipper::Adapter
# Private: The key that stores the set of known features.
FeaturesKey = :flipper_features
# Public: The name of the adapter.
attr_reader :name
# Public: Initializes a Redis flipper adapter.
#
# client - The Redis client to use. Feel free to namespace it.
def initialize(client)
@client = client
@name = :redis
end
# Public: The set of known features.
def features
read_feature_keys
end
# Public: Adds a feature to the set of known features.
def add(feature)
@client.sadd FeaturesKey, feature.key
true
end
# Public: Removes a feature from the set of known features.
def remove(feature)
@client.multi do
@client.srem FeaturesKey, feature.key
@client.del feature.key
end
true
end
# Public: Clears the gate values for a feature.
def clear(feature)
@client.del feature.key
true
end
# Public: Gets the values for all gates for a given feature.
#
# Returns a Hash of Flipper::Gate#key => value.
def get(feature)
doc = doc_for(feature)
result_for_feature(feature, doc)
end
def get_multi(features)
read_many_features(features)
end
def get_all
features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) }
read_many_features(features)
end
# Public: Enables a gate for a given thing.
#
# feature - The Flipper::Feature for the gate.
# gate - The Flipper::Gate to disable.
# thing - The Flipper::Type being enabled for the gate.
#
# Returns true.
def enable(feature, gate, thing)
case gate.data_type
when :boolean, :integer
@client.hset feature.key, gate.key, thing.value.to_s
when :set
@client.hset feature.key, to_field(gate, thing), 1
else
unsupported_data_type gate.data_type
end
true
end
# Public: Disables a gate for a given thing.
#
# feature - The Flipper::Feature for the gate.
# gate - The Flipper::Gate to disable.
# thing - The Flipper::Type being disabled for the gate.
#
# Returns true.
def disable(feature, gate, thing)
case gate.data_type
when :boolean
@client.del feature.key
when :integer
@client.hset feature.key, gate.key, thing.value.to_s
when :set
@client.hdel feature.key, to_field(gate, thing)
else
unsupported_data_type gate.data_type
end
true
end
private
def read_many_features(features)
docs = docs_for(features)
result = {}
features.zip(docs) do |feature, doc|
result[feature.key] = result_for_feature(feature, doc)
end
result
end
def read_feature_keys
@client.smembers(FeaturesKey).to_set
end
# Private: Gets a hash of fields => values for the given feature.
#
# Returns a Hash of fields => values.
def doc_for(feature)
@client.hgetall(feature.key)
end
def docs_for(features)
@client.pipelined do
features.each do |feature|
doc_for(feature)
end
end
end
def result_for_feature(feature, doc)
result = {}
fields = doc.keys
feature.gates.each do |gate|
result[gate.key] =
case gate.data_type
when :boolean, :integer
doc[gate.key.to_s]
when :set
fields_to_gate_value fields, gate
else
unsupported_data_type gate.data_type
end
end
result
end
# Private: Converts gate and thing to hash key.
def to_field(gate, thing)
"#{gate.key}/#{thing.value}"
end
# Private: Returns a set of values given an array of fields and a gate.
#
# Returns a Set of the values enabled for the gate.
def fields_to_gate_value(fields, gate)
regex = %r{^#{Regexp.escape(gate.key.to_s)}/}
keys = fields.grep(regex)
values = keys.map { |key| key.split('/', 2).last }
values.to_set
end
# Private
def unsupported_data_type(data_type)
raise "#{data_type} is not supported by this adapter"
end
end
end
end
|