flipper 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Guardfile +3 -8
- data/README.md +26 -38
- data/examples/percentage_of_actors.rb +17 -12
- data/examples/percentage_of_random.rb +3 -7
- data/lib/flipper.rb +8 -1
- data/lib/flipper/adapter.rb +2 -208
- data/lib/flipper/adapters/decorator.rb +9 -0
- data/lib/flipper/adapters/instrumented.rb +92 -0
- data/lib/flipper/adapters/memoizable.rb +88 -0
- data/lib/flipper/adapters/memory.rb +89 -7
- data/lib/flipper/adapters/operation_logger.rb +31 -45
- data/lib/flipper/decorator.rb +6 -0
- data/lib/flipper/dsl.rb +29 -2
- data/lib/flipper/feature.rb +83 -49
- data/lib/flipper/gate.rb +24 -41
- data/lib/flipper/gates/actor.rb +24 -24
- data/lib/flipper/gates/boolean.rb +28 -15
- data/lib/flipper/gates/group.rb +25 -34
- data/lib/flipper/gates/percentage_of_actors.rb +21 -13
- data/lib/flipper/gates/percentage_of_random.rb +20 -12
- data/lib/flipper/instrumentation/log_subscriber.rb +14 -22
- data/lib/flipper/middleware/memoizer.rb +23 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +141 -92
- data/lib/flipper/types/boolean.rb +5 -1
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/instrumented_spec.rb +92 -0
- data/spec/flipper/adapters/memoizable_spec.rb +184 -0
- data/spec/flipper/adapters/memory_spec.rb +1 -11
- data/spec/flipper/adapters/operation_logger_spec.rb +93 -0
- data/spec/flipper/dsl_spec.rb +18 -43
- data/spec/flipper/feature_spec.rb +25 -9
- data/spec/flipper/gate_spec.rb +8 -20
- data/spec/flipper/gates/actor_spec.rb +6 -14
- data/spec/flipper/gates/boolean_spec.rb +80 -13
- data/spec/flipper/gates/group_spec.rb +8 -18
- data/spec/flipper/gates/percentage_of_actors_spec.rb +12 -28
- data/spec/flipper/gates/percentage_of_random_spec.rb +6 -14
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +15 -8
- data/spec/flipper/instrumentation/metriks_subscriber_spec.rb +3 -6
- data/spec/flipper/middleware/{local_cache_spec.rb → memoizer_spec.rb} +25 -55
- data/spec/flipper/types/boolean_spec.rb +13 -3
- data/spec/flipper_spec.rb +7 -0
- data/spec/helper.rb +21 -3
- data/spec/integration_spec.rb +115 -116
- metadata +17 -27
- data/lib/flipper/adapters/memoized.rb +0 -55
- data/lib/flipper/key.rb +0 -38
- data/lib/flipper/middleware/local_cache.rb +0 -36
- data/lib/flipper/toggle.rb +0 -54
- data/lib/flipper/toggles/boolean.rb +0 -54
- data/lib/flipper/toggles/set.rb +0 -25
- data/lib/flipper/toggles/value.rb +0 -25
- data/spec/flipper/adapter_spec.rb +0 -463
- data/spec/flipper/adapters/memoized_spec.rb +0 -93
- data/spec/flipper/key_spec.rb +0 -23
- data/spec/flipper/toggle_spec.rb +0 -22
- data/spec/flipper/toggles/boolean_spec.rb +0 -40
- data/spec/flipper/toggles/set_spec.rb +0 -35
- data/spec/flipper/toggles/value_spec.rb +0 -55
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'flipper/adapters/decorator'
|
2
|
+
|
3
|
+
module Flipper
|
4
|
+
module Adapters
|
5
|
+
class Memoizable < Decorator
|
6
|
+
FeaturesKey = :flipper_features
|
7
|
+
|
8
|
+
# Internal
|
9
|
+
def self.cache
|
10
|
+
Thread.current[:flipper_memoize_cache] ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Internal
|
14
|
+
def self.memoizing?
|
15
|
+
!!Thread.current[:flipper_memoize]
|
16
|
+
end
|
17
|
+
|
18
|
+
# Internal
|
19
|
+
def self.memoize=(value)
|
20
|
+
cache.clear
|
21
|
+
Thread.current[:flipper_memoize] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public
|
25
|
+
def initialize(adapter)
|
26
|
+
super(adapter)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public
|
30
|
+
def get(feature)
|
31
|
+
if memoizing?
|
32
|
+
cache.fetch(feature) { cache[feature] = super }
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public
|
39
|
+
def enable(feature, gate, thing)
|
40
|
+
result = super
|
41
|
+
cache.delete(feature) if memoizing?
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public
|
46
|
+
def disable(feature, gate, thing)
|
47
|
+
result = super
|
48
|
+
cache.delete(feature) if memoizing?
|
49
|
+
result
|
50
|
+
end
|
51
|
+
|
52
|
+
# Public
|
53
|
+
def features
|
54
|
+
if memoizing?
|
55
|
+
cache.fetch(FeaturesKey) {
|
56
|
+
cache[FeaturesKey] = super
|
57
|
+
}
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Public
|
64
|
+
def add(feature)
|
65
|
+
result = super
|
66
|
+
cache.delete(FeaturesKey) if memoizing?
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
# Internal
|
71
|
+
def cache
|
72
|
+
self.class.cache
|
73
|
+
end
|
74
|
+
|
75
|
+
# Internal: Turns local caching on/off.
|
76
|
+
#
|
77
|
+
# value - The Boolean that decides if local caching is on.
|
78
|
+
def memoize=(value)
|
79
|
+
self.class.memoize = value
|
80
|
+
end
|
81
|
+
|
82
|
+
# Internal: Returns true for using local cache, false for not.
|
83
|
+
def memoizing?
|
84
|
+
self.class.memoizing?
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -3,46 +3,128 @@ require 'set'
|
|
3
3
|
module Flipper
|
4
4
|
module Adapters
|
5
5
|
class Memory
|
6
|
+
include Flipper::Adapter
|
7
|
+
|
8
|
+
FeaturesKey = :flipper_features
|
9
|
+
|
10
|
+
# Public: The name of the adapter.
|
11
|
+
attr_reader :name
|
12
|
+
|
6
13
|
# Public
|
7
14
|
def initialize(source = nil)
|
8
15
|
@source = source || {}
|
16
|
+
@name = :memory
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public
|
20
|
+
def get(feature)
|
21
|
+
result = {}
|
22
|
+
|
23
|
+
feature.gates.each do |gate|
|
24
|
+
result[gate.key] = case gate.data_type
|
25
|
+
when :boolean, :integer
|
26
|
+
read key(feature, gate)
|
27
|
+
when :set
|
28
|
+
set_members key(feature, gate)
|
29
|
+
else
|
30
|
+
raise "#{gate} is not supported by this adapter yet"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
result
|
9
35
|
end
|
10
36
|
|
11
37
|
# Public
|
38
|
+
def enable(feature, gate, thing)
|
39
|
+
case gate.data_type
|
40
|
+
when :boolean, :integer
|
41
|
+
write key(feature, gate), thing.value.to_s
|
42
|
+
when :set
|
43
|
+
set_add key(feature, gate), thing.value.to_s
|
44
|
+
else
|
45
|
+
raise "#{gate} is not supported by this adapter yet"
|
46
|
+
end
|
47
|
+
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Public
|
52
|
+
def disable(feature, gate, thing)
|
53
|
+
case gate.data_type
|
54
|
+
when :boolean
|
55
|
+
feature.gates.each do |gate|
|
56
|
+
delete key(feature, gate)
|
57
|
+
end
|
58
|
+
when :integer
|
59
|
+
write key(feature, gate), thing.value.to_s
|
60
|
+
when :set
|
61
|
+
set_delete key(feature, gate), thing.value.to_s
|
62
|
+
else
|
63
|
+
raise "#{gate} is not supported by this adapter yet"
|
64
|
+
end
|
65
|
+
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public: Adds a feature to the set of known features.
|
70
|
+
def add(feature)
|
71
|
+
features.add(feature.name.to_s)
|
72
|
+
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
# Public: The set of known features.
|
77
|
+
def features
|
78
|
+
set_members(FeaturesKey)
|
79
|
+
end
|
80
|
+
|
81
|
+
def inspect
|
82
|
+
attributes = [
|
83
|
+
"name=:memory",
|
84
|
+
"source=#{@source.inspect}",
|
85
|
+
]
|
86
|
+
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
87
|
+
end
|
88
|
+
|
89
|
+
# private
|
90
|
+
def key(feature, gate)
|
91
|
+
"#{feature.key}/#{gate.key}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Private
|
12
95
|
def read(key)
|
13
96
|
@source[key.to_s]
|
14
97
|
end
|
15
98
|
|
16
|
-
#
|
99
|
+
# Private
|
17
100
|
def write(key, value)
|
18
101
|
@source[key.to_s] = value.to_s
|
19
102
|
end
|
20
103
|
|
21
|
-
#
|
104
|
+
# Private
|
22
105
|
def delete(key)
|
23
106
|
@source.delete(key.to_s)
|
24
107
|
end
|
25
108
|
|
26
|
-
#
|
109
|
+
# Private
|
27
110
|
def set_add(key, value)
|
28
111
|
ensure_set_initialized(key)
|
29
112
|
@source[key.to_s].add(value.to_s)
|
30
113
|
end
|
31
114
|
|
32
|
-
#
|
115
|
+
# Private
|
33
116
|
def set_delete(key, value)
|
34
117
|
ensure_set_initialized(key)
|
35
118
|
@source[key.to_s].delete(value.to_s)
|
36
119
|
end
|
37
120
|
|
38
|
-
#
|
121
|
+
# Private
|
39
122
|
def set_members(key)
|
40
123
|
ensure_set_initialized(key)
|
41
124
|
@source[key.to_s]
|
42
125
|
end
|
43
126
|
|
44
|
-
|
45
|
-
|
127
|
+
# Private
|
46
128
|
def ensure_set_initialized(key)
|
47
129
|
@source[key.to_s] ||= Set.new
|
48
130
|
end
|
@@ -1,61 +1,47 @@
|
|
1
|
+
require 'flipper/adapters/decorator'
|
2
|
+
|
1
3
|
module Flipper
|
2
4
|
module Adapters
|
3
5
|
# Public: Adapter that wraps another adapter and stores the operations.
|
4
6
|
#
|
5
|
-
# Useful in tests to verify calls and such.
|
6
|
-
class OperationLogger
|
7
|
+
# Useful in tests to verify calls and such. Never use outside of testing.
|
8
|
+
class OperationLogger < Decorator
|
9
|
+
Operation = Struct.new(:type, :args)
|
10
|
+
|
11
|
+
OperationTypes = [
|
12
|
+
:get,
|
13
|
+
:add,
|
14
|
+
:enable,
|
15
|
+
:disable,
|
16
|
+
:features
|
17
|
+
]
|
18
|
+
|
19
|
+
# Internal: An array of the operations that have happened.
|
7
20
|
attr_reader :operations
|
8
21
|
|
9
|
-
Read = Struct.new(:key)
|
10
|
-
Write = Struct.new(:key, :value)
|
11
|
-
Delete = Struct.new(:key)
|
12
|
-
SetAdd = Struct.new(:key, :value)
|
13
|
-
SetDelete = Struct.new(:key, :value)
|
14
|
-
SetMember = Struct.new(:key)
|
15
|
-
|
16
|
-
# Public
|
17
|
-
def initialize(adapter)
|
18
|
-
@operations = []
|
19
|
-
@adapter = adapter
|
20
|
-
end
|
21
|
-
|
22
|
-
# Public
|
23
|
-
def read(key)
|
24
|
-
@operations << Read.new(key.to_s)
|
25
|
-
@adapter.read key
|
26
|
-
end
|
27
|
-
|
28
22
|
# Public
|
29
|
-
def
|
30
|
-
|
31
|
-
@
|
23
|
+
def initialize(adapter, operations = nil)
|
24
|
+
super(adapter)
|
25
|
+
@operations = operations || []
|
32
26
|
end
|
33
27
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
def set_add(key, value)
|
42
|
-
@operations << SetAdd.new(key.to_s, value)
|
43
|
-
@adapter.set_add key, value
|
28
|
+
OperationTypes.each do |type|
|
29
|
+
class_eval <<-EOE
|
30
|
+
def #{type}(*args)
|
31
|
+
@operations << Operation.new(:#{type}, args)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
EOE
|
44
35
|
end
|
45
36
|
|
46
|
-
# Public
|
47
|
-
def
|
48
|
-
@operations
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# Public
|
53
|
-
def set_members(key)
|
54
|
-
@operations << SetMembers.new(key.to_s)
|
55
|
-
@adapter.set_members key
|
37
|
+
# Public: Count the number of times a certain operation happened.
|
38
|
+
def count(type)
|
39
|
+
@operations.select { |operation|
|
40
|
+
operation.type == type
|
41
|
+
}.size
|
56
42
|
end
|
57
43
|
|
58
|
-
# Public:
|
44
|
+
# Public: Resets the operation log to empty
|
59
45
|
def reset
|
60
46
|
@operations.clear
|
61
47
|
end
|
data/lib/flipper/dsl.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require 'flipper/
|
1
|
+
require 'flipper/adapters/instrumented'
|
2
|
+
require 'flipper/adapters/memoizable'
|
2
3
|
require 'flipper/instrumenters/noop'
|
3
4
|
|
4
5
|
module Flipper
|
@@ -16,7 +17,13 @@ module Flipper
|
|
16
17
|
# :instrumenter - What should be used to instrument all the things.
|
17
18
|
def initialize(adapter, options = {})
|
18
19
|
@instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop)
|
19
|
-
|
20
|
+
|
21
|
+
instrumented = Flipper::Adapters::Instrumented.new(adapter, {
|
22
|
+
:instrumenter => @instrumenter,
|
23
|
+
})
|
24
|
+
memoized = Flipper::Adapters::Memoizable.new(instrumented)
|
25
|
+
@adapter = memoized
|
26
|
+
|
20
27
|
@memoized_features = {}
|
21
28
|
end
|
22
29
|
|
@@ -56,6 +63,10 @@ module Flipper
|
|
56
63
|
#
|
57
64
|
# Returns an instance of Flipper::Feature.
|
58
65
|
def feature(name)
|
66
|
+
if !name.is_a?(String) && !name.is_a?(Symbol)
|
67
|
+
raise ArgumentError, "#{name} must be a String or Symbol"
|
68
|
+
end
|
69
|
+
|
59
70
|
@memoized_features[name.to_sym] ||= Feature.new(name, @adapter, {
|
60
71
|
:instrumenter => instrumenter,
|
61
72
|
})
|
@@ -68,6 +79,22 @@ module Flipper
|
|
68
79
|
# Returns an instance of Flipper::Feature.
|
69
80
|
alias_method :[], :feature
|
70
81
|
|
82
|
+
# Public: Shortcut for getting a boolean type instance.
|
83
|
+
#
|
84
|
+
# value - The true or false value for the boolean.
|
85
|
+
#
|
86
|
+
# Returns a Flipper::Types::Boolean instance.
|
87
|
+
def boolean(value = true)
|
88
|
+
Types::Boolean.new(value)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: Event shorter shortcut for getting a boolean type instance.
|
92
|
+
#
|
93
|
+
# value - The true or false value for the boolean.
|
94
|
+
#
|
95
|
+
# Returns a Flipper::Types::Boolean instance.
|
96
|
+
alias_method :bool, :boolean
|
97
|
+
|
71
98
|
# Public: Access a flipper group by name.
|
72
99
|
#
|
73
100
|
# name - The String or Symbol name of the feature.
|
data/lib/flipper/feature.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
require 'flipper/adapter'
|
2
1
|
require 'flipper/errors'
|
3
2
|
require 'flipper/type'
|
4
|
-
require 'flipper/toggle'
|
5
3
|
require 'flipper/gate'
|
6
4
|
require 'flipper/instrumenters/noop'
|
7
5
|
|
@@ -13,6 +11,9 @@ module Flipper
|
|
13
11
|
# Internal: The name of the feature.
|
14
12
|
attr_reader :name
|
15
13
|
|
14
|
+
# Internal: Name converted to value safe for adapter.
|
15
|
+
attr_reader :key
|
16
|
+
|
16
17
|
# Private: The adapter this feature should use.
|
17
18
|
attr_reader :adapter
|
18
19
|
|
@@ -29,29 +30,36 @@ module Flipper
|
|
29
30
|
#
|
30
31
|
def initialize(name, adapter, options = {})
|
31
32
|
@name = name
|
33
|
+
@key = name.to_s
|
32
34
|
@instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop)
|
33
|
-
@adapter =
|
35
|
+
@adapter = adapter
|
34
36
|
end
|
35
37
|
|
36
38
|
# Public: Enable this feature for something.
|
37
39
|
#
|
38
40
|
# Returns the result of Flipper::Gate#enable.
|
39
|
-
def enable(thing = Types::Boolean.new)
|
41
|
+
def enable(thing = Types::Boolean.new(true))
|
40
42
|
instrument(:enable, thing) { |payload|
|
43
|
+
adapter.add self
|
44
|
+
|
41
45
|
gate = gate_for(thing)
|
42
46
|
payload[:gate_name] = gate.name
|
43
|
-
|
47
|
+
|
48
|
+
adapter.enable self, gate, gate.wrap(thing)
|
44
49
|
}
|
45
50
|
end
|
46
51
|
|
47
52
|
# Public: Disable this feature for something.
|
48
53
|
#
|
49
54
|
# Returns the result of Flipper::Gate#disable.
|
50
|
-
def disable(thing = Types::Boolean.new)
|
55
|
+
def disable(thing = Types::Boolean.new(false))
|
51
56
|
instrument(:disable, thing) { |payload|
|
57
|
+
adapter.add self
|
58
|
+
|
52
59
|
gate = gate_for(thing)
|
53
60
|
payload[:gate_name] = gate.name
|
54
|
-
|
61
|
+
|
62
|
+
adapter.disable self, gate, gate.wrap(thing)
|
55
63
|
}
|
56
64
|
end
|
57
65
|
|
@@ -60,7 +68,11 @@ module Flipper
|
|
60
68
|
# Returns true if enabled, false if not.
|
61
69
|
def enabled?(thing = nil)
|
62
70
|
instrument(:enabled?, thing) { |payload|
|
63
|
-
|
71
|
+
gate_values = adapter.get(self)
|
72
|
+
|
73
|
+
gate = gates.detect { |gate|
|
74
|
+
gate.open?(thing, gate_values[gate.key])
|
75
|
+
}
|
64
76
|
|
65
77
|
if gate.nil?
|
66
78
|
false
|
@@ -71,19 +83,71 @@ module Flipper
|
|
71
83
|
}
|
72
84
|
end
|
73
85
|
|
86
|
+
# Public
|
87
|
+
def state
|
88
|
+
gate_values = adapter.get(self)
|
89
|
+
boolean_value = gate_values[:boolean]
|
90
|
+
|
91
|
+
if boolean_gate.enabled?(boolean_value)
|
92
|
+
:on
|
93
|
+
elsif conditional_gates(gate_values).any?
|
94
|
+
:conditional
|
95
|
+
else
|
96
|
+
:off
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Public
|
101
|
+
def description
|
102
|
+
gate_values = adapter.get(self)
|
103
|
+
boolean_value = gate_values[:boolean]
|
104
|
+
conditional_gates = conditional_gates(gate_values)
|
105
|
+
|
106
|
+
if boolean_gate.enabled?(boolean_value)
|
107
|
+
boolean_gate.description(boolean_value).capitalize
|
108
|
+
elsif conditional_gates.any?
|
109
|
+
fragments = conditional_gates.map { |gate|
|
110
|
+
value = gate_values[gate.key]
|
111
|
+
gate.description(value)
|
112
|
+
}
|
113
|
+
|
114
|
+
"Enabled for #{fragments.join(', ')}"
|
115
|
+
else
|
116
|
+
boolean_gate.description(boolean_value).capitalize
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Public: Pretty string version for debugging.
|
121
|
+
def inspect
|
122
|
+
attributes = [
|
123
|
+
"name=#{name.inspect}",
|
124
|
+
"state=#{state.inspect}",
|
125
|
+
"description=#{description.inspect}",
|
126
|
+
"adapter=#{adapter.name.inspect}",
|
127
|
+
]
|
128
|
+
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
129
|
+
end
|
130
|
+
|
74
131
|
# Internal: Gates to check to see if feature is enabled/disabled
|
75
132
|
#
|
76
133
|
# Returns an array of gates
|
77
134
|
def gates
|
78
135
|
@gates ||= [
|
79
|
-
Gates::Boolean.new(
|
80
|
-
Gates::Group.new(
|
81
|
-
Gates::Actor.new(
|
82
|
-
Gates::PercentageOfActors.new(
|
83
|
-
Gates::PercentageOfRandom.new(
|
136
|
+
Gates::Boolean.new(@name, :instrumenter => @instrumenter),
|
137
|
+
Gates::Group.new(@name, :instrumenter => @instrumenter),
|
138
|
+
Gates::Actor.new(@name, :instrumenter => @instrumenter),
|
139
|
+
Gates::PercentageOfActors.new(@name, :instrumenter => @instrumenter),
|
140
|
+
Gates::PercentageOfRandom.new(@name, :instrumenter => @instrumenter),
|
84
141
|
]
|
85
142
|
end
|
86
143
|
|
144
|
+
# Internal: Finds a gate by name.
|
145
|
+
#
|
146
|
+
# Returns a Flipper::Gate if found, nil if not.
|
147
|
+
def gate(name)
|
148
|
+
gates.detect { |gate| gate.name == name.to_sym }
|
149
|
+
end
|
150
|
+
|
87
151
|
# Internal: Find the gate that protects a thing.
|
88
152
|
#
|
89
153
|
# thing - The object for which you would like to find a gate
|
@@ -95,42 +159,9 @@ module Flipper
|
|
95
159
|
raise(GateNotFound.new(thing))
|
96
160
|
end
|
97
161
|
|
98
|
-
# Public: Pretty string version for debugging.
|
99
|
-
def inspect
|
100
|
-
attributes = [
|
101
|
-
"name=#{name.inspect}",
|
102
|
-
"state=#{state.inspect}",
|
103
|
-
"adapter=#{adapter.name.inspect}",
|
104
|
-
]
|
105
|
-
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
106
|
-
end
|
107
|
-
|
108
|
-
# Public
|
109
|
-
def state
|
110
|
-
if boolean_gate.enabled?
|
111
|
-
:on
|
112
|
-
elsif conditional_gates.any?
|
113
|
-
:conditional
|
114
|
-
else
|
115
|
-
:off
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Public
|
120
|
-
def description
|
121
|
-
if boolean_gate.enabled?
|
122
|
-
boolean_gate.description.capitalize
|
123
|
-
elsif conditional_gates.any?
|
124
|
-
fragments = conditional_gates.map(&:description)
|
125
|
-
"Enabled for #{fragments.join(', ')}"
|
126
|
-
else
|
127
|
-
boolean_gate.description.capitalize
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
162
|
# Private
|
132
163
|
def boolean_gate
|
133
|
-
@boolean_gate ||=
|
164
|
+
@boolean_gate ||= gate(:boolean)
|
134
165
|
end
|
135
166
|
|
136
167
|
# Private
|
@@ -139,8 +170,11 @@ module Flipper
|
|
139
170
|
end
|
140
171
|
|
141
172
|
# Private
|
142
|
-
def conditional_gates
|
143
|
-
@conditional_gates ||= non_boolean_gates.select { |gate|
|
173
|
+
def conditional_gates(gate_values)
|
174
|
+
@conditional_gates ||= non_boolean_gates.select { |gate|
|
175
|
+
value = gate_values[gate.key]
|
176
|
+
gate.enabled?(value)
|
177
|
+
}
|
144
178
|
end
|
145
179
|
|
146
180
|
# Private
|