flipper 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +12 -0
  3. data/Gemfile +1 -0
  4. data/Rakefile +9 -4
  5. data/docs/Optimization.md +8 -2
  6. data/lib/flipper/adapters/instrumented.rb +21 -17
  7. data/lib/flipper/adapters/memoizable.rb +20 -13
  8. data/lib/flipper/adapters/memory.rb +1 -1
  9. data/lib/flipper/adapters/operation_logger.rb +49 -12
  10. data/lib/flipper/adapters/pstore.rb +1 -1
  11. data/lib/flipper/adapters/read_only.rb +51 -0
  12. data/lib/flipper/dsl.rb +9 -0
  13. data/lib/flipper/feature.rb +8 -20
  14. data/lib/flipper/gate.rb +0 -4
  15. data/lib/flipper/gate_values.rb +4 -2
  16. data/lib/flipper/gates/actor.rb +1 -1
  17. data/lib/flipper/gates/boolean.rb +1 -1
  18. data/lib/flipper/gates/group.rb +1 -1
  19. data/lib/flipper/gates/percentage_of_actors.rb +3 -5
  20. data/lib/flipper/gates/percentage_of_time.rb +2 -3
  21. data/lib/flipper/instrumentation/log_subscriber.rb +0 -28
  22. data/lib/flipper/instrumentation/subscriber.rb +0 -22
  23. data/lib/flipper/middleware/memoizer.rb +2 -3
  24. data/lib/flipper/spec/shared_adapter_specs.rb +32 -0
  25. data/lib/flipper/test/shared_adapter_test.rb +249 -0
  26. data/lib/flipper/typecast.rb +1 -1
  27. data/lib/flipper/types/group.rb +1 -1
  28. data/lib/flipper/version.rb +1 -1
  29. data/spec/flipper/adapters/instrumented_spec.rb +6 -0
  30. data/spec/flipper/adapters/memoizable_spec.rb +6 -0
  31. data/spec/flipper/adapters/read_only_spec.rb +94 -0
  32. data/spec/flipper/dsl_spec.rb +14 -2
  33. data/spec/flipper/feature_spec.rb +11 -0
  34. data/spec/flipper/instrumentation/log_subscriber_spec.rb +0 -10
  35. data/spec/flipper/instrumentation/metriks_subscriber_spec.rb +0 -10
  36. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +0 -10
  37. data/spec/flipper/types/group_spec.rb +14 -0
  38. data/spec/helper.rb +4 -0
  39. data/spec/integration_spec.rb +11 -0
  40. data/spec/support/spec_helpers.rb +4 -0
  41. data/test/adapters/memory_test.rb +10 -0
  42. data/test/adapters/pstore_test.rb +17 -0
  43. data/test/helper.rb +0 -2
  44. data/test/test_helper.rb +6 -0
  45. metadata +12 -4
  46. data/lib/flipper/adapters/decorator.rb +0 -11
  47. data/lib/flipper/decorator.rb +0 -6
@@ -145,6 +145,15 @@ module Flipper
145
145
  feature(name).disable_percentage_of_actors
146
146
  end
147
147
 
148
+ # Public: Remove a feature.
149
+ #
150
+ # name - The String or Symbol name of the feature.
151
+ #
152
+ # Returns result of remove.
153
+ def remove(name)
154
+ feature(name).remove
155
+ end
156
+
148
157
  # Public: Access a feature instance by name.
149
158
  #
150
159
  # name - The String or Symbol name of the feature.
@@ -9,9 +9,6 @@ module Flipper
9
9
  # Private: The name of feature instrumentation events.
10
10
  InstrumentationName = "feature_operation.#{InstrumentationNamespace}"
11
11
 
12
- # Private: The name of gate instrumentation events.
13
- GateInstrumentationName = "gate_operation.#{InstrumentationNamespace}"
14
-
15
12
  # Public: The name of the feature.
16
13
  attr_reader :name
17
14
 
@@ -75,6 +72,13 @@ module Flipper
75
72
  }
76
73
  end
77
74
 
75
+ # Public: Removes this feature.
76
+ #
77
+ # Returns the result of Adapter#remove.
78
+ def remove
79
+ instrument(:remove) { adapter.remove(self) }
80
+ end
81
+
78
82
  # Public: Check if a feature is enabled for a thing.
79
83
  #
80
84
  # Returns true if enabled, false if not.
@@ -85,9 +89,7 @@ module Flipper
85
89
  payload[:thing] = gate(:actor).wrap(thing) unless thing.nil?
86
90
 
87
91
  open_gate = gates.detect { |gate|
88
- instrument_gate(gate, :open?, thing) { |gate_payload|
89
- gate.open?(thing, values[gate.key], feature_name: @name)
90
- }
92
+ gate.open?(thing, values[gate.key], feature_name: @name)
91
93
  }
92
94
 
93
95
  if open_gate.nil?
@@ -356,19 +358,5 @@ module Flipper
356
358
  payload[:result] = yield(payload) if block_given?
357
359
  }
358
360
  end
