flipper 0.7.0.beta2 → 0.7.0.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -0
- data/flipper.gemspec +1 -1
- data/lib/flipper/adapters/decorator.rb +1 -1
- data/lib/flipper/adapters/instrumented.rb +1 -1
- data/lib/flipper/adapters/memory.rb +1 -1
- data/lib/flipper/adapters/pstore.rb +166 -0
- data/lib/flipper/dsl.rb +3 -3
- data/lib/flipper/feature.rb +39 -43
- data/lib/flipper/gate.rb +0 -4
- data/lib/flipper/gates/actor.rb +0 -9
- data/lib/flipper/gates/boolean.rb +10 -9
- data/lib/flipper/gates/group.rb +1 -10
- data/lib/flipper/gates/percentage_of_actors.rb +1 -9
- data/lib/flipper/gates/percentage_of_time.rb +0 -8
- data/lib/flipper/type.rb +5 -0
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +1 -1
- data/spec/flipper/adapters/pstore_spec.rb +13 -0
- data/spec/flipper/feature_spec.rb +81 -33
- data/spec/flipper/gates/actor_spec.rb +0 -15
- data/spec/flipper/gates/boolean_spec.rb +30 -14
- data/spec/flipper/gates/group_spec.rb +0 -15
- data/spec/flipper/gates/percentage_of_actors_spec.rb +0 -14
- data/spec/flipper/gates/percentage_of_time_spec.rb +0 -14
- data/spec/helper.rb +4 -9
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b952f92d9398ba28267085a3084fbc659e571f6f
|
4
|
+
data.tar.gz: 4e535ba61ef3e38e24f419489c305c17cbdf58e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5457f2328718155eef1ff0014346179f5bec08cd594a9c6f31ccd3e02977a91089477bffee66d53438bf849c04d66b9ca73dc56e06653f8a4dbb6168e69e56c8
|
7
|
+
data.tar.gz: 9c7da626025b191605e70beb3b6cdefd190d124c429354b54451472e7d3ea3fb48b90218356da8843fd30955529981769b320404e2c07e7ffef92da2cf787e40
|
data/README.md
CHANGED
@@ -250,6 +250,35 @@ A good place to start when creating your own adapter is to copy one of the adapt
|
|
250
250
|
|
251
251
|
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.
|
252
252
|
|
253
|
+
## Instrumentation
|
254
|
+
|
255
|
+
Flipper comes with automatic instrumentation. By default these work with ActiveSupport::Notifications, but only require the pieces of ActiveSupport that are needed and only do so if you actually attempt to require the instrumentation files listed below.
|
256
|
+
|
257
|
+
To use the log subscriber:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
# Gemfile
|
261
|
+
gem "activesupport"
|
262
|
+
|
263
|
+
# config/initializers/flipper.rb (or wherever you want it)
|
264
|
+
require "flipper/instrumentation/log_subscriber"
|
265
|
+
```
|
266
|
+
|
267
|
+
To use the statsd instrumentation:
|
268
|
+
|
269
|
+
```ruby
|
270
|
+
# Gemfile
|
271
|
+
gem "activesupport"
|
272
|
+
gem "statsd-ruby"
|
273
|
+
|
274
|
+
# config/initializers/flipper.rb (or wherever you want it)
|
275
|
+
require "flipper/instrumentation/statsd"
|
276
|
+
Flipper::Instrumentation::StatsdSubscriber.client = Statsd.new # or whatever your statsd instance is
|
277
|
+
```
|
278
|
+
|
279
|
+
You can also do whatever you want with the instrumented events. Check out [this example](https://github.com/jnunemaker/flipper/blob/master/examples/instrumentation.rb) for more.
|
280
|
+
|
281
|
+
|
253
282
|
## Optimization
|
254
283
|
|
255
284
|
One optimization that flipper provides is a memoizing middleware. The memoizing middleware ensures that you only make one adapter call per feature per request.
|
data/flipper.gemspec
CHANGED
@@ -4,7 +4,6 @@ require File.expand_path('../lib/flipper/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["John Nunemaker"]
|
6
6
|
gem.email = ["nunemaker@gmail.com"]
|
7
|
-
gem.description = %q{Feature flipper for any adapter}
|
8
7
|
gem.summary = %q{Feature flipper for any adapter}
|
9
8
|
gem.homepage = "http://jnunemaker.github.com/flipper"
|
10
9
|
|
@@ -14,4 +13,5 @@ Gem::Specification.new do |gem|
|
|
14
13
|
gem.name = "flipper"
|
15
14
|
gem.require_paths = ["lib"]
|
16
15
|
gem.version = Flipper::VERSION
|
16
|
+
gem.license = "MIT"
|
17
17
|
end
|
@@ -20,7 +20,7 @@ module Flipper
|
|
20
20
|
def initialize(adapter, options = {})
|
21
21
|
super(adapter)
|
22
22
|
@name = :instrumented
|
23
|
-
@instrumenter = options.fetch(:instrumenter,
|
23
|
+
@instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Public
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require "pstore"
|
2
|
+
require "set"
|
3
|
+
|
4
|
+
module Flipper
|
5
|
+
module Adapters
|
6
|
+
class PStore
|
7
|
+
include Adapter
|
8
|
+
|
9
|
+
FeaturesKey = :flipper_features
|
10
|
+
|
11
|
+
# Public: The name of the adapter.
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
# Public: The path to where the file is stored.
|
15
|
+
attr_reader :path
|
16
|
+
|
17
|
+
# Public
|
18
|
+
def initialize(path = "flipper.pstore")
|
19
|
+
@path = path
|
20
|
+
@store = ::PStore.new(path)
|
21
|
+
@name = :pstore
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: The set of known features.
|
25
|
+
def features
|
26
|
+
set_members FeaturesKey
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Adds a feature to the set of known features.
|
30
|
+
def add(feature)
|
31
|
+
set_add FeaturesKey, feature.key
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: Removes a feature from the set of known features and clears
|
36
|
+
# all the values for the feature.
|
37
|
+
def remove(feature)
|
38
|
+
set_delete FeaturesKey, feature.key
|
39
|
+
clear(feature)
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Clears all the gate values for a feature.
|
44
|
+
def clear(feature)
|
45
|
+
feature.gates.each do |gate|
|
46
|
+
delete key(feature, gate)
|
47
|
+
end
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Public
|
52
|
+
def get(feature)
|
53
|
+
result = {}
|
54
|
+
|
55
|
+
feature.gates.each do |gate|
|
56
|
+
result[gate.key] = case gate.data_type
|
57
|
+
when :boolean, :integer
|
58
|
+
read key(feature, gate)
|
59
|
+
when :set
|
60
|
+
set_members key(feature, gate)
|
61
|
+
else
|
62
|
+
raise "#{gate} is not supported by this adapter yet"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
result
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public
|
70
|
+
def enable(feature, gate, thing)
|
71
|
+
case gate.data_type
|
72
|
+
when :boolean, :integer
|
73
|
+
write key(feature, gate), thing.value.to_s
|
74
|
+
when :set
|
75
|
+
set_add key(feature, gate), thing.value.to_s
|
76
|
+
else
|
77
|
+
raise "#{gate} is not supported by this adapter yet"
|
78
|
+
end
|
79
|
+
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
# Public
|
84
|
+
def disable(feature, gate, thing)
|
85
|
+
case gate.data_type
|
86
|
+
when :boolean
|
87
|
+
clear(feature)
|
88
|
+
when :integer
|
89
|
+
write key(feature, gate), thing.value.to_s
|
90
|
+
when :set
|
91
|
+
set_delete key(feature, gate), thing.value.to_s
|
92
|
+
else
|
93
|
+
raise "#{gate} is not supported by this adapter yet"
|
94
|
+
end
|
95
|
+
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
# Public
|
100
|
+
def inspect
|
101
|
+
attributes = [
|
102
|
+
"name=#{@name.inspect}",
|
103
|
+
"path=#{@path.inspect}",
|
104
|
+
"store=#{@store}",
|
105
|
+
]
|
106
|
+
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# Private
|
112
|
+
def key(feature, gate)
|
113
|
+
"#{feature.key}/#{gate.key}"
|
114
|
+
end
|
115
|
+
|
116
|
+
# Private
|
117
|
+
def read(key)
|
118
|
+
@store.transaction do
|
119
|
+
@store[key.to_s]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Private
|
124
|
+
def write(key, value)
|
125
|
+
@store.transaction do
|
126
|
+
@store[key.to_s] = value.to_s
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Private
|
131
|
+
def delete(key)
|
132
|
+
@store.transaction do
|
133
|
+
@store.delete(key.to_s)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Private
|
138
|
+
def set_add(key, value)
|
139
|
+
set_members(key) do |members|
|
140
|
+
members.add(value.to_s)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Private
|
145
|
+
def set_delete(key, value)
|
146
|
+
set_members(key) do |members|
|
147
|
+
members.delete(value.to_s)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Private
|
152
|
+
def set_members(key)
|
153
|
+
key = key.to_s
|
154
|
+
@store.transaction do
|
155
|
+
@store[key] ||= Set.new
|
156
|
+
|
157
|
+
if block_given?
|
158
|
+
yield @store[key]
|
159
|
+
else
|
160
|
+
@store[key]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/lib/flipper/dsl.rb
CHANGED
@@ -16,12 +16,12 @@ module Flipper
|
|
16
16
|
# options - The Hash of options.
|
17
17
|
# :instrumenter - What should be used to instrument all the things.
|
18
18
|
def initialize(adapter, options = {})
|
19
|
-
@instrumenter = options.fetch(:instrumenter,
|
19
|
+
@instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
|
20
20
|
|
21
|
-
instrumented =
|
21
|
+
instrumented = Adapters::Instrumented.new(adapter, {
|
22
22
|
:instrumenter => @instrumenter,
|
23
23
|
})
|
24
|
-
memoized =
|
24
|
+
memoized = Adapters::Memoizable.new(instrumented)
|
25
25
|
@adapter = memoized
|
26
26
|
|
27
27
|
@memoized_features = {}
|
data/lib/flipper/feature.rb
CHANGED
@@ -35,14 +35,14 @@ module Flipper
|
|
35
35
|
def initialize(name, adapter, options = {})
|
36
36
|
@name = name
|
37
37
|
@key = name.to_s
|
38
|
-
@instrumenter = options.fetch(:instrumenter,
|
38
|
+
@instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
|
39
39
|
@adapter = adapter
|
40
40
|
end
|
41
41
|
|
42
42
|
# Public: Enable this feature for something.
|
43
43
|
#
|
44
44
|
# Returns the result of Adapter#enable.
|
45
|
-
def enable(thing =
|
45
|
+
def enable(thing = true)
|
46
46
|
instrument(:enable, thing) { |payload|
|
47
47
|
adapter.add self
|
48
48
|
|
@@ -56,7 +56,7 @@ module Flipper
|
|
56
56
|
# Public: Disable this feature for something.
|
57
57
|
#
|
58
58
|
# Returns the result of Adapter#disable.
|
59
|
-
def disable(thing =
|
59
|
+
def disable(thing = false)
|
60
60
|
instrument(:disable, thing) { |payload|
|
61
61
|
adapter.add self
|
62
62
|
|
@@ -110,7 +110,7 @@ module Flipper
|
|
110
110
|
#
|
111
111
|
# Returns result of enable.
|
112
112
|
def enable_group(group)
|
113
|
-
enable
|
113
|
+
enable Types::Group.wrap(group)
|
114
114
|
end
|
115
115
|
|
116
116
|
# Public: Enables a feature a percentage of time.
|
@@ -150,7 +150,7 @@ module Flipper
|
|
150
150
|
#
|
151
151
|
# Returns result of disable.
|
152
152
|
def disable_group(group)
|
153
|
-
disable
|
153
|
+
disable Types::Group.wrap(group)
|
154
154
|
end
|
155
155
|
|
156
156
|
# Public: Disables a feature a percentage of time.
|
@@ -176,10 +176,12 @@ module Flipper
|
|
176
176
|
# Public: Returns state for feature (:on, :off, or :conditional).
|
177
177
|
def state
|
178
178
|
values = gate_values
|
179
|
+
boolean = gate(:boolean)
|
180
|
+
non_boolean_gates = gates - [boolean]
|
179
181
|
|
180
|
-
if
|
182
|
+
if values.boolean || values.percentage_of_actors == 100 || values.percentage_of_time == 100
|
181
183
|
:on
|
182
|
-
elsif
|
184
|
+
elsif non_boolean_gates.detect { |gate| gate.enabled?(values[gate.key]) }
|
183
185
|
:conditional
|
184
186
|
else
|
185
187
|
:off
|
@@ -202,23 +204,6 @@ module Flipper
|
|
202
204
|
state == :conditional
|
203
205
|
end
|
204
206
|
|
205
|
-
# Public: Human readable description of the enabled-ness of the feature.
|
206
|
-
def description
|
207
|
-
values = gate_values
|
208
|
-
conditional_gates = conditional_gates(values)
|
209
|
-
|
210
|
-
if boolean_gate.enabled?(values.boolean) || !conditional_gates.any?
|
211
|
-
boolean_gate.description(values.boolean).capitalize
|
212
|
-
else
|
213
|
-
fragments = conditional_gates.map { |gate|
|
214
|
-
value = values[gate.key]
|
215
|
-
gate.description(value)
|
216
|
-
}
|
217
|
-
|
218
|
-
"Enabled for #{fragments.join(', ')}"
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
207
|
# Public: Returns the raw gate values stored by the adapter.
|
223
208
|
def gate_values
|
224
209
|
GateValues.new(adapter.get(self))
|
@@ -274,6 +259,35 @@ module Flipper
|
|
274
259
|
gate_values.percentage_of_time
|
275
260
|
end
|
276
261
|
|
262
|
+
# Public: Get the gates that have been enabled for the feature.
|
263
|
+
#
|
264
|
+
# Returns an Array of Flipper::Gate instances.
|
265
|
+
def enabled_gates
|
266
|
+
values = gate_values
|
267
|
+
gates.select { |gate| gate.enabled?(values[gate.key]) }
|
268
|
+
end
|
269
|
+
|
270
|
+
# Public: Get the names of the enabled gates.
|
271
|
+
#
|
272
|
+
# Returns an Array of gate names.
|
273
|
+
def enabled_gate_names
|
274
|
+
enabled_gates.map(&:name)
|
275
|
+
end
|
276
|
+
|
277
|
+
# Public: Get the gates that have not been enabled for the feature.
|
278
|
+
#
|
279
|
+
# Returns an Array of Flipper::Gate instances.
|
280
|
+
def disabled_gates
|
281
|
+
gates - enabled_gates
|
282
|
+
end
|
283
|
+
|
284
|
+
# Public: Get the names of the disabled gates.
|
285
|
+
#
|
286
|
+
# Returns an Array of gate names.
|
287
|
+
def disabled_gate_names
|
288
|
+
disabled_gates.map(&:name)
|
289
|
+
end
|
290
|
+
|
277
291
|
# Public: Returns the string representation of the feature.
|
278
292
|
def to_s
|
279
293
|
name.to_s
|
@@ -289,7 +303,7 @@ module Flipper
|
|
289
303
|
attributes = [
|
290
304
|
"name=#{name.inspect}",
|
291
305
|
"state=#{state.inspect}",
|
292
|
-
"
|
306
|
+
"enabled_gate_names=#{enabled_gate_names.inspect}",
|
293
307
|
"adapter=#{adapter.name.inspect}",
|
294
308
|
]
|
295
309
|
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
@@ -328,24 +342,6 @@ module Flipper
|
|
328
342
|
|
329
343
|
private
|
330
344
|
|
331
|
-
# Private: Get the boolean gate.
|
332
|
-
def boolean_gate
|
333
|
-
@boolean_gate ||= gate(:boolean)
|
334
|
-
end
|
335
|
-
|
336
|
-
# Private: Get all gates except the boolean gate.
|
337
|
-
def non_boolean_gates
|
338
|
-
@non_boolean_gates ||= gates - [boolean_gate]
|
339
|
-
end
|
340
|
-
|
341
|
-
# Private: Get all non boolean gates that are enabled in some way.
|
342
|
-
def conditional_gates(gate_values)
|
343
|
-
non_boolean_gates.select { |gate|
|
344
|
-
value = gate_values[gate.key]
|
345
|
-
gate.enabled?(value)
|
346
|
-
}
|
347
|
-
end
|
348
|
-
|
349
345
|
# Private: Instrument a feature operation.
|
350
346
|
def instrument(operation, thing)
|
351
347
|
payload = {
|
data/lib/flipper/gate.rb
CHANGED
data/lib/flipper/gates/actor.rb
CHANGED
@@ -15,15 +15,6 @@ module Flipper
|
|
15
15
|
:set
|
16
16
|
end
|
17
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
18
|
def enabled?(value)
|
28
19
|
!Typecast.to_set(value).empty?
|
29
20
|
end
|
@@ -15,14 +15,6 @@ module Flipper
|
|
15
15
|
:boolean
|
16
16
|
end
|
17
17
|
|
18
|
-
def description(value)
|
19
|
-
if enabled?(value)
|
20
|
-
'Enabled'
|
21
|
-
else
|
22
|
-
'Disabled'
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
18
|
def enabled?(value)
|
27
19
|
Typecast.to_boolean(value)
|
28
20
|
end
|
@@ -35,8 +27,17 @@ module Flipper
|
|
35
27
|
value
|
36
28
|
end
|
37
29
|
|
30
|
+
def wrap(thing)
|
31
|
+
Types::Boolean.wrap(thing)
|
32
|
+
end
|
33
|
+
|
38
34
|
def protects?(thing)
|
39
|
-
thing
|
35
|
+
case thing
|
36
|
+
when Types::Boolean, TrueClass, FalseClass
|
37
|
+
true
|
38
|
+
else
|
39
|
+
false
|
40
|
+
end
|
40
41
|
end
|
41
42
|
end
|
42
43
|
end
|
data/lib/flipper/gates/group.rb
CHANGED
@@ -15,15 +15,6 @@ module Flipper
|
|
15
15
|
:set
|
16
16
|
end
|
17
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
18
|
def enabled?(value)
|
28
19
|
!Typecast.to_set(value).empty?
|
29
20
|
end
|
@@ -47,7 +38,7 @@ module Flipper
|
|
47
38
|
end
|
48
39
|
|
49
40
|
def protects?(thing)
|
50
|
-
thing.is_a?(
|
41
|
+
thing.is_a?(Types::Group)
|
51
42
|
end
|
52
43
|
end
|
53
44
|
end
|
@@ -17,14 +17,6 @@ module Flipper
|
|
17
17
|
:integer
|
18
18
|
end
|
19
19
|
|
20
|
-
def description(value)
|
21
|
-
if enabled?(value)
|
22
|
-
"#{value}% of actors"
|
23
|
-
else
|
24
|
-
'disabled'
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
20
|
def enabled?(value)
|
29
21
|
Typecast.to_integer(value) > 0
|
30
22
|
end
|
@@ -46,7 +38,7 @@ module Flipper
|
|
46
38
|
end
|
47
39
|
|
48
40
|
def protects?(thing)
|
49
|
-
thing.is_a?(
|
41
|
+
thing.is_a?(Types::PercentageOfActors)
|
50
42
|
end
|
51
43
|
end
|
52
44
|
end
|
data/lib/flipper/type.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
module Flipper
|
2
2
|
# Internal: Root class for all flipper types. You should never need to use this.
|
3
3
|
class Type
|
4
|
+
def self.wrap(value_or_instance)
|
5
|
+
return value_or_instance if value_or_instance.is_a?(self)
|
6
|
+
new(value_or_instance)
|
7
|
+
end
|
8
|
+
|
4
9
|
def value
|
5
10
|
raise 'Not implemented'
|
6
11
|
end
|
data/lib/flipper/version.rb
CHANGED
data/lib/flipper.rb
CHANGED
@@ -68,7 +68,7 @@ module Flipper
|
|
68
68
|
# Raises Flipper::GroupNotRegistered if group is not registered.
|
69
69
|
def self.group(name)
|
70
70
|
groups_registry.get(name)
|
71
|
-
rescue
|
71
|
+
rescue Registry::KeyNotFound => e
|
72
72
|
raise GroupNotRegistered, "Group #{e.key.inspect} has not been registered"
|
73
73
|
end
|
74
74
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'flipper/adapters/pstore'
|
3
|
+
require 'flipper/spec/shared_adapter_specs'
|
4
|
+
|
5
|
+
describe Flipper::Adapters::PStore do
|
6
|
+
subject { described_class.new(FlipperRoot.join("tmp", "flipper.pstore")) }
|
7
|
+
|
8
|
+
it_should_behave_like 'a flipper adapter'
|
9
|
+
|
10
|
+
it "defaults path to flipper.pstore" do
|
11
|
+
described_class.new.path.should eq("flipper.pstore")
|
12
|
+
end
|
13
|
+
end
|
@@ -97,8 +97,13 @@ describe Flipper::Feature do
|
|
97
97
|
string.should include('Flipper::Feature')
|
98
98
|
string.should include('name=:search')
|
99
99
|
string.should include('state=:off')
|
100
|
-
string.should include('
|
100
|
+
string.should include('enabled_gate_names=[]')
|
101
101
|
string.should include("adapter=#{subject.adapter.name.inspect}")
|
102
|
+
|
103
|
+
subject.enable
|
104
|
+
string = subject.inspect
|
105
|
+
string.should include('state=:on')
|
106
|
+
string.should include('enabled_gate_names=[:boolean]')
|
102
107
|
end
|
103
108
|
end
|
104
109
|
|
@@ -178,21 +183,21 @@ describe Flipper::Feature do
|
|
178
183
|
end
|
179
184
|
end
|
180
185
|
|
181
|
-
context "
|
186
|
+
context "percentage of time set to 100" do
|
182
187
|
before do
|
183
|
-
subject.
|
188
|
+
subject.enable_percentage_of_time 100
|
184
189
|
end
|
185
190
|
|
186
|
-
it "returns :
|
187
|
-
subject.state.should be(:
|
191
|
+
it "returns :on" do
|
192
|
+
subject.state.should be(:on)
|
188
193
|
end
|
189
194
|
|
190
|
-
it "returns
|
191
|
-
subject.on?.should be(
|
195
|
+
it "returns true for on?" do
|
196
|
+
subject.on?.should be(true)
|
192
197
|
end
|
193
198
|
|
194
|
-
it "returns
|
195
|
-
subject.off?.should be(
|
199
|
+
it "returns false for off?" do
|
200
|
+
subject.off?.should be(false)
|
196
201
|
end
|
197
202
|
|
198
203
|
it "returns false for conditional?" do
|
@@ -200,59 +205,69 @@ describe Flipper::Feature do
|
|
200
205
|
end
|
201
206
|
end
|
202
207
|
|
203
|
-
context "
|
208
|
+
context "percentage of actors set to 100" do
|
204
209
|
before do
|
205
|
-
subject.
|
210
|
+
subject.enable_percentage_of_actors 100
|
206
211
|
end
|
207
212
|
|
208
|
-
it "returns :
|
209
|
-
subject.state.should be(:
|
213
|
+
it "returns :on" do
|
214
|
+
subject.state.should be(:on)
|
210
215
|
end
|
211
216
|
|
212
|
-
it "returns
|
213
|
-
subject.on?.should be(
|
217
|
+
it "returns true for on?" do
|
218
|
+
subject.on?.should be(true)
|
214
219
|
end
|
215
220
|
|
216
221
|
it "returns false for off?" do
|
217
222
|
subject.off?.should be(false)
|
218
223
|
end
|
219
224
|
|
220
|
-
it "returns
|
221
|
-
subject.conditional?.should be(
|
225
|
+
it "returns false for conditional?" do
|
226
|
+
subject.conditional?.should be(false)
|
222
227
|
end
|
223
228
|
end
|
224
|
-
end
|
225
229
|
|
226
|
-
|
227
|
-
context "fully on" do
|
230
|
+
context "fully off" do
|
228
231
|
before do
|
229
|
-
subject.
|
232
|
+
subject.disable
|
230
233
|
end
|
231
234
|
|
232
|
-
it "returns
|
233
|
-
subject.
|
235
|
+
it "returns :off" do
|
236
|
+
subject.state.should be(:off)
|
234
237
|
end
|
235
|
-
end
|
236
238
|
|
237
|
-
|
238
|
-
|
239
|
-
|
239
|
+
it "returns false for on?" do
|
240
|
+
subject.on?.should be(false)
|
241
|
+
end
|
242
|
+
|
243
|
+
it "returns true for off?" do
|
244
|
+
subject.off?.should be(true)
|
240
245
|
end
|
241
246
|
|
242
|
-
it "returns
|
243
|
-
subject.
|
247
|
+
it "returns false for conditional?" do
|
248
|
+
subject.conditional?.should be(false)
|
244
249
|
end
|
245
250
|
end
|
246
251
|
|
247
252
|
context "partially on" do
|
248
253
|
before do
|
249
|
-
actor = Struct.new(:flipper_id).new(5)
|
250
254
|
subject.enable Flipper::Types::PercentageOfTime.new(5)
|
251
|
-
subject.enable actor
|
252
255
|
end
|
253
256
|
|
254
|
-
it "returns
|
255
|
-
subject.
|
257
|
+
it "returns :conditional" do
|
258
|
+
subject.state.should be(:conditional)
|
259
|
+
end
|
260
|
+
|
261
|
+
it "returns false for on?" do
|
262
|
+
subject.on?.should be(false)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "returns false for off?" do
|
266
|
+
subject.off?.should be(false)
|
267
|
+
end
|
268
|
+
|
269
|
+
it "returns true for conditional?" do
|
270
|
+
subject.conditional?.should be(true)
|
256
271
|
end
|
257
272
|
end
|
258
273
|
end
|
@@ -623,4 +638,37 @@ describe Flipper::Feature do
|
|
623
638
|
end
|
624
639
|
end
|
625
640
|
end
|
641
|
+
|
642
|
+
describe "#enabled/disabled_gates" do
|
643
|
+
before do
|
644
|
+
subject.enable_percentage_of_time 5
|
645
|
+
subject.enable_percentage_of_actors 5
|
646
|
+
end
|
647
|
+
|
648
|
+
it "can return enabled gates" do
|
649
|
+
subject.enabled_gates.map(&:name).to_set.should eq(Set[
|
650
|
+
:percentage_of_actors,
|
651
|
+
:percentage_of_time,
|
652
|
+
])
|
653
|
+
|
654
|
+
subject.enabled_gate_names.to_set.should eq(Set[
|
655
|
+
:percentage_of_actors,
|
656
|
+
:percentage_of_time,
|
657
|
+
])
|
658
|
+
end
|
659
|
+
|
660
|
+
it "can return disabled gates" do
|
661
|
+
subject.disabled_gates.map(&:name).to_set.should eq(Set[
|
662
|
+
:actor,
|
663
|
+
:boolean,
|
664
|
+
:group,
|
665
|
+
])
|
666
|
+
|
667
|
+
subject.disabled_gate_names.to_set.should eq(Set[
|
668
|
+
:actor,
|
669
|
+
:boolean,
|
670
|
+
:group,
|
671
|
+
])
|
672
|
+
end
|
673
|
+
end
|
626
674
|
end
|
@@ -6,19 +6,4 @@ describe Flipper::Gates::Actor do
|
|
6
6
|
subject {
|
7
7
|
described_class.new
|
8
8
|
}
|
9
|
-
|
10
|
-
describe "#description" do
|
11
|
-
context "with actors in set" do
|
12
|
-
it "returns text" do
|
13
|
-
values = Set['bacon', 'ham']
|
14
|
-
subject.description(values).should eq('actors ("bacon", "ham")')
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
context "with no actors in set" do
|
19
|
-
it "returns disabled" do
|
20
|
-
subject.description(Set.new).should eq('disabled')
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
9
|
end
|
@@ -7,20 +7,6 @@ describe Flipper::Gates::Boolean do
|
|
7
7
|
described_class.new
|
8
8
|
}
|
9
9
|
|
10
|
-
describe "#description" do
|
11
|
-
context "for enabled" do
|
12
|
-
it "returns Enabled" do
|
13
|
-
subject.description(true).should eq('Enabled')
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
context "for disabled" do
|
18
|
-
it "returns Disabled" do
|
19
|
-
subject.description(false).should eq('Disabled')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
10
|
describe "#enabled?" do
|
25
11
|
context "for true value" do
|
26
12
|
it "returns true" do
|
@@ -48,4 +34,34 @@ describe Flipper::Gates::Boolean do
|
|
48
34
|
end
|
49
35
|
end
|
50
36
|
end
|
37
|
+
|
38
|
+
describe "#protects?" do
|
39
|
+
it "returns true for boolean type" do
|
40
|
+
subject.protects?(Flipper::Types::Boolean.new(true)).should be(true)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns true for true" do
|
44
|
+
subject.protects?(true).should be(true)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns true for false" do
|
48
|
+
subject.protects?(false).should be(true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#wrap" do
|
53
|
+
it "returns boolean type for boolean type" do
|
54
|
+
subject.wrap(Flipper::Types::Boolean.new(true)).should be_instance_of(Flipper::Types::Boolean)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns boolean type for true" do
|
58
|
+
subject.wrap(true).should be_instance_of(Flipper::Types::Boolean)
|
59
|
+
subject.wrap(true).value.should be(true)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "returns boolean type for true" do
|
63
|
+
subject.wrap(false).should be_instance_of(Flipper::Types::Boolean)
|
64
|
+
subject.wrap(false).value.should be(false)
|
65
|
+
end
|
66
|
+
end
|
51
67
|
end
|
@@ -7,21 +7,6 @@ describe Flipper::Gates::Group do
|
|
7
7
|
described_class.new
|
8
8
|
}
|
9
9
|
|
10
|
-
describe "#description" do
|
11
|
-
context "with groups in set" do
|
12
|
-
it "returns text" do
|
13
|
-
values = Set['bacon', 'ham']
|
14
|
-
subject.description(values).should eq('groups (:bacon, :ham)')
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
context "with no groups in set" do
|
19
|
-
it "returns disabled" do
|
20
|
-
subject.description(Set.new).should eq('disabled')
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
10
|
describe "#open?" do
|
26
11
|
context "with a group in adapter, but not registered" do
|
27
12
|
before do
|
@@ -7,20 +7,6 @@ describe Flipper::Gates::PercentageOfActors do
|
|
7
7
|
described_class.new
|
8
8
|
}
|
9
9
|
|
10
|
-
describe "#description" do
|
11
|
-
context "when enabled" do
|
12
|
-
it "returns text" do
|
13
|
-
subject.description(22).should eq('22% of actors')
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
context "when disabled" do
|
18
|
-
it "returns disabled" do
|
19
|
-
subject.description(0).should eq('disabled')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
10
|
describe "#open?" do
|
25
11
|
context "when compared against two features" do
|
26
12
|
let(:percentage) { 0.05 }
|
@@ -6,18 +6,4 @@ describe Flipper::Gates::PercentageOfTime do
|
|
6
6
|
subject {
|
7
7
|
described_class.new
|
8
8
|
}
|
9
|
-
|
10
|
-
describe "#description" do
|
11
|
-
context "when enabled" do
|
12
|
-
it "returns text" do
|
13
|
-
subject.description(22).should eq('22% of the time')
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
context "when disabled" do
|
18
|
-
it "returns disabled" do
|
19
|
-
subject.description(0).should eq('disabled')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
9
|
end
|
data/spec/helper.rb
CHANGED
@@ -3,9 +3,9 @@ $:.unshift(File.expand_path('../../lib', __FILE__))
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'logger'
|
5
5
|
|
6
|
-
|
7
|
-
lib_path =
|
8
|
-
log_path =
|
6
|
+
FlipperRoot = Pathname(__FILE__).dirname.join('..').expand_path
|
7
|
+
lib_path = FlipperRoot.join('lib')
|
8
|
+
log_path = FlipperRoot.join('log')
|
9
9
|
log_path.mkpath
|
10
10
|
|
11
11
|
require 'rubygems'
|
@@ -15,14 +15,9 @@ Bundler.setup(:default)
|
|
15
15
|
|
16
16
|
require 'flipper'
|
17
17
|
|
18
|
-
Dir[
|
18
|
+
Dir[FlipperRoot.join("spec/support/**/*.rb")].each { |f| require f }
|
19
19
|
|
20
20
|
RSpec.configure do |config|
|
21
|
-
config.filter_run :focus => true
|
22
|
-
config.alias_example_to :fit, :focused => true
|
23
|
-
config.alias_example_to :xit, :pending => true
|
24
|
-
config.run_all_when_everything_filtered = true
|
25
|
-
|
26
21
|
config.before(:each) do
|
27
22
|
Flipper.unregister_groups
|
28
23
|
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0.
|
4
|
+
version: 0.7.0.beta3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description:
|
14
14
|
email:
|
15
15
|
- nunemaker@gmail.com
|
16
16
|
executables: []
|
@@ -42,6 +42,7 @@ files:
|
|
42
42
|
- lib/flipper/adapters/memoizable.rb
|
43
43
|
- lib/flipper/adapters/memory.rb
|
44
44
|
- lib/flipper/adapters/operation_logger.rb
|
45
|
+
- lib/flipper/adapters/pstore.rb
|
45
46
|
- lib/flipper/decorator.rb
|
46
47
|
- lib/flipper/dsl.rb
|
47
48
|
- lib/flipper/errors.rb
|
@@ -81,6 +82,7 @@ files:
|
|
81
82
|
- spec/flipper/adapters/memoizable_spec.rb
|
82
83
|
- spec/flipper/adapters/memory_spec.rb
|
83
84
|
- spec/flipper/adapters/operation_logger_spec.rb
|
85
|
+
- spec/flipper/adapters/pstore_spec.rb
|
84
86
|
- spec/flipper/dsl_spec.rb
|
85
87
|
- spec/flipper/feature_spec.rb
|
86
88
|
- spec/flipper/gate_spec.rb
|
@@ -109,7 +111,8 @@ files:
|
|
109
111
|
- spec/integration_spec.rb
|
110
112
|
- spec/support/fake_udp_socket.rb
|
111
113
|
homepage: http://jnunemaker.github.com/flipper
|
112
|
-
licenses:
|
114
|
+
licenses:
|
115
|
+
- MIT
|
113
116
|
metadata: {}
|
114
117
|
post_install_message:
|
115
118
|
rdoc_options: []
|
@@ -136,6 +139,7 @@ test_files:
|
|
136
139
|
- spec/flipper/adapters/memoizable_spec.rb
|
137
140
|
- spec/flipper/adapters/memory_spec.rb
|
138
141
|
- spec/flipper/adapters/operation_logger_spec.rb
|
142
|
+
- spec/flipper/adapters/pstore_spec.rb
|
139
143
|
- spec/flipper/dsl_spec.rb
|
140
144
|
- spec/flipper/feature_spec.rb
|
141
145
|
- spec/flipper/gate_spec.rb
|