flipper 0.11.0.beta9 → 0.11.0.rc1

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: 82cc5ffd7d40e3b8930038e06718a378e22d5798
4
- data.tar.gz: 5cbc699f683fa1ee88efc46aff9577220ff63ac1
3
+ metadata.gz: 7fe7a64b6dfb6cbefeb2f111e3ea777018016ff5
4
+ data.tar.gz: 3c8e8754b21fdc37f9f90772ca893a0e16931f33
5
5
  SHA512:
6
- metadata.gz: acebdb682fce4b45c99267ef345e9eec161b7421ac957248445e235a7ebde7d2fd3a6181f2d5e2cc362c69ecf5637cfede638cbba49233e8658895b801a5140f
7
- data.tar.gz: 239a7733640400443d7adb7c4d745257eadbe61dd791330235728296395ff365292f9570a15f1994d054c08374486fbc85cc7fe65ee5ba4c99e01ae0c4804b1d
6
+ metadata.gz: 63863f70f3b8499f2d6f802eb6cfe20e19c84fad799c8b27d92de31b0ffba968f623b2dad4c387b9fe55c974d83a82f58253e8d683badac9ab9a4c084cafc216
7
+ data.tar.gz: 27f0085cfd6865a80dcd2722f81e2b516d3cd67b9702999fb90737cce263f6dd2afe5e013cbf470a6522447e70df7748f692b28cb1b7ca3a2e2ccf7e4cea4e42
data/.rubocop.yml CHANGED
@@ -40,3 +40,6 @@ Style/TrailingCommaInLiteral:
40
40
 
41
41
  RSpec/InstanceVariable:
42
42
  Enabled: false
43
+
44
+ Style/AccessorMethodName:
45
+ Enabled: false
data/Gemfile CHANGED
@@ -13,7 +13,7 @@ gem 'statsd-ruby', '~> 1.2.1'
13
13
  gem 'rspec', '~> 3.0'
14
14
  gem 'rack-test', '~> 0.6.3'
15
15
  gem 'sqlite3', '~> 1.3.11'
16
- gem 'rails', "~> #{ENV['RAILS_VERSION'] || '4.2.5'}"
16
+ gem 'rails', "~> #{ENV['RAILS_VERSION'] || '5.1.4'}"
17
17
  gem 'minitest', '~> 5.8.0'
18
18
  gem 'rubocop', '~> 0.45.0'
19
19
  gem 'rubocop-rspec', '= 1.5.1'
@@ -1,17 +1,9 @@
1
1
  require File.expand_path('../example_setup', __FILE__)
2
-
2
+ require_relative 'active_record/ar_setup'
3
3
  require 'flipper'
4
4
  require 'flipper/adapters/redis'
5
5
  require 'flipper/adapters/active_record'
6
6
 
7
- # Active Record boiler plate, feel free to ignore.
8
- ActiveRecord::Base.establish_connection({
9
- adapter: 'sqlite3',
10
- database: ':memory:',
11
- })
12
- require 'generators/flipper/templates/migration'
13
- CreateFlipperTables.up
14
-
15
7
  # Say you are using redis...
16
8
  redis_adapter = Flipper::Adapters::Redis.new(Redis.new)
17
9
  redis_flipper = Flipper.new(redis_adapter)
@@ -9,6 +9,7 @@ module Flipper
9
9
  include ::Flipper::Adapter
10
10
 
11
11
  FeaturesKey = :flipper_features
12
+ GetAllKey = :all_memoized
12
13
 
13
14
  # Internal
14
15
  attr_reader :cache
@@ -19,6 +20,11 @@ module Flipper
19
20
  # Internal: The adapter this adapter is wrapping.
20
21
  attr_reader :adapter
21
22
 
23
+ # Private
24
+ def self.key_for(key)
25
+ "feature/#{key}"
26
+ end
27
+
22
28
  # Public
23
29
  def initialize(adapter, cache = nil)
24
30
  super(adapter)
@@ -26,7 +32,6 @@ module Flipper
26
32
  @name = :memoizable
27
33
  @cache = cache || {}
28
34
  @memoize = false
29
- @all_fetched = false
30
35
  end
31
36
 
32
37
  # Public
