flipper 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|