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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fcc306a0110366af90921368bda9c8aeffa353ca
4
- data.tar.gz: a1b1808be6c648f99f14e9e033dd945a4152744a
3
+ metadata.gz: b952f92d9398ba28267085a3084fbc659e571f6f
4
+ data.tar.gz: 4e535ba61ef3e38e24f419489c305c17cbdf58e1
5
5
  SHA512:
6
- metadata.gz: 16397be35aa9c521e74c15046bd2efacb8537d9460f0e4dc9ef3ca212e46d320791d0a22fd1dbacd058776feab6ea7cd91e6af7855b7c9e297bcca75342ea641
7
- data.tar.gz: e70319c0460a878e2f861a637715cc749f76992d08f3ee6d17a149b05ea847a49fb665d2a77a604981dac8366e09fc3740c08a59ba35cdba027952b4fb220a7e
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
@@ -3,7 +3,7 @@ require 'flipper/decorator'
3
3
  module Flipper
4
4
  module Adapters
5
5
  class Decorator < ::Flipper::Decorator
6
- include Flipper::Adapter
6
+ include Adapter
7
7
  end
8
8
  end
9
9
  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, Flipper::Instrumenters::Noop)
23
+ @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
24
24
  end
25
25
 
26
26
  # Public
@@ -3,7 +3,7 @@ require 'set'
3
3
  module Flipper
4
4
  module Adapters
5
5
  class Memory
6
- include Flipper::Adapter
6
+ include Adapter
7
7
 
8
8
  FeaturesKey = :flipper_features
9
9
 
@@ -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, Flipper::Instrumenters::Noop)
19
+ @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
20
20
 
21
- instrumented = Flipper::Adapters::Instrumented.new(adapter, {
21
+ instrumented = Adapters::Instrumented.new(adapter, {
22
22
  :instrumenter => @instrumenter,
23
23
  })
24
- memoized = Flipper::Adapters::Memoizable.new(instrumented)
24
+ memoized = Adapters::Memoizable.new(instrumented)
25
25
  @adapter = memoized
26
26
 
27
27
  @memoized_features = {}
@@ -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, Flipper::Instrumenters::Noop)
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 = Types::Boolean.new(true))
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 = Types::Boolean.new(false))
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 Flipper::Types::Group.wrap(group)
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 Flipper::Types::Group.wrap(group)
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 boolean_gate.enabled?(values.boolean)
182
+ if values.boolean || values.percentage_of_actors == 100 || values.percentage_of_time == 100
181
183
  :on
182
- elsif conditional_gates(values).any?
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
- "description=#{description.inspect}",
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
@@ -26,10 +26,6 @@ module Flipper
26
26
  raise 'Not implemented'
27
27
  end
28
28
 
29
- def description(value)
30
- raise 'Not implemented'
31
- end
32
-
33
29
  # Internal: Check if a gate is open for a thing. Implemented in subclass.
34
30
  #
35
31
  # Returns true if gate open for thing, false if not.
@@ -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.is_a?(Flipper::Types::Boolean)
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
@@ -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?(Flipper::Types::Group)
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?(Flipper::Types::PercentageOfActors)
41
+ thing.is_a?(Types::PercentageOfActors)
50
42
  end
51
43
  end
52
44
  end
@@ -15,14 +15,6 @@ module Flipper
15
15
  :integer
16
16
  end
17
17
 
18
- def description(value)
19
- if enabled?(value)
20
- "#{value}% of the time"
21
- else
22
- 'disabled'
23
- end
24
- end
25
-
26
18
  def enabled?(value)
27
19
  Typecast.to_integer(value) > 0
28
20
  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
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = "0.7.0.beta2"
2
+ VERSION = "0.7.0.beta3"
3
3
  end
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 Flipper::Registry::KeyNotFound => e
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('description="Disabled"')
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 "fully off" do
186
+ context "percentage of time set to 100" do
182
187
  before do
183
- subject.disable
188
+ subject.enable_percentage_of_time 100
184
189
  end
185
190
 
186
- it "returns :off" do
187
- subject.state.should be(:off)
191
+ it "returns :on" do
192
+ subject.state.should be(:on)
188
193
  end
189
194
 
190
- it "returns false for on?" do
191
- subject.on?.should be(false)
195
+ it "returns true for on?" do
196
+ subject.on?.should be(true)
192
197
  end
193
198
 
194
- it "returns true for off?" do
195
- subject.off?.should be(true)
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 "partially on" do
208
+ context "percentage of actors set to 100" do
204
209
  before do
205
- subject.enable Flipper::Types::PercentageOfTime.new(5)
210
+ subject.enable_percentage_of_actors 100
206
211
  end
207
212
 
208
- it "returns :conditional" do
209
- subject.state.should be(:conditional)
213
+ it "returns :on" do
214
+ subject.state.should be(:on)
210
215
  end
211
216
 
212
- it "returns false for on?" do
213
- subject.on?.should be(false)
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 true for conditional?" do
221
- subject.conditional?.should be(true)
225
+ it "returns false for conditional?" do
226
+ subject.conditional?.should be(false)
222
227
  end
223
228
  end
224
- end
225
229
 
226
- describe "#description" do
227
- context "fully on" do
230
+ context "fully off" do
228
231
  before do
229
- subject.enable
232
+ subject.disable
230
233
  end
231
234
 
232
- it "returns enabled" do
233
- subject.description.should eq('Enabled')
235
+ it "returns :off" do
236
+ subject.state.should be(:off)
234
237
  end
235
- end
236
238
 
237
- context "fully off" do
238
- before do
239
- subject.disable
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 disabled" do
243
- subject.description.should eq('Disabled')
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 text" do
255
- subject.description.should eq('Enabled for actors ("5"), 5% of the time')
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
- root_path = Pathname(__FILE__).dirname.join('..').expand_path
7
- lib_path = root_path.join('lib')
8
- log_path = root_path.join('log')
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[root_path.join("spec/support/**/*.rb")].each { |f| require f }
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.beta2
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-16 00:00:00.000000000 Z
11
+ date: 2015-04-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Feature flipper for any adapter
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