@@ -63,7 +68,7 @@ module Flipper
63
68
  # Public
64
69
  def get(feature)
65
70
  if memoizing?
66
- cache.fetch(feature.key) { cache[feature.key] = @adapter.get(feature) }
71
+ cache.fetch(key_for(feature.key)) { cache[key_for(feature.key)] = @adapter.get(feature) }
67
72
  else
68
73
  @adapter.get(feature)
69
74
  end
@@ -72,18 +77,18 @@ module Flipper
72
77
  # Public
73
78
  def get_multi(features)
74
79
  if memoizing?
75
- uncached_features = features.reject { |feature| cache[feature.key] }
80
+ uncached_features = features.reject { |feature| cache[key_for(feature.key)] }
76
81
 
77
82
  if uncached_features.any?
78
83
  response = @adapter.get_multi(uncached_features)
79
84
  response.each do |key, hash|
80
- cache[key] = hash
85
+ cache[key_for(key)] = hash
81
86
  end
82
87
  end
83
88
 
84
89
  result = {}
85
90
  features.each do |feature|
86
- result[feature.key] = cache[feature.key]
91
+ result[feature.key] = cache[key_for(feature.key)]
87
92
  end
88
93
  result
89
94
  else
@@ -93,20 +98,25 @@ module Flipper
93
98
 
94
99
  def get_all
95
100
  if memoizing?
96
- unless @all_fetched
97
- result = @adapter.get_all
98
- result.each do |key, value|
99
- cache[key] = value
101
+ response = nil
102
+ if cache[GetAllKey]
103
+ response = {}
104
+ cache[FeaturesKey].each do |key|
105
+ response[key] = cache[key_for(key)]
100
106
  end
101
- cache[FeaturesKey] = result.keys.to_set
102
- @all_fetched = true
107
+ else
108
+ response = @adapter.get_all
109
+ response.each do |key, value|
110
+ cache[key_for(key)] = value
111
+ end
112
+ cache[FeaturesKey] = response.keys.to_set
113
+ cache[GetAllKey] = true
103
114
  end
104
115
 
105
- result = {}
106
- features.each do |key|
107
- result[key] = cache[key]
108
- end
109
- result
116
+ # Ensures that looking up other features that do not exist doesn't
117
+ # result in N+1 adapter calls.
118
+ response.default_proc = ->(memo, key) { memo[key] = default_config }
119
+ response
110
120
  else
111
121
  @adapter.get_all
112
122
  end
@@ -130,7 +140,6 @@ module Flipper
130
140
  #
131
141
  # value - The Boolean that decides if local caching is on.
132
142
  def memoize=(value)
133
- @all_fetched = false
134
143
  cache.clear
135
144
  @memoize = value
136
145
  end
@@ -142,8 +151,12 @@ module Flipper
142
151
 
143
152
  private
144
153
 
154
+ def key_for(key)
155
+ self.class.key_for(key)
156
+ end
157
+
145
158
  def expire_feature(feature)
146
- cache.delete(feature.key) if memoizing?
159
+ cache.delete(key_for(feature.key)) if memoizing?
147
160
  end
148
161
 
149
162
  def expire_features_set
@@ -7,7 +7,7 @@ module Flipper
7
7
  class Memory
8
8
  include ::Flipper::Adapter
9
9
 
10
- FeaturesKey = :flipper_features
10
+ FeaturesKey = :features
11
11
 
12
12
  # Public: The name of the adapter.
13
13
  attr_reader :name
@@ -20,7 +20,7 @@ module Flipper
20
20
 
21
21
  # Public: The set of known features.
22
22
  def features
23
- set_members(FeaturesKey)
23
+ read_feature_keys
24
24
  end
25
25
 
26
26
  # Public: Adds a feature to the set of known features.
@@ -47,21 +47,19 @@ module Flipper
47
47
 
48
48
  # Public
49
49
  def get(feature)
50
- result = {}
50
+ read_feature(feature)
51
+ end
51
52
 
52
- feature.gates.each do |gate|
53
- result[gate.key] =
54
- case gate.data_type
55
- when :boolean, :integer
56
- read key(feature, gate)
57
- when :set
58
- set_members key(feature, gate)
59
- else
60
- raise "#{gate} is not supported by this adapter yet"
61
- end
53
+ def get_multi(features)
54
+ read_many_features(features)
55
+ end
56
+
57
+ def get_all
58
+ features = read_feature_keys.map do |key|
59
+ Flipper::Feature.new(key, self)
62
60
  end