359
-
360
- # Private: Intrument a gate operation.
361
- def instrument_gate(gate, operation, thing)
362
- payload = {
363
- :feature_name => @name,
364
- :gate_name => gate.name,
365
- :operation => operation,
366
- :thing => thing,
367
- }
368
-
369
- @instrumenter.instrument(GateInstrumentationName, payload) { |payload|
370
- payload[:result] = yield(payload) if block_given?
371
- }
372
- end
373
361
  end
374
362
  end
@@ -1,9 +1,5 @@
1
- require 'forwardable'
2
-
3
1
  module Flipper
4
2
  class Gate
5
- extend Forwardable
6
-
7
3
  # Public
8
4
  def initialize(options = {})
9
5
  end
@@ -1,14 +1,16 @@
1
+ require "set"
2
+
1
3
  module Flipper
2
4
  class GateValues
3
5
  # Private: Array of instance variables that are readable through the []
4
6
  # instance method.
5
- LegitIvars = [
7
+ LegitIvars = Set.new([
6
8
  "boolean",
7
9
  "actors",
8
10
  "groups",
9
11
  "percentage_of_time",
10
12
  "percentage_of_actors",
11
- ]
13
+ ]).freeze
12
14
 
13
15
  attr_reader :boolean
14
16
  attr_reader :actors
@@ -16,7 +16,7 @@ module Flipper
16
16
  end
17
17
 
18
18
  def enabled?(value)
19
- !Typecast.to_set(value).empty?
19
+ !value.empty?
20
20
  end
21
21
 
22
22
  # Internal: Checks if the gate is open for a thing.
@@ -16,7 +16,7 @@ module Flipper
16
16
  end
17
17
 
18
18
  def enabled?(value)
19
- Typecast.to_boolean(value)
19
+ !!value
20
20
  end
21
21
 
22
22
  # Internal: Checks if the gate is open for a thing.
@@ -16,7 +16,7 @@ module Flipper
16
16
  end
17
17
 
18
18
  def enabled?(value)
19
- !Typecast.to_set(value).empty?
19
+ !value.empty?
20
20
  end
21
21
 
22
22
  # Internal: Checks if the gate is open for a thing.
@@ -18,20 +18,18 @@ module Flipper
18
18
  end
19
19
 
20
20
  def enabled?(value)
21
- Typecast.to_integer(value) > 0
21
+ value > 0
22
22
  end
23
23
 
24
24
  # Internal: Checks if the gate is open for a thing.
25
25
  #
26
26
  # Returns true if gate open for thing, false if not.
27
27
  def open?(thing, value, options = {})
28
- feature_name = options.fetch(:feature_name)
29
- percentage = Typecast.to_integer(value)
30
-
31
28
  if Types::Actor.wrappable?(thing)
32
29
  actor = Types::Actor.wrap(thing)
30
+ feature_name = options.fetch(:feature_name)
33
31
  key = "#{feature_name}#{actor.value}"
34
- Zlib.crc32(key) % 100 < percentage
32
+ Zlib.crc32(key) % 100 < value
35
33
  else
36
34
  false
37
35
  end
@@ -16,15 +16,14 @@ module Flipper
16
16
  end
17
17
 
18
18
  def enabled?(value)
19
- Typecast.to_integer(value) > 0
19
+ value > 0
20
20
  end
21
21
 
22
22
  # Internal: Checks if the gate is open for a thing.
23
23
  #
24
24
  # Returns true if gate open for thing, false if not.
25
25
  def open?(thing, value, options = {})
26
- percentage = Typecast.to_integer(value)
27
- rand < (percentage / 100.0)
26
+ rand < (value / 100.0)
28
27
  end
29
28
 
30
29
  def protects?(thing)
@@ -65,34 +65,6 @@ module Flipper
65
65
  debug " #{color(name, CYAN, true)} [ #{details} ]"
66
66
  end
67
67
 
68
- # Logs a gate operation.
69
- #
70
- # Example Output
71
- #
72
- # flipper[:search].enabled?(user)
73
- # # Flipper feature(search) gate(boolean) open false (0.1ms) [ thing=... ]
74
- # # Flipper feature(search) gate(group) open false (0.1ms) [ thing=... ]
75
- # # Flipper feature(search) gate(actor) open false (0.1ms) [ thing=... ]
76
- # # Flipper feature(search) gate(percentage_of_actors) open false (0.1ms) [ thing=... ]
77
- # # Flipper feature(search) gate(percentage_of_time) open false (0.1ms) [ thing=... ]
78
- #
79
- # Returns nothing.
80
- def gate_operation(event)
81
- return unless logger.debug?
82
-
83
- feature_name = event.payload[:feature_name]
84
- gate_name = event.payload[:gate_name]
85
- operation = event.payload[:operation]
86
- result = event.payload[:result]
87
- thing = event.payload[:thing]
88
-
89
- description = "Flipper feature(#{feature_name}) gate(#{gate_name}) #{operation} #{result.inspect}"
90
- details = "thing=#{thing.inspect}"
91
-
92
- name = '%s (%.1fms)' % [description, event.duration]
93
- debug " #{color(name, CYAN, true)} [ #{details} ]"
94
- end
95
-
96
68
  def logger
