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
data/lib/flipper/gate.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'forwardable'
|
2
|
-
require 'flipper/key'
|
3
2
|
require 'flipper/instrumenters/noop'
|
4
3
|
|
5
4
|
module Flipper
|
@@ -10,16 +9,14 @@ module Flipper
|
|
10
9
|
InstrumentationName = "gate_operation.#{InstrumentationNamespace}"
|
11
10
|
|
12
11
|
# Private
|
13
|
-
attr_reader :
|
12
|
+
attr_reader :feature_name
|
14
13
|
|
15
14
|
# Private: What is used to instrument all the things.
|
16
15
|
attr_reader :instrumenter
|
17
16
|
|
18
|
-
def_delegator :@feature, :adapter
|
19
|
-
|
20
17
|
# Public
|
21
|
-
def initialize(
|
22
|
-
@
|
18
|
+
def initialize(feature_name, options = {})
|
19
|
+
@feature_name = feature_name
|
23
20
|
@instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop)
|
24
21
|
end
|
25
22
|
|
@@ -28,26 +25,29 @@ module Flipper
|
|
28
25
|
raise 'Not implemented'
|
29
26
|
end
|
30
27
|
|
31
|
-
# Private:
|
32
|
-
# Implemented in subclass.
|
28
|
+
# Private: Name converted to value safe for adapter. Implemented in subclass.
|
33
29
|
def key
|
34
30
|
raise 'Not implemented'
|
35
31
|
end
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
33
|
+
def data_type
|
34
|
+
raise 'Not implemented'
|
35
|
+
end
|
36
|
+
|
37
|
+
def enable(thing)
|
38
|
+
raise 'Not implemented'
|
39
|
+
end
|
40
|
+
|
41
|
+
def disable(thing)
|
42
|
+
raise 'Not implemented'
|
41
43
|
end
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
Toggles::Value
|
45
|
+
def enabled?(value)
|
46
|
+
raise 'Not implemented'
|
46
47
|
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
@toggle ||= toggle_class.new(self)
|
49
|
+
def description(value)
|
50
|
+
raise 'Not implemented'
|
51
51
|
end
|
52
52
|
|
53
53
|
# Internal: Check if a gate is open for a thing. Implemented in subclass.
|
@@ -64,33 +64,16 @@ module Flipper
|
|
64
64
|
false
|
65
65
|
end
|
66
66
|
|
67
|
-
# Internal:
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
toggle.enable(thing)
|
72
|
-
end
|
73
|
-
|
74
|
-
# Internal: Disable this gate for a thing.
|
75
|
-
#
|
76
|
-
# Returns the result of Flipper::Toggle#disable.
|
77
|
-
def disable(thing)
|
78
|
-
toggle.disable(thing)
|
79
|
-
end
|
80
|
-
|
81
|
-
def enabled?
|
82
|
-
toggle.enabled?
|
67
|
+
# Internal: Allows gate to wrap thing using one of the supported flipper
|
68
|
+
# types so adapters always get something that responds to value.
|
69
|
+
def wrap(thing)
|
70
|
+
thing
|
83
71
|
end
|
84
72
|
|
85
73
|
# Public: Pretty string version for debugging.
|
86
74
|
def inspect
|
87
75
|
attributes = [
|
88
|
-
"
|
89
|
-
"description=#{description.inspect}",
|
90
|
-
"adapter=#{adapter.name.inspect}",
|
91
|
-
"adapter_key=#{adapter_key.inspect}",
|
92
|
-
"toggle_class=#{toggle_class.inspect}",
|
93
|
-
"toggle_value=#{toggle.value.inspect}",
|
76
|
+
"feature_name=#{feature_name.inspect}",
|
94
77
|
]
|
95
78
|
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
96
79
|
end
|
@@ -101,7 +84,7 @@ module Flipper
|
|
101
84
|
:thing => thing,
|
102
85
|
:operation => operation,
|
103
86
|
:gate_name => name,
|
104
|
-
:feature_name => @
|
87
|
+
:feature_name => @feature_name,
|
105
88
|
}
|
106
89
|
|
107
90
|
@instrumenter.instrument(InstrumentationName, payload) {
|
data/lib/flipper/gates/actor.rb
CHANGED
@@ -6,27 +6,39 @@ module Flipper
|
|
6
6
|
:actor
|
7
7
|
end
|
8
8
|
|
9
|
-
# Internal:
|
9
|
+
# Internal: Name converted to value safe for adapter.
|
10
10
|
def key
|
11
11
|
:actors
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
def data_type
|
15
|
+
:set
|
16
|
+
end
|
17
|
+
|
18
|
+
def description(value)
|
19
|
+
if enabled?(value)
|
20
|
+
actor_ids = value.to_a.sort.map { |id| id.inspect }
|
21
|
+
"actors (#{actor_ids.join(', ')})"
|
22
|
+
else
|
23
|
+
'disabled'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def enabled?(value)
|
28
|
+
!value.nil? && !value.empty?
|
17
29
|
end
|
18
30
|
|
19
31
|
# Internal: Checks if the gate is open for a thing.
|
20
32
|
#
|
21
33
|
# Returns true if gate open for thing, false if not.
|
22
|
-
def open?(thing)
|
34
|
+
def open?(thing, value)
|
23
35
|
instrument(:open?, thing) { |payload|
|
24
36
|
if thing.nil?
|
25
37
|
false
|
26
38
|
else
|
27
|
-
if
|
28
|
-
actor =
|
29
|
-
enabled_actor_ids =
|
39
|
+
if protects?(thing)
|
40
|
+
actor = wrap(thing)
|
41
|
+
enabled_actor_ids = value
|
30
42
|
enabled_actor_ids.include?(actor.value)
|
31
43
|
else
|
32
44
|
false
|
@@ -35,24 +47,12 @@ module Flipper
|
|
35
47
|
}
|
36
48
|
end
|
37
49
|
|
38
|
-
def
|
39
|
-
Types::Actor.
|
40
|
-
end
|
41
|
-
|
42
|
-
def enable(thing)
|
43
|
-
super Types::Actor.wrap(thing)
|
50
|
+
def wrap(thing)
|
51
|
+
Types::Actor.wrap(thing)
|
44
52
|
end
|
45
53
|
|
46
|
-
def
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
def description
|
51
|
-
if enabled?
|
52
|
-
"actors (#{toggle.value.to_a.sort.join(', ')})"
|
53
|
-
else
|
54
|
-
'disabled'
|
55
|
-
end
|
54
|
+
def protects?(thing)
|
55
|
+
Types::Actor.wrappable?(thing)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -1,39 +1,52 @@
|
|
1
1
|
module Flipper
|
2
2
|
module Gates
|
3
3
|
class Boolean < Gate
|
4
|
+
TruthMap = {
|
5
|
+
true => true,
|
6
|
+
false => false,
|
7
|
+
'true' => true,
|
8
|
+
'false' => false,
|
9
|
+
}
|
10
|
+
|
4
11
|
# Internal: The name of the gate. Used for instrumentation, etc.
|
5
12
|
def name
|
6
13
|
:boolean
|
7
14
|
end
|
8
15
|
|
9
|
-
# Internal:
|
16
|
+
# Internal: Name converted to value safe for adapter.
|
10
17
|
def key
|
11
18
|
:boolean
|
12
19
|
end
|
13
20
|
|
14
|
-
|
15
|
-
|
16
|
-
|
21
|
+
def data_type
|
22
|
+
:boolean
|
23
|
+
end
|
24
|
+
|
25
|
+
def description(value)
|
26
|
+
if enabled?(value)
|
27
|
+
'Enabled'
|
28
|
+
else
|
29
|
+
'Disabled'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def enabled?(value)
|
34
|
+
!!TruthMap[value]
|
17
35
|
end
|
18
36
|
|
19
37
|
# Internal: Checks if the gate is open for a thing.
|
20
38
|
#
|
21
|
-
# Returns true if
|
22
|
-
|
23
|
-
|
39
|
+
# Returns true if explicitly set to true, false if explicitly set to false
|
40
|
+
# or nil if not explicitly set.
|
41
|
+
def open?(thing, value)
|
42
|
+
instrument(:open?, thing) { |payload|
|
43
|
+
!!TruthMap[value]
|
44
|
+
}
|
24
45
|
end
|
25
46
|
|
26
47
|
def protects?(thing)
|
27
48
|
thing.is_a?(Flipper::Types::Boolean)
|
28
49
|
end
|
29
|
-
|
30
|
-
def description
|
31
|
-
if enabled?
|
32
|
-
'Enabled'
|
33
|
-
else
|
34
|
-
'Disabled'
|
35
|
-
end
|
36
|
-
end
|
37
50
|
end
|
38
51
|
end
|
39
52
|
end
|
data/lib/flipper/gates/group.rb
CHANGED
@@ -6,25 +6,44 @@ module Flipper
|
|
6
6
|
:group
|
7
7
|
end
|
8
8
|
|
9
|
-
# Internal:
|
9
|
+
# Internal: Name converted to value safe for adapter.
|
10
10
|
def key
|
11
11
|
:groups
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
def data_type
|
15
|
+
:set
|
16
|
+
end
|
17
|
+
|
18
|
+
def description(value)
|
19
|
+
if enabled?(value)
|
20
|
+
group_names = value.to_a.sort.map { |name| name.to_sym.inspect }
|
21
|
+
"groups (#{group_names.join(', ')})"
|
22
|
+
else
|
23
|
+
'disabled'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def enabled?(value)
|
28
|
+
!value.nil? && !value.empty?
|
17
29
|
end
|
18
30
|
|
19
31
|
# Internal: Checks if the gate is open for a thing.
|
20
32
|
#
|
21
33
|
# Returns true if gate open for thing, false if not.
|
22
|
-
def open?(thing)
|
34
|
+
def open?(thing, value)
|
23
35
|
instrument(:open?, thing) { |payload|
|
24
36
|
if thing.nil?
|
25
37
|
false
|
26
38
|
else
|
27
|
-
|
39
|
+
value.any? { |name|
|
40
|
+
begin
|
41
|
+
group = Flipper.group(name)
|
42
|
+
group.match?(thing)
|
43
|
+
rescue GroupNotRegistered
|
44
|
+
false
|
45
|
+
end
|
46
|
+
}
|
28
47
|
end
|
29
48
|
}
|
30
49
|
end
|
@@ -32,34 +51,6 @@ module Flipper
|
|
32
51
|
def protects?(thing)
|
33
52
|
thing.is_a?(Flipper::Types::Group)
|
34
53
|
end
|
35
|
-
|
36
|
-
def description
|
37
|
-
if enabled?
|
38
|
-
"groups (#{toggle.value.to_a.sort.join(', ')})"
|
39
|
-
else
|
40
|
-
'disabled'
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Private: Get all the enabled groups for this gate.
|
45
|
-
#
|
46
|
-
# Returns an Array of Flipper::Types::Group instances.
|
47
|
-
def enabled_groups
|
48
|
-
enabled_group_names.map { |name|
|
49
|
-
begin
|
50
|
-
Flipper.group(name)
|
51
|
-
rescue GroupNotRegistered
|
52
|
-
nil
|
53
|
-
end
|
54
|
-
}.compact
|
55
|
-
end
|
56
|
-
|
57
|
-
# Private: Get all the names of enabled groups.
|
58
|
-
#
|
59
|
-
# Returns a Set of the enabled group names.
|
60
|
-
def enabled_group_names
|
61
|
-
toggle.value
|
62
|
-
end
|
63
54
|
end
|
64
55
|
end
|
65
56
|
end
|
@@ -8,21 +8,37 @@ module Flipper
|
|
8
8
|
:percentage_of_actors
|
9
9
|
end
|
10
10
|
|
11
|
-
# Internal:
|
11
|
+
# Internal: Name converted to value safe for adapter.
|
12
12
|
def key
|
13
|
-
:
|
13
|
+
:percentage_of_actors
|
14
|
+
end
|
15
|
+
|
16
|
+
def data_type
|
17
|
+
:integer
|
18
|
+
end
|
19
|
+
|
20
|
+
def description(value)
|
21
|
+
if enabled?(value)
|
22
|
+
"#{value}% of actors"
|
23
|
+
else
|
24
|
+
'disabled'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def enabled?(value)
|
29
|
+
!value.nil? && value.to_i > 0
|
14
30
|
end
|
15
31
|
|
16
32
|
# Internal: Checks if the gate is open for a thing.
|
17
33
|
#
|
18
34
|
# Returns true if gate open for thing, false if not.
|
19
|
-
def open?(thing)
|
35
|
+
def open?(thing, value)
|
20
36
|
instrument(:open?, thing) { |payload|
|
21
|
-
percentage =
|
37
|
+
percentage = value.to_i
|
22
38
|
|
23
39
|
if Types::Actor.wrappable?(thing)
|
24
40
|
actor = Types::Actor.wrap(thing)
|
25
|
-
key = "#{@
|
41
|
+
key = "#{@feature_name}#{actor.value}"
|
26
42
|
Zlib.crc32(key) % 100 < percentage
|
27
43
|
else
|
28
44
|
false
|
@@ -33,14 +49,6 @@ module Flipper
|
|
33
49
|
def protects?(thing)
|
34
50
|
thing.is_a?(Flipper::Types::PercentageOfActors)
|
35
51
|
end
|
36
|
-
|
37
|
-
def description
|
38
|
-
if enabled?
|
39
|
-
"#{toggle.value}% of actors"
|
40
|
-
else
|
41
|
-
'disabled'
|
42
|
-
end
|
43
|
-
end
|
44
52
|
end
|
45
53
|
end
|
46
54
|
end
|
@@ -6,17 +6,33 @@ module Flipper
|
|
6
6
|
:percentage_of_random
|
7
7
|
end
|
8
8
|
|
9
|
-
# Internal:
|
9
|
+
# Internal: Name converted to value safe for adapter.
|
10
10
|
def key
|
11
|
-
:
|
11
|
+
:percentage_of_random
|
12
|
+
end
|
13
|
+
|
14
|
+
def data_type
|
15
|
+
:integer
|
16
|
+
end
|
17
|
+
|
18
|
+
def description(value)
|
19
|
+
if enabled?(value)
|
20
|
+
"#{value}% of the time"
|
21
|
+
else
|
22
|
+
'disabled'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def enabled?(value)
|
27
|
+
!value.nil? && value.to_i > 0
|
12
28
|
end
|
13
29
|
|
14
30
|
# Internal: Checks if the gate is open for a thing.
|
15
31
|
#
|
16
32
|
# Returns true if gate open for thing, false if not.
|
17
|
-
def open?(thing)
|
33
|
+
def open?(thing, value)
|
18
34
|
instrument(:open?, thing) { |payload|
|
19
|
-
percentage =
|
35
|
+
percentage = value.to_i
|
20
36
|
|
21
37
|
rand < (percentage / 100.0)
|
22
38
|
}
|
@@ -25,14 +41,6 @@ module Flipper
|
|
25
41
|
def protects?(thing)
|
26
42
|
thing.is_a?(Flipper::Types::PercentageOfRandom)
|
27
43
|
end
|
28
|
-
|
29
|
-
def description
|
30
|
-
if enabled?
|
31
|
-
"#{toggle.value}% of the time"
|
32
|
-
else
|
33
|
-
'disabled'
|
34
|
-
end
|
35
|
-
end
|
36
44
|
end
|
37
45
|
end
|
38
46
|
end
|