63
61
 
64
- result
62
+ read_many_features(features)
65
63
  end
66
64
 
67
65
  # Public
@@ -103,9 +101,41 @@ module Flipper
103
101
  "#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
104
102
  end
105
103
 
104
+ private
105
+
106
+ def read_feature_keys
107
+ set_members(FeaturesKey)
108
+ end
109
+
106
110
  # Private
107
111
  def key(feature, gate)
108
- "#{feature.key}/#{gate.key}"
112
+ "feature/#{feature.key}/#{gate.key}"
113
+ end
114
+
115
+ def read_many_features(features)
116
+ result = {}
117
+ features.each do |feature|
118
+ result[feature.key] = read_feature(feature)
119
+ end
120
+ result
121
+ end
122
+
123
+ def read_feature(feature)
124
+ result = {}
125
+
126
+ feature.gates.each do |gate|
127
+ result[gate.key] =
128
+ case gate.data_type
129
+ when :boolean, :integer
130
+ read key(feature, gate)
131
+ when :set
132
+ set_members key(feature, gate)
133
+ else
134
+ raise "#{gate} is not supported by this adapter yet"
135
+ end
136
+ end
137
+
138
+ result
109
139
  end
110
140
 
111
141
  # Private
@@ -25,59 +25,68 @@ module Flipper
25
25
 
26
26
  # Public: The set of known features.
27
27
  def features
28
- set_members FeaturesKey
28
+ @store.transaction do
29
+ read_feature_keys
30
+ end
29
31
  end
30
32
 
31
33
  # Public: Adds a feature to the set of known features.
32
34
  def add(feature)
33
- set_add FeaturesKey, feature.key
35
+ @store.transaction do
36
+ set_add FeaturesKey, feature.key
37
+ end
34
38
  true
35
39
  end
36
40
 
37
41
  # Public: Removes a feature from the set of known features and clears
38
42
  # all the values for the feature.
39
43
  def remove(feature)
40
- set_delete FeaturesKey, feature.key
41
- clear(feature)
44
+ @store.transaction do
45
+ set_delete FeaturesKey, feature.key
46
+ clear_gates(feature)
47
+ end
42
48
  true
43
49
  end
44
50
 
45
51
  # Public: Clears all the gate values for a feature.
46
52
  def clear(feature)
47
- feature.gates.each do |gate|
48
- delete key(feature, gate)
53
+ @store.transaction do
54
+ clear_gates(feature)
49
55
  end
50
56
  true
51
57
  end
52
58
 
53
59
  # Public
54
60
  def get(feature)
55
- result = {}
61
+ @store.transaction do
62
+ result_for_feature(feature)
63
+ end
64
+ end
56
65
 
57
- feature.gates.each do |gate|
58
- result[gate.key] =
59
- case gate.data_type
60
- when :boolean, :integer
61
- read key(feature, gate)
62
- when :set
63
- set_members key(feature, gate)
64
- else
65
- raise "#{gate} is not supported by this adapter yet"
66
- end
66
+ def get_multi(features)
67
+ @store.transaction do
68
+ read_many_features(features)
67
69
  end
70
+ end
68
71
 
69
- result
72
+ def get_all
73
+ @store.transaction do
74
+ features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) }
75
+ read_many_features(features)
76
+ end
70
77
  end
71
78
 
72
79
  # Public
73
80
  def enable(feature, gate, thing)
74
- case gate.data_type
75
- when :boolean, :integer
76
- write key(feature, gate), thing.value.to_s
77
- when :set
78
- set_add key(feature, gate), thing.value.to_s
79
- else
80
- raise "#{gate} is not supported by this adapter yet"
81
+ @store.transaction do
82
+ case gate.data_type
83
+ when :boolean, :integer
84
+ write key(feature, gate), thing.value.to_s
85
+ when :set
86
+ set_add key(feature, gate), thing.value.to_s
87
+ else
88
+ raise "#{gate} is not supported by this adapter yet"
89
+ end
81
90
  end
