flipper 0.7.5 → 0.8.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.
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