flipper 0.8.0 → 0.9.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +6 -0
- data/docs/Adapters.md +35 -1
- data/lib/flipper/adapters/instrumented.rb +3 -1
- data/lib/flipper/adapters/memoizable.rb +4 -1
- data/lib/flipper/adapters/operation_logger.rb +4 -1
- data/lib/flipper/dsl.rb +1 -7
- data/lib/flipper/feature.rb +1 -1
- data/lib/flipper/gate_values.rb +10 -9
- data/lib/flipper/instrumentation/subscriber.rb +3 -1
- data/lib/flipper/test/shared_adapter_test.rb +250 -246
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/instrumented_spec.rb +10 -0
- data/spec/flipper/adapters/memoizable_spec.rb +10 -0
- data/spec/flipper/adapters/operation_logger_spec.rb +10 -0
- data/spec/flipper/dsl_spec.rb +0 -7
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +5 -1
- data/spec/flipper/instrumentation/metriks_subscriber_spec.rb +4 -1
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +4 -1
- data/test/adapters/memory_test.rb +1 -1
- data/test/adapters/pstore_test.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61e55bc67bc9d28868981582af136a9d57546713
|
4
|
+
data.tar.gz: d8d9cd17434a08805be919c60d4072eeceb31342
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce7c808ec096c1b1af7336a81b390f465b02d8b06c2eca86295b275ca0fa04d32ee6058f9dfc5a2c17150bad0a1c010522d70c97104480f51d7514086990cf16
|
7
|
+
data.tar.gz: 92ffbd0d092b4d4661dbe139fcfb969c878db4674aa791a4b5e7311cfe7cd724d2c1eba1199b3dd7a9dd65b75912b0fbf39009340951c5a4f05303e9e573be15
|
data/Changelog.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## master
|
2
|
+
|
3
|
+
* Moves SharedAdapterTests module to Flipper::Test::SharedAdapterTests to avoid clobbering anything top level in apps that use Flipper
|
4
|
+
* Memoizable, Instrumented and OperationLogger now delegate any missing methods to the original adapter. This was lost with the removal of the official decorator in 0.8, but is actually useful functionality for these "wrapping" adapters.
|
5
|
+
* Instrumenting adapters is now off by default. Use Flipper::Adapters::Instrumented.new(adapter) to instrument adapters and maintain the old functionality.
|
6
|
+
|
1
7
|
## 0.8
|
2
8
|
|
3
9
|
* removed Flipper::Decorator and Flipper::Adapters::Decorator in favor of just calling methods on wrapped adapter
|
data/docs/Adapters.md
CHANGED
@@ -29,10 +29,13 @@ The basic API for an adapter is this:
|
|
29
29
|
* `enable(feature, gate, thing)` - Enable a gate for a thing.
|
30
30
|
* `disable(feature, gate, thing)` - Disable a gate for a thing.
|
31
31
|
|
32
|
-
If you would like to make your own adapter, there are shared adapter specs that you can use to verify that you have everything working correctly.
|
32
|
+
If you would like to make your own adapter, there are shared adapter specs (RSpec) and tests (MiniTest) that you can use to verify that you have everything working correctly.
|
33
33
|
|
34
|
+
### RSpec
|
34
35
|
For example, here is what the in-memory adapter spec looks like:
|
35
36
|
|
37
|
+
`spec/flipper/adapters/memory_spec.rb`
|
38
|
+
|
36
39
|
```ruby
|
37
40
|
require 'helper'
|
38
41
|
require 'flipper/adapters/memory'
|
@@ -51,6 +54,37 @@ describe Flipper::Adapters::Memory do
|
|
51
54
|
end
|
52
55
|
```
|
53
56
|
|
57
|
+
### MiniTest
|
58
|
+
|
59
|
+
Here is what an in-memory adapter MiniTest looks like:
|
60
|
+
|
61
|
+
`test/adapters/memory_test.rb`
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
require 'test_helper'
|
65
|
+
require 'flipper/adapters/memory'
|
66
|
+
|
67
|
+
class MemoryTest < MiniTest::Test
|
68
|
+
prepend SharedAdapterTests
|
69
|
+
|
70
|
+
def setup
|
71
|
+
# Any code here will run before each test
|
72
|
+
@adapter = Flipper::Adapters::Memory.new
|
73
|
+
end
|
74
|
+
|
75
|
+
def teardown
|
76
|
+
# Any code here will run after each test
|
77
|
+
end
|
78
|
+
end
|
79
|
+
```
|
80
|
+
1. Create a file under `test/adapters` that inherits from MiniTest::Test.
|
81
|
+
|
82
|
+
2. `prepend SharedAdapterTests`.
|
83
|
+
|
84
|
+
3. Initialize an instance variable `@adapter` referencing an instance of the adapter.
|
85
|
+
|
86
|
+
4. Add any code to run before each test in a `setup` method and any code to run after each test in a `teardown` method.
|
87
|
+
|
54
88
|
A good place to start when creating your own adapter is to copy one of the adapters mentioned above and replace the client specific code with whatever client you are attempting to adapt.
|
55
89
|
|
56
90
|
I would also recommend setting `fail_fast = true` in your RSpec configuration as that will just give you one failure at a time to work through. It is also handy to have the shared adapter spec file open.
|
@@ -1,10 +1,11 @@
|
|
1
|
+
require 'delegate'
|
1
2
|
require 'flipper/instrumenters/noop'
|
2
3
|
|
3
4
|
module Flipper
|
4
5
|
module Adapters
|
5
6
|
# Internal: Adapter that wraps another adapter and instruments all adapter
|
6
7
|
# operations. Used by flipper dsl to provide instrumentatin for flipper.
|
7
|
-
class Instrumented
|
8
|
+
class Instrumented < SimpleDelegator
|
8
9
|
include ::Flipper::Adapter
|
9
10
|
|
10
11
|
# Private: The name of instrumentation events.
|
@@ -24,6 +25,7 @@ module Flipper
|
|
24
25
|
# :instrumenter - What to use to instrument all the things.
|
25
26
|
#
|
26
27
|
def initialize(adapter, options = {})
|
28
|
+
super(adapter)
|
27
29
|
@adapter = adapter
|
28
30
|
@name = :instrumented
|
29
31
|
@instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
|
@@ -1,9 +1,11 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
1
3
|
module Flipper
|
2
4
|
module Adapters
|
3
5
|
# Internal: Adapter that wraps another adapter with the ability to memoize
|
4
6
|
# adapter calls in memory. Used by flipper dsl and the memoizer middleware
|
5
7
|
# to make it possible to memoize adapter calls for the duration of a request.
|
6
|
-
class Memoizable
|
8
|
+
class Memoizable < SimpleDelegator
|
7
9
|
include ::Flipper::Adapter
|
8
10
|
|
9
11
|
FeaturesKey = :flipper_features
|
@@ -19,6 +21,7 @@ module Flipper
|
|
19
21
|
|
20
22
|
# Public
|
21
23
|
def initialize(adapter, cache = nil)
|
24
|
+
super(adapter)
|
22
25
|
@adapter = adapter
|
23
26
|
@name = :memoizable
|
24
27
|
@cache = cache || {}
|
@@ -1,9 +1,11 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
1
3
|
module Flipper
|
2
4
|
module Adapters
|
3
5
|
# Public: Adapter that wraps another adapter and stores the operations.
|
4
6
|
#
|
5
7
|
# Useful in tests to verify calls and such. Never use outside of testing.
|
6
|
-
class OperationLogger
|
8
|
+
class OperationLogger < SimpleDelegator
|
7
9
|
include ::Flipper::Adapter
|
8
10
|
|
9
11
|
Operation = Struct.new(:type, :args)
|
@@ -26,6 +28,7 @@ module Flipper
|
|
26
28
|
|
27
29
|
# Public
|
28
30
|
def initialize(adapter, operations = nil)
|
31
|
+
super(adapter)
|
29
32
|
@adapter = adapter
|
30
33
|
@name = :operation_logger
|
31
34
|
@operations = operations || []
|
data/lib/flipper/dsl.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'flipper/adapters/instrumented'
|
2
1
|
require 'flipper/adapters/memoizable'
|
3
2
|
require 'flipper/instrumenters/noop'
|
4
3
|
|
@@ -17,13 +16,8 @@ module Flipper
|
|
17
16
|
# :instrumenter - What should be used to instrument all the things.
|
18
17
|
def initialize(adapter, options = {})
|
19
18
|
@instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
|
20
|
-
|
21
|
-
instrumented = Adapters::Instrumented.new(adapter, {
|
22
|
-
:instrumenter => @instrumenter,
|
23
|
-
})
|
24
|
-
memoized = Adapters::Memoizable.new(instrumented)
|
19
|
+
memoized = Adapters::Memoizable.new(adapter)
|
25
20
|
@adapter = memoized
|
26
|
-
|
27
21
|
@memoized_features = {}
|
28
22
|
end
|
29
23
|
|
data/lib/flipper/feature.rb
CHANGED
@@ -7,7 +7,7 @@ require 'flipper/instrumenters/noop'
|
|
7
7
|
module Flipper
|
8
8
|
class Feature
|
9
9
|
# Private: The name of feature instrumentation events.
|
10
|
-
InstrumentationName = "feature_operation.#{InstrumentationNamespace}"
|
10
|
+
InstrumentationName = "feature_operation.#{InstrumentationNamespace}".freeze
|
11
11
|
|
12
12
|
# Public: The name of the feature.
|
13
13
|
attr_reader :name
|
data/lib/flipper/gate_values.rb
CHANGED
@@ -4,13 +4,13 @@ module Flipper
|
|
4
4
|
class GateValues
|
5
5
|
# Private: Array of instance variables that are readable through the []
|
6
6
|
# instance method.
|
7
|
-
LegitIvars =
|
8
|
-
"boolean",
|
9
|
-
"actors",
|
10
|
-
"groups",
|
11
|
-
"percentage_of_time",
|
12
|
-
"percentage_of_actors",
|
13
|
-
|
7
|
+
LegitIvars = {
|
8
|
+
"boolean" => "@boolean",
|
9
|
+
"actors" => "@actors",
|
10
|
+
"groups" => "@groups",
|
11
|
+
"percentage_of_time" => "@percentage_of_time",
|
12
|
+
"percentage_of_actors" => "@percentage_of_actors",
|
13
|
+
}.freeze
|
14
14
|
|
15
15
|
attr_reader :boolean
|
16
16
|
attr_reader :actors
|
@@ -27,8 +27,9 @@ module Flipper
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def [](key)
|
30
|
-
|
31
|
-
|
30
|
+
if ivar = LegitIvars[key.to_s]
|
31
|
+
instance_variable_get(ivar)
|
32
|
+
end
|
32
33
|
end
|
33
34
|
|
34
35
|
def eql?(other)
|
@@ -71,9 +71,11 @@ module Flipper
|
|
71
71
|
update_timer "flipper.adapter.#{adapter_name}.#{operation}"
|
72
72
|
end
|
73
73
|
|
74
|
+
QUESTION_MARK = "?".freeze
|
75
|
+
|
74
76
|
# Private
|
75
77
|
def strip_trailing_question_mark(operation)
|
76
|
-
operation.to_s.chomp(
|
78
|
+
operation.to_s.chomp(QUESTION_MARK)
|
77
79
|
end
|
78
80
|
end
|
79
81
|
end
|
@@ -1,249 +1,253 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
1
|
+
module Flipper
|
2
|
+
module Test
|
3
|
+
module SharedAdapterTests
|
4
|
+
def setup
|
5
|
+
super
|
6
|
+
@flipper = Flipper.new(@adapter)
|
7
|
+
@actor_class = Struct.new(:flipper_id)
|
8
|
+
@feature = @flipper[:stats]
|
9
|
+
@boolean_gate = @feature.gate(:boolean)
|
10
|
+
@group_gate = @feature.gate(:group)
|
11
|
+
@actor_gate = @feature.gate(:actor)
|
12
|
+
@actors_gate = @feature.gate(:percentage_of_actors)
|
13
|
+
@time_gate = @feature.gate(:percentage_of_time)
|
14
|
+
|
15
|
+
Flipper.register(:admins) do |actor|
|
16
|
+
actor.respond_to?(:admin?) && actor.admin?
|
17
|
+
end
|
18
|
+
|
19
|
+
Flipper.register(:early_access) { |actor|
|
20
|
+
actor.respond_to?(:early_access?) && actor.early_access?
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def teardown
|
25
|
+
super
|
26
|
+
Flipper.unregister_groups
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_has_name_that_is_a_symbol
|
30
|
+
refute_empty @adapter.name
|
31
|
+
assert_kind_of Symbol, @adapter.name
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_has_included_the_flipper_adapter_module
|
35
|
+
assert_includes @adapter.class.ancestors, Flipper::Adapter
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_returns_correct_default_values_for_gates_if_none_are_enabled
|
39
|
+
expected = {
|
40
|
+
:boolean => nil,
|
41
|
+
:groups => Set.new,
|
42
|
+
:actors => Set.new,
|
43
|
+
:percentage_of_actors => nil,
|
44
|
+
:percentage_of_time => nil,
|
45
|
+
}
|
46
|
+
assert_equal expected, @adapter.get(@feature)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_can_enable_disable_and_get_value_for_boolean_gate
|
50
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
51
|
+
assert_equal 'true', @adapter.get(@feature)[:boolean]
|
52
|
+
assert_equal true, @adapter.disable(@feature, @boolean_gate, @flipper.boolean(false))
|
53
|
+
assert_equal nil, @adapter.get(@feature)[:boolean]
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_fully_disables_all_enabled_things_when_boolean_gate_disabled
|
57
|
+
actor_22 = @actor_class.new('22')
|
58
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
59
|
+
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
60
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_22))
|
61
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25))
|
62
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45))
|
63
|
+
assert_equal true, @adapter.disable(@feature, @boolean_gate, @flipper.boolean(false))
|
64
|
+
expected = {
|
65
|
+
:boolean => nil,
|
66
|
+
:groups => Set.new,
|
67
|
+
:actors => Set.new,
|
68
|
+
:percentage_of_actors => nil,
|
69
|
+
:percentage_of_time => nil,
|
70
|
+
}
|
71
|
+
assert_equal expected, @adapter.get(@feature)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_can_enable_disable_get_value_for_group_gate
|
75
|
+
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
76
|
+
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:early_access))
|
77
|
+
|
78
|
+
result = @adapter.get(@feature)
|
79
|
+
assert_equal Set['admins', 'early_access'], result[:groups]
|
80
|
+
|
81
|
+
assert_equal true, @adapter.disable(@feature, @group_gate, @flipper.group(:early_access))
|
82
|
+
result = @adapter.get(@feature)
|
83
|
+
assert_equal Set['admins'], result[:groups]
|
84
|
+
|
85
|
+
assert_equal true, @adapter.disable(@feature, @group_gate, @flipper.group(:admins))
|
86
|
+
result = @adapter.get(@feature)
|
87
|
+
assert_equal Set.new, result[:groups]
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_can_enable_disable_and_get_value_for_an_actor_gate
|
91
|
+
actor_22 = @actor_class.new('22')
|
92
|
+
actor_asdf = @actor_class.new('asdf')
|
93
|
+
|
94
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_22))
|
95
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_asdf))
|
96
|
+
|
97
|
+
result = @adapter.get(@feature)
|
98
|
+
assert_equal Set['22', 'asdf'], result[:actors]
|
99
|
+
|
100
|
+
assert true, @adapter.disable(@feature, @actor_gate, @flipper.actor(actor_22))
|
101
|
+
result = @adapter.get(@feature)
|
102
|
+
assert_equal Set['asdf'], result[:actors]
|
103
|
+
|
104
|
+
assert_equal true, @adapter.disable(@feature, @actor_gate, @flipper.actor(actor_asdf))
|
105
|
+
result = @adapter.get(@feature)
|
106
|
+
assert_equal Set.new, result[:actors]
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_can_enable_disable_get_value_for_percentage_of_actors_gate
|
110
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(15))
|
111
|
+
result = @adapter.get(@feature)
|
112
|
+
assert_equal '15', result[:percentage_of_actors]
|
113
|
+
|
114
|
+
assert_equal true, @adapter.disable(@feature, @actors_gate, @flipper.actors(0))
|
115
|
+
result = @adapter.get(@feature)
|
116
|
+
assert_equal '0', result[:percentage_of_actors]
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_can_enable_percentage_of_actors_gate_many_times_and_consistently_return_values
|
120
|
+
(1..100).each do |percentage|
|
121
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(percentage))
|
122
|
+
result = @adapter.get(@feature)
|
123
|
+
assert_equal percentage.to_s, result[:percentage_of_actors]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_can_disable_percentage_of_actors_gate_many_times_and_consistently_return_values
|
128
|
+
(1..100).each do |percentage|
|
129
|
+
assert_equal true, @adapter.disable(@feature, @actors_gate, @flipper.actors(percentage))
|
130
|
+
result = @adapter.get(@feature)
|
131
|
+
assert_equal percentage.to_s, result[:percentage_of_actors]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_can_enable_disable_and_get_value_for_percentage_of_time_gate
|
136
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(10))
|
137
|
+
result = @adapter.get(@feature)
|
138
|
+
assert_equal '10', result[:percentage_of_time]
|
139
|
+
|
140
|
+
assert_equal true, @adapter.disable(@feature, @time_gate, @flipper.time(0))
|
141
|
+
result = @adapter.get(@feature)
|
142
|
+
assert_equal '0', result[:percentage_of_time]
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_can_enable_percentage_of_time_gate_many_times_and_consistently_return_values
|
146
|
+
(1..100).each do |percentage|
|
147
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(percentage))
|
148
|
+
result = @adapter.get(@feature)
|
149
|
+
assert_equal percentage.to_s, result[:percentage_of_time]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_can_disable_percentage_of_time_gate_many_times_and_consistently_return_values
|
154
|
+
(1..100).each do |percentage|
|
155
|
+
assert_equal true, @adapter.disable(@feature, @time_gate, @flipper.time(percentage))
|
156
|
+
result = @adapter.get(@feature)
|
157
|
+
assert_equal percentage.to_s, result[:percentage_of_time]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_converts_boolean_value_to_a_string
|
162
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
163
|
+
result = @adapter.get(@feature)
|
164
|
+
assert_equal 'true', result[:boolean]
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_converts_the_actor_value_to_a_string
|
168
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(@actor_class.new(22)))
|
169
|
+
result = @adapter.get(@feature)
|
170
|
+
assert_equal Set['22'], result[:actors]
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_converts_group_value_to_a_string
|
174
|
+
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
175
|
+
result = @adapter.get(@feature)
|
176
|
+
assert_equal Set['admins'], result[:groups]
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_converts_percentage_of_time_integer_value_to_a_string
|
180
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(10))
|
181
|
+
result = @adapter.get(@feature)
|
182
|
+
assert_equal '10', result[:percentage_of_time]
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_converts_percentage_of_actors_integer_value_to_a_string
|
186
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(10))
|
187
|
+
result = @adapter.get(@feature)
|
188
|
+
assert_equal '10', result[:percentage_of_actors]
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_can_add_remove_and_list_known_features
|
192
|
+
assert_equal Set.new, @adapter.features
|
193
|
+
|
194
|
+
assert_equal true, @adapter.add(@flipper[:stats])
|
195
|
+
assert_equal Set['stats'], @adapter.features
|
196
|
+
|
197
|
+
assert_equal true, @adapter.add(@flipper[:search])
|
198
|
+
assert_equal Set['stats', 'search'], @adapter.features
|
199
|
+
|
200
|
+
assert_equal true, @adapter.remove(@flipper[:stats])
|
201
|
+
assert_equal Set['search'], @adapter.features
|
202
|
+
|
203
|
+
assert_equal true, @adapter.remove(@flipper[:search])
|
204
|
+
assert_equal Set.new, @adapter.features
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_clears_all_the_gate_values_for_the_feature_on_remove
|
208
|
+
actor_22 = @actor_class.new('22')
|
209
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
210
|
+
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
211
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_22))
|
212
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25))
|
213
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45))
|
214
|
+
|
215
|
+
assert_equal true, @adapter.remove(@feature)
|
216
|
+
|
217
|
+
assert_equal @adapter.get(@feature), {
|
218
|
+
:boolean => nil,
|
219
|
+
:groups => Set.new,
|
220
|
+
:actors => Set.new,
|
221
|
+
:percentage_of_actors => nil,
|
222
|
+
:percentage_of_time => nil,
|
223
|
+
}
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_can_clear_all_the_gate_values_for_a_feature
|
227
|
+
actor_22 = @actor_class.new('22')
|
228
|
+
@adapter.add(@feature)
|
229
|
+
assert_includes @adapter.features, @feature.key
|
230
|
+
|
231
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
232
|
+
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
233
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_22))
|
234
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25))
|
235
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45))
|
236
|
+
|
237
|
+
assert_equal true, @adapter.clear(@feature)
|
238
|
+
assert_includes @adapter.features, @feature.key
|
239
|
+
assert_equal @adapter.get(@feature), {
|
240
|
+
:boolean => nil,
|
241
|
+
:groups => Set.new,
|
242
|
+
:actors => Set.new,
|
243
|
+
:percentage_of_actors => nil,
|
244
|
+
:percentage_of_time => nil,
|
245
|
+
}
|
246
|
+
end
|
247
|
+
|
248
|
+
def test_does_not_complain_clearing_a_feature_that_does_not_exist_in_adapter
|
249
|
+
assert_equal true, @adapter.clear(@flipper[:stats])
|
250
|
+
end
|
15
251
|
end
|
16
|
-
|
17
|
-
Flipper.register(:early_access) { |actor|
|
18
|
-
actor.respond_to?(:early_access?) && actor.early_access?
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
|
-
def teardown
|
23
|
-
super
|
24
|
-
Flipper.unregister_groups
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_has_name_that_is_a_symbol
|
28
|
-
refute_empty @adapter.name
|
29
|
-
assert_kind_of Symbol, @adapter.name
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_has_included_the_flipper_adapter_module
|
33
|
-
assert_includes @adapter.class.ancestors, Flipper::Adapter
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_returns_correct_default_values_for_gates_if_none_are_enabled
|
37
|
-
expected = {
|
38
|
-
:boolean => nil,
|
39
|
-
:groups => Set.new,
|
40
|
-
:actors => Set.new,
|
41
|
-
:percentage_of_actors => nil,
|
42
|
-
:percentage_of_time => nil,
|
43
|
-
}
|
44
|
-
assert_equal expected, @adapter.get(@feature)
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_can_enable_disable_and_get_value_for_boolean_gate
|
48
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
49
|
-
assert_equal 'true', @adapter.get(@feature)[:boolean]
|
50
|
-
assert_equal true, @adapter.disable(@feature, @boolean_gate, @flipper.boolean(false))
|
51
|
-
assert_equal nil, @adapter.get(@feature)[:boolean]
|
52
|
-
end
|
53
|
-
|
54
|
-
def test_fully_disables_all_enabled_things_when_boolean_gate_disabled
|
55
|
-
actor_22 = @actor_class.new('22')
|
56
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
57
|
-
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
58
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_22))
|
59
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25))
|
60
|
-
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45))
|
61
|
-
assert_equal true, @adapter.disable(@feature, @boolean_gate, @flipper.boolean(false))
|
62
|
-
expected = {
|
63
|
-
:boolean => nil,
|
64
|
-
:groups => Set.new,
|
65
|
-
:actors => Set.new,
|
66
|
-
:percentage_of_actors => nil,
|
67
|
-
:percentage_of_time => nil,
|
68
|
-
}
|
69
|
-
assert_equal expected, @adapter.get(@feature)
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_can_enable_disable_get_value_for_group_gate
|
73
|
-
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
74
|
-
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:early_access))
|
75
|
-
|
76
|
-
result = @adapter.get(@feature)
|
77
|
-
assert_equal Set['admins', 'early_access'], result[:groups]
|
78
|
-
|
79
|
-
assert_equal true, @adapter.disable(@feature, @group_gate, @flipper.group(:early_access))
|
80
|
-
result = @adapter.get(@feature)
|
81
|
-
assert_equal Set['admins'], result[:groups]
|
82
|
-
|
83
|
-
assert_equal true, @adapter.disable(@feature, @group_gate, @flipper.group(:admins))
|
84
|
-
result = @adapter.get(@feature)
|
85
|
-
assert_equal Set.new, result[:groups]
|
86
|
-
end
|
87
|
-
|
88
|
-
def test_can_enable_disable_and_get_value_for_an_actor_gate
|
89
|
-
actor_22 = @actor_class.new('22')
|
90
|
-
actor_asdf = @actor_class.new('asdf')
|
91
|
-
|
92
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_22))
|
93
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_asdf))
|
94
|
-
|
95
|
-
result = @adapter.get(@feature)
|
96
|
-
assert_equal Set['22', 'asdf'], result[:actors]
|
97
|
-
|
98
|
-
assert true, @adapter.disable(@feature, @actor_gate, @flipper.actor(actor_22))
|
99
|
-
result = @adapter.get(@feature)
|
100
|
-
assert_equal Set['asdf'], result[:actors]
|
101
|
-
|
102
|
-
assert_equal true, @adapter.disable(@feature, @actor_gate, @flipper.actor(actor_asdf))
|
103
|
-
result = @adapter.get(@feature)
|
104
|
-
assert_equal Set.new, result[:actors]
|
105
|
-
end
|
106
|
-
|
107
|
-
def test_can_enable_disable_get_value_for_percentage_of_actors_gate
|
108
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(15))
|
109
|
-
result = @adapter.get(@feature)
|
110
|
-
assert_equal '15', result[:percentage_of_actors]
|
111
|
-
|
112
|
-
assert_equal true, @adapter.disable(@feature, @actors_gate, @flipper.actors(0))
|
113
|
-
result = @adapter.get(@feature)
|
114
|
-
assert_equal '0', result[:percentage_of_actors]
|
115
|
-
end
|
116
|
-
|
117
|
-
def test_can_enable_percentage_of_actors_gate_many_times_and_consistently_return_values
|
118
|
-
(1..100).each do |percentage|
|
119
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(percentage))
|
120
|
-
result = @adapter.get(@feature)
|
121
|
-
assert_equal percentage.to_s, result[:percentage_of_actors]
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def test_can_disable_percentage_of_actors_gate_many_times_and_consistently_return_values
|
126
|
-
(1..100).each do |percentage|
|
127
|
-
assert_equal true, @adapter.disable(@feature, @actors_gate, @flipper.actors(percentage))
|
128
|
-
result = @adapter.get(@feature)
|
129
|
-
assert_equal percentage.to_s, result[:percentage_of_actors]
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def test_can_enable_disable_and_get_value_for_percentage_of_time_gate
|
134
|
-
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(10))
|
135
|
-
result = @adapter.get(@feature)
|
136
|
-
assert_equal '10', result[:percentage_of_time]
|
137
|
-
|
138
|
-
assert_equal true, @adapter.disable(@feature, @time_gate, @flipper.time(0))
|
139
|
-
result = @adapter.get(@feature)
|
140
|
-
assert_equal '0', result[:percentage_of_time]
|
141
|
-
end
|
142
|
-
|
143
|
-
def test_can_enable_percentage_of_time_gate_many_times_and_consistently_return_values
|
144
|
-
(1..100).each do |percentage|
|
145
|
-
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(percentage))
|
146
|
-
result = @adapter.get(@feature)
|
147
|
-
assert_equal percentage.to_s, result[:percentage_of_time]
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def test_can_disable_percentage_of_time_gate_many_times_and_consistently_return_values
|
152
|
-
(1..100).each do |percentage|
|
153
|
-
assert_equal true, @adapter.disable(@feature, @time_gate, @flipper.time(percentage))
|
154
|
-
result = @adapter.get(@feature)
|
155
|
-
assert_equal percentage.to_s, result[:percentage_of_time]
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def test_converts_boolean_value_to_a_string
|
160
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
161
|
-
result = @adapter.get(@feature)
|
162
|
-
assert_equal 'true', result[:boolean]
|
163
|
-
end
|
164
|
-
|
165
|
-
def test_converts_the_actor_value_to_a_string
|
166
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(@actor_class.new(22)))
|
167
|
-
result = @adapter.get(@feature)
|
168
|
-
assert_equal Set['22'], result[:actors]
|
169
|
-
end
|
170
|
-
|
171
|
-
def test_converts_group_value_to_a_string
|
172
|
-
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
173
|
-
result = @adapter.get(@feature)
|
174
|
-
assert_equal Set['admins'], result[:groups]
|
175
|
-
end
|
176
|
-
|
177
|
-
def test_converts_percentage_of_time_integer_value_to_a_string
|
178
|
-
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(10))
|
179
|
-
result = @adapter.get(@feature)
|
180
|
-
assert_equal '10', result[:percentage_of_time]
|
181
|
-
end
|
182
|
-
|
183
|
-
def test_converts_percentage_of_actors_integer_value_to_a_string
|
184
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(10))
|
185
|
-
result = @adapter.get(@feature)
|
186
|
-
assert_equal '10', result[:percentage_of_actors]
|
187
|
-
end
|
188
|
-
|
189
|
-
def test_can_add_remove_and_list_known_features
|
190
|
-
assert_equal Set.new, @adapter.features
|
191
|
-
|
192
|
-
assert_equal true, @adapter.add(@flipper[:stats])
|
193
|
-
assert_equal Set['stats'], @adapter.features
|
194
|
-
|
195
|
-
assert_equal true, @adapter.add(@flipper[:search])
|
196
|
-
assert_equal Set['stats', 'search'], @adapter.features
|
197
|
-
|
198
|
-
assert_equal true, @adapter.remove(@flipper[:stats])
|
199
|
-
assert_equal Set['search'], @adapter.features
|
200
|
-
|
201
|
-
assert_equal true, @adapter.remove(@flipper[:search])
|
202
|
-
assert_equal Set.new, @adapter.features
|
203
|
-
end
|
204
|
-
|
205
|
-
def test_clears_all_the_gate_values_for_the_feature_on_remove
|
206
|
-
actor_22 = @actor_class.new('22')
|
207
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
208
|
-
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
209
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_22))
|
210
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25))
|
211
|
-
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45))
|
212
|
-
|
213
|
-
assert_equal true, @adapter.remove(@feature)
|
214
|
-
|
215
|
-
assert_equal @adapter.get(@feature), {
|
216
|
-
:boolean => nil,
|
217
|
-
:groups => Set.new,
|
218
|
-
:actors => Set.new,
|
219
|
-
:percentage_of_actors => nil,
|
220
|
-
:percentage_of_time => nil,
|
221
|
-
}
|
222
|
-
end
|
223
|
-
|
224
|
-
def test_can_clear_all_the_gate_values_for_a_feature
|
225
|
-
actor_22 = @actor_class.new('22')
|
226
|
-
@adapter.add(@feature)
|
227
|
-
assert_includes @adapter.features, @feature.key
|
228
|
-
|
229
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate, @flipper.boolean)
|
230
|
-
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
231
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate, @flipper.actor(actor_22))
|
232
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate, @flipper.actors(25))
|
233
|
-
assert_equal true, @adapter.enable(@feature, @time_gate, @flipper.time(45))
|
234
|
-
|
235
|
-
assert_equal true, @adapter.clear(@feature)
|
236
|
-
assert_includes @adapter.features, @feature.key
|
237
|
-
assert_equal @adapter.get(@feature), {
|
238
|
-
:boolean => nil,
|
239
|
-
:groups => Set.new,
|
240
|
-
:actors => Set.new,
|
241
|
-
:percentage_of_actors => nil,
|
242
|
-
:percentage_of_time => nil,
|
243
|
-
}
|
244
|
-
end
|
245
|
-
|
246
|
-
def test_does_not_complain_clearing_a_feature_that_does_not_exist_in_adapter
|
247
|
-
assert_equal true, @adapter.clear(@flipper[:stats])
|
248
252
|
end
|
249
253
|
end
|
data/lib/flipper/version.rb
CHANGED
@@ -19,6 +19,16 @@ RSpec.describe Flipper::Adapters::Instrumented do
|
|
19
19
|
|
20
20
|
it_should_behave_like 'a flipper adapter'
|
21
21
|
|
22
|
+
it "forwards missing methods to underlying adapter" do
|
23
|
+
adapter = Class.new do
|
24
|
+
def foo
|
25
|
+
:foo
|
26
|
+
end
|
27
|
+
end.new
|
28
|
+
instrumented = described_class.new(adapter)
|
29
|
+
expect(instrumented.foo).to eq(:foo)
|
30
|
+
end
|
31
|
+
|
22
32
|
describe "#name" do
|
23
33
|
it "is instrumented" do
|
24
34
|
expect(subject.name).to be(:instrumented)
|
@@ -13,6 +13,16 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
13
13
|
|
14
14
|
it_should_behave_like 'a flipper adapter'
|
15
15
|
|
16
|
+
it "forwards missing methods to underlying adapter" do
|
17
|
+
adapter = Class.new do
|
18
|
+
def foo
|
19
|
+
:foo
|
20
|
+
end
|
21
|
+
end.new
|
22
|
+
memoizable = described_class.new(adapter)
|
23
|
+
expect(memoizable.foo).to eq(:foo)
|
24
|
+
end
|
25
|
+
|
16
26
|
describe "#name" do
|
17
27
|
it "is instrumented" do
|
18
28
|
expect(subject.name).to be(:memoizable)
|
@@ -12,6 +12,16 @@ RSpec.describe Flipper::Adapters::OperationLogger do
|
|
12
12
|
|
13
13
|
it_should_behave_like 'a flipper adapter'
|
14
14
|
|
15
|
+
it "forwards missing methods to underlying adapter" do
|
16
|
+
adapter = Class.new do
|
17
|
+
def foo
|
18
|
+
:foo
|
19
|
+
end
|
20
|
+
end.new
|
21
|
+
operation_logger = described_class.new(adapter)
|
22
|
+
expect(operation_logger.foo).to eq(:foo)
|
23
|
+
end
|
24
|
+
|
15
25
|
describe "#get" do
|
16
26
|
before do
|
17
27
|
@feature = flipper[:stats]
|
data/spec/flipper/dsl_spec.rb
CHANGED
@@ -25,13 +25,6 @@ RSpec.describe Flipper::DSL do
|
|
25
25
|
dsl = described_class.new(adapter, :instrumenter => instrumenter)
|
26
26
|
expect(dsl.instrumenter).to be(instrumenter)
|
27
27
|
end
|
28
|
-
|
29
|
-
it "passes overridden instrumenter to instrumented adapter" do
|
30
|
-
dsl = described_class.new(adapter, :instrumenter => instrumenter)
|
31
|
-
memoized = dsl.adapter
|
32
|
-
instrumented = memoized.adapter
|
33
|
-
expect(instrumented.instrumenter).to be(instrumenter)
|
34
|
-
end
|
35
28
|
end
|
36
29
|
end
|
37
30
|
|
@@ -1,10 +1,14 @@
|
|
1
1
|
require 'logger'
|
2
2
|
require 'helper'
|
3
|
+
require 'flipper/adapters/instrumented'
|
3
4
|
require 'flipper/adapters/memory'
|
4
5
|
require 'flipper/instrumentation/log_subscriber'
|
5
6
|
|
6
7
|
RSpec.describe Flipper::Instrumentation::LogSubscriber do
|
7
|
-
let(:adapter) {
|
8
|
+
let(:adapter) {
|
9
|
+
memory = Flipper::Adapters::Memory.new
|
10
|
+
Flipper::Adapters::Instrumented.new(memory, :instrumenter => ActiveSupport::Notifications)
|
11
|
+
}
|
8
12
|
let(:flipper) {
|
9
13
|
Flipper.new(adapter, :instrumenter => ActiveSupport::Notifications)
|
10
14
|
}
|
@@ -3,7 +3,10 @@ require 'flipper/adapters/memory'
|
|
3
3
|
require 'flipper/instrumentation/metriks'
|
4
4
|
|
5
5
|
RSpec.describe Flipper::Instrumentation::MetriksSubscriber do
|
6
|
-
let(:adapter) {
|
6
|
+
let(:adapter) {
|
7
|
+
memory = Flipper::Adapters::Memory.new
|
8
|
+
Flipper::Adapters::Instrumented.new(memory, :instrumenter => ActiveSupport::Notifications)
|
9
|
+
}
|
7
10
|
let(:flipper) {
|
8
11
|
Flipper.new(adapter, :instrumenter => ActiveSupport::Notifications)
|
9
12
|
}
|
@@ -5,7 +5,10 @@ require 'flipper/instrumentation/statsd'
|
|
5
5
|
RSpec.describe Flipper::Instrumentation::StatsdSubscriber do
|
6
6
|
let(:statsd_client) { Statsd.new }
|
7
7
|
let(:socket) { FakeUDPSocket.new }
|
8
|
-
let(:adapter) {
|
8
|
+
let(:adapter) {
|
9
|
+
memory = Flipper::Adapters::Memory.new
|
10
|
+
Flipper::Adapters::Instrumented.new(memory, :instrumenter => ActiveSupport::Notifications)
|
11
|
+
}
|
9
12
|
let(:flipper) {
|
10
13
|
Flipper.new(adapter, :instrumenter => ActiveSupport::Notifications)
|
11
14
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Feature flipper is the act of enabling/disabling features in your application,
|
14
14
|
ideally without re-deploying or changing anything in your code base. Flipper makes
|
@@ -133,9 +133,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
133
133
|
version: '0'
|
134
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - ">"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 1.3.1
|
139
139
|
requirements: []
|
140
140
|
rubyforge_project:
|
141
141
|
rubygems_version: 2.4.5.1
|