82
91
 
83
92
  true
@@ -89,9 +98,13 @@ module Flipper
89
98
  when :boolean
90
99
  clear(feature)
91
100
  when :integer
92
- write key(feature, gate), thing.value.to_s
101
+ @store.transaction do
102
+ write key(feature, gate), thing.value.to_s
103
+ end
93
104
  when :set
94
- set_delete key(feature, gate), thing.value.to_s
105
+ @store.transaction do
106
+ set_delete key(feature, gate), thing.value.to_s
107
+ end
95
108
  else
96
109
  raise "#{gate} is not supported by this adapter yet"
97
110
  end
@@ -111,6 +124,42 @@ module Flipper
111
124
 
112
125
  private
113
126
 
127
+ def clear_gates(feature)
128
+ feature.gates.each do |gate|
129
+ delete key(feature, gate)
130
+ end
131
+ end
132
+
133
+ def read_feature_keys
134
+ set_members FeaturesKey
135
+ end
136
+
137
+ def read_many_features(features)
138
+ result = {}
139
+ features.each do |feature|
140
+ result[feature.key] = result_for_feature(feature)
141
+ end
142
+ result
143
+ end
144
+
145
+ def result_for_feature(feature)
146
+ result = {}
147
+
148
+ feature.gates.each do |gate|
149
+ result[gate.key] =
150
+ case gate.data_type
151
+ when :boolean, :integer
152
+ read key(feature, gate)
153
+ when :set
154
+ set_members key(feature, gate)
155
+ else
156
+ raise "#{gate} is not supported by this adapter yet"
157
+ end
158
+ end
159
+
160
+ result
161
+ end
162
+
114
163
  # Private
115
164
  def key(feature, gate)
116
165
  "#{feature.key}/#{gate.key}"
@@ -118,23 +167,17 @@ module Flipper
118
167
 
119
168
  # Private
120
169
  def read(key)
121
- @store.transaction do
122
- @store[key.to_s]
123
- end
170
+ @store[key.to_s]
124
171
  end
125
172
 
126
173
  # Private
127
174
  def write(key, value)
128
- @store.transaction do
129
- @store[key.to_s] = value.to_s
130
- end
175
+ @store[key.to_s] = value.to_s
131
176
  end
132
177
 
133
178
  # Private
134
179
  def delete(key)
135
- @store.transaction do
136
- @store.delete(key.to_s)
137
- end
180
+ @store.delete(key.to_s)
138
181
  end
139
182
 
140
183
  # Private
@@ -154,14 +197,13 @@ module Flipper
154
197
  # Private
155
198
  def set_members(key)
156
199
  key = key.to_s
157
- @store.transaction do
158
- @store[key] ||= Set.new
159
200
 
160
- if block_given?
161
- yield @store[key]
162
- else
163
- @store[key]
164
- end
201
+ @store[key] ||= Set.new
202
+
203
+ if block_given?
204
+ yield @store[key]
205
+ else
206
+ @store[key]
165
207
  end
166
208
  end
167
209
  end
@@ -27,6 +27,14 @@ module Flipper
27
27
  @adapter.get(feature)
28
28
  end
29
29
 
30
+ def get_multi(features)
31
+ @adapter.get_multi(features)
32
+ end
33
+
34
+ def get_all
35
+ @adapter.get_all
36
+ end
37
+
30
38
  def add(_feature)
31
39
  raise WriteAttempted
32
40
  end
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.11.0.beta9'.freeze
2
+ VERSION = '0.11.0.rc1'.freeze
3
3
  end
@@ -1,6 +1,7 @@
1
1
  require 'helper'
2
2
  require 'flipper/adapters/memoizable'
3
3
  require 'flipper/adapters/memory'
4
+ require 'flipper/adapters/operation_logger'
4
5
  require 'flipper/spec/shared_adapter_specs'
5
6
 
6
7
  RSpec.describe Flipper::Adapters::Memoizable do
@@ -66,7 +67,7 @@ RSpec.describe Flipper::Adapters::Memoizable do
66
67
  it 'memoizes feature' do
67
68
  feature = flipper[:stats]
68
69
  result = subject.get(feature)
69
- expect(cache[feature.key]).to be(result)
70
+ expect(cache[described_class.key_for(feature.key)]).to be(result)
70
71
  end