97
69
  self.class.logger
98
70
  end
@@ -71,28 +71,6 @@ module Flipper
71
71
  update_timer "flipper.adapter.#{adapter_name}.#{operation}"
72
72
  end
73
73
 
74
- # Private
75
- def update_gate_operation_metrics
76
- feature_name = @payload[:feature_name]
77
- gate_name = @payload[:gate_name]
78
- operation = strip_trailing_question_mark(@payload[:operation])
79
- result = @payload[:result]
80
- thing = @payload[:thing]
81
-
82
- update_timer "flipper.gate_operation.#{gate_name}.#{operation}"
83
- update_timer "flipper.feature.#{feature_name}.gate_operation.#{gate_name}.#{operation}"
84
-
85
- if @payload[:operation] == :open?
86
- metric_name = if result
87
- "flipper.feature.#{feature_name}.gate.#{gate_name}.open"
88
- else
89
- "flipper.feature.#{feature_name}.gate.#{gate_name}.closed"
90
- end
91
-
92
- update_counter metric_name
93
- end
94
- end
95
-
96
74
  # Private
97
75
  def strip_trailing_question_mark(operation)
98
76
  operation.to_s.chomp('?')
@@ -3,7 +3,7 @@ require 'rack/body_proxy'
3
3
  module Flipper
4
4
  module Middleware
5
5
  class Memoizer
6
- # Public: Initializes an instance of the UI middleware.
6
+ # Public: Initializes an instance of the Memoizer middleware.
7
7
  #
8
8
  # app - The app this middleware is included in.
9
9
  # flipper_or_block - The Flipper::DSL instance or a block that yields a
@@ -11,9 +11,8 @@ module Flipper
11
11
  #
12
12
  # Examples
13
13
  #
14
- # flipper = Flipper.new(...)
15
- #
16
14
  # # using with a normal flipper instance
15
+ # flipper = Flipper.new(...)
17
16
  # use Flipper::Middleware::Memoizer, flipper
18
17
  #
19
18
  # # using with a block that yields a flipper instance
@@ -121,6 +121,22 @@ shared_examples_for 'a flipper adapter' do
121
121
  expect(result[:percentage_of_actors]).to eq('0')
122
122
  end
123
123
 
124
+ it "can enable percentage of actors gate many times and consistently return values" do
125
+ (1..100).each do |percentage|
126
+ expect(subject.enable(feature, actors_gate, flipper.actors(percentage))).to eq(true)
127
+ result = subject.get(feature)
128
+ expect(result[:percentage_of_actors]).to eq(percentage.to_s)
129
+ end
130
+ end
131
+
132
+ it "can disable percentage of actors gate many times and consistently return values" do
133
+ (1..100).each do |percentage|
134
+ expect(subject.disable(feature, actors_gate, flipper.actors(percentage))).to eq(true)
135
+ result = subject.get(feature)
136
+ expect(result[:percentage_of_actors]).to eq(percentage.to_s)
137
+ end
138
+ end
139
+
124
140
  it "can enable, disable and get value for percentage of time gate" do
125
141
  expect(subject.enable(feature, time_gate, flipper.time(10))).to eq(true)
126
142
  result = subject.get(feature)
@@ -131,6 +147,22 @@ shared_examples_for 'a flipper adapter' do
131
147
  expect(result[:percentage_of_time]).to eq('0')
132
148
  end
133
149
 
150
+ it "can enable percentage of time gate many times and consistently return values" do
151
+ (1..100).each do |percentage|
152
+ expect(subject.enable(feature, time_gate, flipper.time(percentage))).to eq(true)
153
+ result = subject.get(feature)
154
+ expect(result[:percentage_of_time]).to eq(percentage.to_s)
155
+ end
156
+ end
157
+
158
+ it "can disable percentage of time gate many times and consistently return values" do
159
+ (1..100).each do |percentage|
160
+ expect(subject.disable(feature, time_gate, flipper.time(percentage))).to eq(true)
161
+ result = subject.get(feature)
162
+ expect(result[:percentage_of_time]).to eq(percentage.to_s)
163
+ end
164
+ end
165
+
134
166
  it "converts boolean value to a string" do
135
167
  expect(subject.enable(feature, boolean_gate, flipper.boolean)).to eq(true)
136
168
  result = subject.get(feature)
@@ -0,0 +1,249 @@
1
+ module SharedAdapterTests
2
+ def setup
3
+ super
4
+ @flipper = Flipper.new(@adapter)
5
+ @actor_class = Struct.new(:flipper_id)
6
+ @feature = @flipper[:stats]
7
+ @boolean_gate = @feature.gate(:boolean)
8
+ @group_gate = @feature.gate(:group)
9
+ @actor_gate = @feature.gate(:actor)
10
+ @actors_gate = @feature.gate(:percentage_of_actors)
11
+ @time_gate = @feature.gate(:percentage_of_time)
12
+
13
+ Flipper.register(:admins) do |actor|
14
+ actor.respond_to?(:admin?) && actor.admin?
15
+ 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
+ end
249
+ end