71
72
  end
72
73
 
@@ -95,8 +96,8 @@ RSpec.describe Flipper::Adapters::Memoizable do
95
96
  features = names.map { |name| flipper[name] }
96
97
  results = subject.get_multi(features)
97
98
  features.each do |feature|
98
- expect(cache[feature.key]).not_to be(nil)
99
- expect(cache[feature.key]).to be(results[feature.key])
99
+ expect(cache[described_class.key_for(feature.key)]).not_to be(nil)
100
+ expect(cache[described_class.key_for(feature.key)]).to be(results[feature.key])
100
101
  end
101
102
  end
102
103
  end
@@ -127,11 +128,32 @@ RSpec.describe Flipper::Adapters::Memoizable do
127
128
  features = names.map { |name| flipper[name].tap(&:enable) }
128
129
  results = subject.get_all
129
130
  features.each do |feature|
130
- expect(cache[feature.key]).not_to be(nil)
131
- expect(cache[feature.key]).to be(results[feature.key])
131
+ expect(cache[described_class.key_for(feature.key)]).not_to be(nil)
132
+ expect(cache[described_class.key_for(feature.key)]).to be(results[feature.key])
132
133
  end
133
134
  expect(cache[subject.class::FeaturesKey]).to eq(names.map(&:to_s).to_set)
134
135
  end
136
+
137
+ it 'only calls get_all once for memoized adapter' do
138
+ adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new)
139
+ cache = {}
140
+ instance = described_class.new(adapter, cache)
141
+ instance.memoize = true
142
+
143
+ instance.get_all
144
+ expect(adapter.count(:get_all)).to be(1)
145
+
146
+ instance.get_all
147
+ expect(adapter.count(:get_all)).to be(1)
148
+ end
149
+
150
+ it 'returns default_config for unknown feature keys' do
151
+ first = subject.get_all
152
+ expect(first['doesntexist']).to eq(subject.default_config)
153
+
154
+ second = subject.get_all
155
+ expect(second['doesntexist']).to eq(subject.default_config)
156
+ end
135
157
  end
136
158
 
137
159
  context "with memoization disabled" do
@@ -146,6 +168,27 @@ RSpec.describe Flipper::Adapters::Memoizable do
146
168
  adapter_result = adapter.get_all
147
169
  expect(result).to eq(adapter_result)
148
170
  end
171
+
172
+ it 'calls get_all every time for memoized adapter' do
173
+ adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new)
174
+ cache = {}
175
+ instance = described_class.new(adapter, cache)
176
+ instance.memoize = false
177
+
178
+ instance.get_all
179
+ expect(adapter.count(:get_all)).to be(1)
180
+
181
+ instance.get_all
182
+ expect(adapter.count(:get_all)).to be(2)
183
+ end
184
+
185
+ it 'returns nil for unknown feature keys' do
186
+ first = subject.get_all
187
+ expect(first['doesntexist']).to be(nil)
188
+
189
+ second = subject.get_all
190
+ expect(second['doesntexist']).to be(nil)
191
+ end
149
192
  end
150
193
  end
151
194
 
@@ -158,9 +201,9 @@ RSpec.describe Flipper::Adapters::Memoizable do
158
201
  it 'unmemoizes feature' do
159
202
  feature = flipper[:stats]
160
203
  gate = feature.gate(:boolean)
161
- cache[feature.key] = { some: 'thing' }
204
+ cache[described_class.key_for(feature.key)] = { some: 'thing' }
162
205
  subject.enable(feature, gate, flipper.bool)
163
- expect(cache[feature.key]).to be_nil
206
+ expect(cache[described_class.key_for(feature.key)]).to be_nil
164
207
  end
165
208
  end
166
209
 
@@ -188,9 +231,9 @@ RSpec.describe Flipper::Adapters::Memoizable do
188
231
  it 'unmemoizes feature' do
189
232
  feature = flipper[:stats]
190
233
  gate = feature.gate(:boolean)
191
- cache[feature.key] = { some: 'thing' }
234
+ cache[described_class.key_for(feature.key)] = { some: 'thing' }
192
235
  subject.disable(feature, gate, flipper.bool)
193
- expect(cache[feature.key]).to be_nil
236
+ expect(cache[described_class.key_for(feature.key)]).to be_nil
194
237
  end
195
238
  end
196
239
 
@@ -272,9 +315,9 @@ RSpec.describe Flipper::Adapters::Memoizable do
272
315
 
273
316
  it 'unmemoizes the feature' do
274
317
  feature = flipper[:stats]
275
- cache[feature.key] = { some: 'thing' }
318
+ cache[described_class.key_for(feature.key)] = { some: 'thing' }
276
319
  subject.remove(feature)
277
- expect(cache[feature.key]).to be_nil
320
+ expect(cache[described_class.key_for(feature.key)]).to be_nil
278
321
  end
279
322
  end
280
323
 
@@ -297,9 +340,9 @@ RSpec.describe Flipper::Adapters::Memoizable do
297
340
 
298
341
  it 'unmemoizes feature' do
299
342
  feature = flipper[:stats]
300
- cache[feature.key] = { some: 'thing' }
343
+ cache[described_class.key_for(feature.key)] = { some: 'thing' }
301
344
  subject.clear(feature)
302
- expect(cache[feature.key]).to be_nil
345
+ expect(cache[described_class.key_for(feature.key)]).to be_nil
303
346
  end
304
347
  end
305
348
 
@@ -1,6 +1,9 @@
1
1
  require 'helper'
2
2
  require 'rack/test'
3
+ require 'active_support/cache'
4
+ require 'active_support/cache/dalli_store'
3
5
  require 'flipper/middleware/memoizer'
6
+ require 'flipper/adapters/active_support_cache_store'
4
7
  require 'flipper/adapters/operation_logger'
5
8
  require 'flipper/adapters/memory'
6
9
 
@@ -141,6 +144,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
141
144
  middleware = described_class.new(app, preload_all: true)
142
145
  middleware.call(env)
143
146
 
147
+ expect(adapter.operations.size).to be(1)
144
148
  expect(adapter.count(:get_all)).to be(1)
145
149
  end
146
150
 
@@ -335,4 +339,45 @@ RSpec.describe Flipper::Middleware::Memoizer do
335
339
  get '/some/other/path', {}, 'flipper' => flipper
336
340
  end
337
341
  end
342
+
343
+ context 'with preload_all and caching adapter' do
344
+ it 'eagerly caches known features for duration of request' do
345
+ memory = Flipper::Adapters::Memory.new
346
+ logged_memory = Flipper::Adapters::OperationLogger.new(memory)
347
+ cache = ActiveSupport::Cache::DalliStore.new
348
+ cache.clear
349
+ cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache, expires_in: 10)
350
+ logged_cached = Flipper::Adapters::OperationLogger.new(cached)
351
+ memo = {}
352
+ flipper = Flipper.new(logged_cached)
353
+ flipper[:stats].enable
354
+ flipper[:shiny].enable
355
+
356
+ # clear the log of operations
357
+ logged_memory.reset
358
+ logged_cached.reset
359
+
360
+ app = lambda do |_env|
361
+ flipper[:stats].enabled?
362
+ flipper[:stats].enabled?
363
+ flipper[:shiny].enabled?
364
+ flipper[:shiny].enabled?
365
+ [200, {}, []]
366
+ end
367
+
368
+ middleware = described_class.new(app, preload_all: true)
369
+
370
+ middleware.call('flipper' => flipper)
371
+ expect(logged_cached.count(:get_all)).to be(1)
372
+ expect(logged_memory.count(:get_all)).to be(1)
373
+
374
+ middleware.call('flipper' => flipper)
375
+ expect(logged_cached.count(:get_all)).to be(2)
376
+ expect(logged_memory.count(:get_all)).to be(1)
377
+
378
+ middleware.call('flipper' => flipper)
379
+ expect(logged_cached.count(:get_all)).to be(3)
380
+ expect(logged_memory.count(:get_all)).to be(1)
381
+ end
382
+ end
338
383
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0.beta9
4
+ version: 0.11.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-28 00:00:00.000000000 Z
11
+ date: 2017-11-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Feature flipper is the act of enabling/disabling features in your application,
14
14
  ideally without re-deploying or changing anything in your code base. Flipper makes