simple_feature_flags 1.0.0 → 1.1.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.
- checksums.yaml +4 -4
- data/.vscode/settings.json +1 -0
- data/Gemfile.lock +1 -1
- data/README.md +86 -33
- data/exe/simple_feature_flags +0 -1
- data/lib/example_files/config/initializers/simple_feature_flags.rb +2 -2
- data/lib/example_files/config/simple_feature_flags.yml +3 -3
- data/lib/simple_feature_flags.rb +14 -2
- data/lib/simple_feature_flags/cli/command/generate.rb +47 -1
- data/lib/simple_feature_flags/cli/options.rb +3 -1
- data/lib/simple_feature_flags/configuration.rb +11 -0
- data/lib/simple_feature_flags/ram_storage.rb +51 -20
- data/lib/simple_feature_flags/redis_storage.rb +48 -29
- data/lib/simple_feature_flags/test_ram_storage.rb +3 -3
- data/lib/simple_feature_flags/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3caaf6b1bc76049e0debeb5a1d5b549bd1853328fb7bf8833469be0c3de666ca
|
4
|
+
data.tar.gz: 3e3a2f901936c0f6bad14a0a1efe75b171a1feb64de2e728c8bfadeda777262c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dadda81140cd5cc53845d1e0b2396df2258a87a779b9e770e9699e66f81fcb0f8acdd66887cf53d4186f225700e5e76ea5c2e15f10097443c68191201247eb59
|
7
|
+
data.tar.gz: 2e52191291340ce433c6e65ebfd71fd6cebbb1e1e77dc9893086fe0b7412f91753a756074a699fdfc2065a93158c892cdea87610a11a8bb953a22a260239e6af
|
data/.vscode/settings.json
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -66,14 +66,14 @@ This initializer in turn makes use of the generated config file `config/simple_f
|
|
66
66
|
:mandatory:
|
67
67
|
# example flag - it will be created with these properties if there is no such flag in Redis/RAM
|
68
68
|
# - name: example
|
69
|
-
# active: '
|
69
|
+
# active: 'globally' # %w[globally partially false] 'false' is the default value
|
70
70
|
# description: example
|
71
71
|
|
72
72
|
- name: example_flag
|
73
73
|
description: This is an example flag which will be automatically added when you start your app (it will be disabled)
|
74
74
|
|
75
75
|
- name: example_active_flag
|
76
|
-
active: '
|
76
|
+
active: 'globally'
|
77
77
|
description: This is an example flag which will be automatically added when you start your app (it will be enabled)
|
78
78
|
|
79
79
|
# nothing will happen if flag that is to be removed does not exist in Redis/RAM
|
@@ -133,10 +133,14 @@ Activates a feature in the global scope
|
|
133
133
|
|
134
134
|
```ruby
|
135
135
|
FEATURE_FLAGS.active?(:feature_name) #=> false
|
136
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
137
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
136
138
|
|
137
|
-
FEATURE_FLAGS.activate(:feature_name)
|
139
|
+
FEATURE_FLAGS.activate(:feature_name) # or FEATURE_FLAGS.activate_globally(:feature_name)
|
138
140
|
|
139
141
|
FEATURE_FLAGS.active?(:feature_name) #=> true
|
142
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> true
|
143
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
140
144
|
```
|
141
145
|
|
142
146
|
#### Deactivate a feature
|
@@ -154,64 +158,75 @@ FEATURE_FLAGS.active?(:feature_name) #=> false
|
|
154
158
|
#### Activate a feature for a particular record/object
|
155
159
|
|
156
160
|
```ruby
|
161
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> true
|
162
|
+
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> false
|
163
|
+
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
164
|
+
|
157
165
|
FEATURE_FLAGS.activate_for(:feature_name, User.first) #=> true
|
166
|
+
|
167
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> true
|
158
168
|
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> true
|
159
169
|
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
160
170
|
```
|
161
171
|
|
162
|
-
Note that the flag itself has to be
|
172
|
+
Note that the flag itself has to be active `partially` for any record/object specific settings to work.
|
163
173
|
When the flag is `deactivated` it is completely turned off globally and for every specific record/object.
|
164
174
|
|
165
175
|
```ruby
|
166
176
|
# The flag is deactivated in the global scope to begin with
|
167
177
|
FEATURE_FLAGS.active?(:feature_name) #=> false
|
178
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
179
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
180
|
+
|
181
|
+
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> false
|
182
|
+
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
168
183
|
|
169
184
|
# We activate it for the first User
|
170
185
|
FEATURE_FLAGS.activate_for(:feature_name, User.first)
|
171
186
|
|
172
187
|
FEATURE_FLAGS.active?(:feature_name) #=> false
|
188
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
189
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
190
|
+
|
173
191
|
# It is globally `deactivated` though, so the feature stays inactive for all users
|
174
192
|
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> false
|
193
|
+
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
175
194
|
|
176
|
-
# Once we activate the flag
|
177
|
-
FEATURE_FLAGS.
|
195
|
+
# Once we activate the flag partially, record specific settings will be applied
|
196
|
+
FEATURE_FLAGS.activate_partially(:feature_name)
|
178
197
|
|
179
198
|
FEATURE_FLAGS.active?(:feature_name) #=> true
|
199
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> true
|
200
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
201
|
+
|
180
202
|
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> true
|
181
203
|
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
182
204
|
|
183
205
|
FEATURE_FLAGS.deactivate(:feature_name)
|
184
206
|
|
185
207
|
FEATURE_FLAGS.active?(:feature_name) #=> false
|
208
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
209
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
210
|
+
|
186
211
|
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> false
|
187
212
|
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
188
213
|
```
|
189
214
|
|
190
|
-
There is a convenience method `activate_for!`, which activates the feature
|
215
|
+
There is a convenience method `activate_for!`, which activates the feature partially and for specific records/objects at the same time
|
191
216
|
|
192
217
|
```ruby
|
193
218
|
# The flag is deactivated in the global scope to begin with
|
194
219
|
FEATURE_FLAGS.active?(:feature_name) #=> false
|
220
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
221
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
195
222
|
|
196
223
|
# We activate it in the global scope and for the first User
|
197
224
|
FEATURE_FLAGS.activate_for!(:feature_name, User.first)
|
198
225
|
|
199
226
|
FEATURE_FLAGS.active?(:feature_name) #=> true
|
200
|
-
FEATURE_FLAGS.
|
201
|
-
FEATURE_FLAGS.
|
202
|
-
```
|
203
|
-
|
204
|
-
A feature that is `active` in the global scope is inactive for all specific records, unless it has been activated for them.
|
205
|
-
|
206
|
-
```ruby
|
207
|
-
# The flag is active in the global scope to begin with
|
208
|
-
FEATURE_FLAGS.active?(:feature_name) #=> true
|
209
|
-
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> false
|
210
|
-
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
211
|
-
|
212
|
-
FEATURE_FLAGS.activate_for(:feature_name, User.first)
|
227
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> true
|
228
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
213
229
|
|
214
|
-
FEATURE_FLAGS.active?(:feature_name) #=> true
|
215
230
|
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> true
|
216
231
|
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
217
232
|
```
|
@@ -233,23 +248,32 @@ FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
|
233
248
|
#### Activate the feature for every record
|
234
249
|
|
235
250
|
```ruby
|
236
|
-
# The flag is active
|
251
|
+
# The flag is active partially
|
237
252
|
FEATURE_FLAGS.active?(:feature_name) #=> true
|
253
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> true
|
254
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
255
|
+
|
238
256
|
# It is also enabled for the first user
|
239
257
|
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> true
|
240
258
|
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
241
259
|
|
242
260
|
# We force it onto every user
|
243
|
-
FEATURE_FLAGS.activate
|
261
|
+
FEATURE_FLAGS.activate(:feature_name)
|
244
262
|
|
245
263
|
FEATURE_FLAGS.active?(:feature_name) #=> true
|
264
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
265
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> true
|
266
|
+
|
246
267
|
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> true
|
247
268
|
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> true
|
248
269
|
|
249
270
|
# We can easily return to the previous settings
|
250
|
-
FEATURE_FLAGS.
|
271
|
+
FEATURE_FLAGS.activate_partially(:feature_name)
|
251
272
|
|
252
273
|
FEATURE_FLAGS.active?(:feature_name) #=> true
|
274
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
275
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> true
|
276
|
+
|
253
277
|
FEATURE_FLAGS.active_for?(:feature_name, User.first) #=> true
|
254
278
|
FEATURE_FLAGS.active_for?(:feature_name, User.last) #=> false
|
255
279
|
```
|
@@ -277,22 +301,32 @@ end
|
|
277
301
|
|
278
302
|
# or using a block
|
279
303
|
|
280
|
-
# this code will run only when the :feature_name flag is active
|
304
|
+
# this code will run only when the :feature_name flag is active (either partially or globally)
|
281
305
|
FEATURE_FLAGS.when_active(:feature_name) do
|
282
306
|
number += 1
|
283
307
|
end
|
284
308
|
|
285
|
-
#
|
286
|
-
FEATURE_FLAGS.
|
309
|
+
# this code will run only when the :feature_name flag is active globally
|
310
|
+
FEATURE_FLAGS.when_active_globally(:feature_name) do
|
311
|
+
number += 1
|
312
|
+
end
|
287
313
|
|
288
|
-
|
314
|
+
# this code will run only when the :feature_name flag is active partially (only for specific records/users)
|
315
|
+
FEATURE_FLAGS.when_active_partially(:feature_name) do
|
289
316
|
number += 1
|
290
317
|
end
|
291
318
|
|
292
|
-
# this code will run only if the :feature_name flag is active for the first User
|
319
|
+
# this code will run only if the :feature_name flag is active partially for the first User
|
293
320
|
FEATURE_FLAGS.when_active_for(:feature_name, User.first) do
|
294
321
|
number += 1
|
295
322
|
end
|
323
|
+
|
324
|
+
# feature flags that don't exist will return false
|
325
|
+
FEATURE_FLAGS.active?(:non_existant) #=> false
|
326
|
+
|
327
|
+
if FEATURE_FLAGS.active_for?(:feature_name, User.first)
|
328
|
+
number += 1
|
329
|
+
end
|
296
330
|
```
|
297
331
|
|
298
332
|
#### Adding feature flags
|
@@ -302,11 +336,27 @@ You can add new feature flags programmatically, though we highly encourage you t
|
|
302
336
|
In case you'd like to add flags programmatically
|
303
337
|
```ruby
|
304
338
|
FEATURE_FLAGS.add(:feature_name, 'Description')
|
339
|
+
|
305
340
|
FEATURE_FLAGS.active?(:feature_name) #=> false
|
341
|
+
FEATURE_FLAGS.active_partially?(:feature_name) #=> false
|
342
|
+
FEATURE_FLAGS.active_globally?(:feature_name) #=> false
|
343
|
+
FEATURE_FLAGS.active_for?(:feature_active_partially, User.first) #=> false
|
344
|
+
|
345
|
+
# add a new globally active flag
|
346
|
+
FEATURE_FLAGS.add(:active_feature, 'Description', :globally)
|
347
|
+
|
348
|
+
FEATURE_FLAGS.active?(:active_feature) #=> true
|
349
|
+
FEATURE_FLAGS.active_partially?(:active_feature) #=> false
|
350
|
+
FEATURE_FLAGS.active_globally?(:active_feature) #=> true
|
351
|
+
FEATURE_FLAGS.active_for?(:active_feature, User.first) #=> true
|
306
352
|
|
307
|
-
# add a new active flag
|
308
|
-
FEATURE_FLAGS.add(:
|
309
|
-
|
353
|
+
# add a new partially active flag
|
354
|
+
FEATURE_FLAGS.add(:feature_active_partially, 'Description', :partially)
|
355
|
+
|
356
|
+
FEATURE_FLAGS.active?(:feature_active_partially) #=> true
|
357
|
+
FEATURE_FLAGS.active_partially?(:feature_active_partially) #=> true
|
358
|
+
FEATURE_FLAGS.active_globally?(:feature_active_partially) #=> false
|
359
|
+
FEATURE_FLAGS.active_for?(:feature_active_partially, User.first) #=> false
|
310
360
|
```
|
311
361
|
|
312
362
|
#### Removing feature flags
|
@@ -316,7 +366,10 @@ You can remove feature flags programmatically, though we highly encourage you to
|
|
316
366
|
In case you'd like to remove flags programmatically
|
317
367
|
```ruby
|
318
368
|
FEATURE_FLAGS.remove(:feature_name)
|
319
|
-
|
369
|
+
|
370
|
+
FEATURE_FLAGS.active?(:feature_active_partially) #=> false
|
371
|
+
FEATURE_FLAGS.active_partially?(:feature_active_partially) #=> false
|
372
|
+
FEATURE_FLAGS.active_globally?(:feature_active_partially) #=> false
|
320
373
|
```
|
321
374
|
|
322
375
|
|
data/exe/simple_feature_flags
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
# Redis has 16 DBs (0 to 15)
|
3
3
|
|
4
4
|
FEATURE_FLAGS = if ::Rails.env.test?
|
5
|
-
# Use
|
6
|
-
::SimpleFeatureFlags::
|
5
|
+
# Use RamStorage in tests to make them faster
|
6
|
+
::SimpleFeatureFlags::RamStorage.new("#{::Rails.root.to_s}/config/simple_feature_flags.yml")
|
7
7
|
else
|
8
8
|
redis = ::Redis.new(host: '127.0.0.1', port: 6379, db: 0)
|
9
9
|
# We recommend using the `redis-namespace` gem to avoid key conflicts with Sidekiq or Resque
|
@@ -3,17 +3,17 @@
|
|
3
3
|
:mandatory:
|
4
4
|
# example flag - it will be created with these properties if there is no such flag in Redis/RAM
|
5
5
|
# - name: example
|
6
|
-
# active: '
|
6
|
+
# active: 'globally' # %w[globally partially false] 'false' is the default value
|
7
7
|
# description: example
|
8
8
|
|
9
9
|
- name: example_flag
|
10
10
|
description: This is an example flag which will be automatically added when you start your app (it will be disabled)
|
11
11
|
|
12
12
|
- name: example_active_flag
|
13
|
-
active: '
|
13
|
+
active: 'globally'
|
14
14
|
description: This is an example flag which will be automatically added when you start your app (it will be enabled)
|
15
15
|
|
16
16
|
# nothing will happen if flag that is to be removed does not exist in Redis/RAM
|
17
17
|
# An array of Feature Flag names that will be removed on app startup
|
18
18
|
:remove:
|
19
|
-
- flag_to_be_removed
|
19
|
+
- flag_to_be_removed
|
data/lib/simple_feature_flags.rb
CHANGED
@@ -2,14 +2,26 @@
|
|
2
2
|
|
3
3
|
require 'json'
|
4
4
|
|
5
|
+
Dir[File.expand_path('simple_feature_flags/*.rb', __dir__)].sort.each { |file| require file }
|
6
|
+
|
5
7
|
module SimpleFeatureFlags
|
6
8
|
NOT_PRESENT = ::Object.new.freeze
|
9
|
+
UI_GEM = 'simple_feature_flags-ui'
|
10
|
+
UI_CLASS_NAME = '::SimpleFeatureFlags::Ui'
|
11
|
+
WEB_UI_CLASS_NAME = '::SimpleFeatureFlags::Ui::Web'
|
12
|
+
|
13
|
+
ACTIVE_GLOBALLY = ['globally', :globally, 'true', true].freeze
|
14
|
+
ACTIVE_PARTIALLY = ['partially', :partially].freeze
|
7
15
|
|
8
16
|
class NoSuchCommandError < StandardError; end
|
9
17
|
|
10
18
|
class IncorrectWorkingDirectoryError < StandardError; end
|
11
19
|
|
12
20
|
class FlagNotDefinedError < StandardError; end
|
13
|
-
end
|
14
21
|
|
15
|
-
|
22
|
+
CONFIG = Configuration.new
|
23
|
+
|
24
|
+
def self.configure(&block)
|
25
|
+
block.call(CONFIG)
|
26
|
+
end
|
27
|
+
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
|
-
require 'byebug'
|
5
4
|
|
6
5
|
module SimpleFeatureFlags
|
7
6
|
module Cli
|
@@ -37,6 +36,41 @@ module SimpleFeatureFlags
|
|
37
36
|
puts '----------'
|
38
37
|
puts "- #{::File.join(destination_dir, 'config')}"
|
39
38
|
print_dir_tree(example_config_dir, 1)
|
39
|
+
|
40
|
+
return unless options.ui
|
41
|
+
|
42
|
+
file_gsub(routes_rb, /.routes.draw do/) do |match|
|
43
|
+
"#{match}\n mount #{WEB_UI_CLASS_NAME}.new => '/admin/simple_feature_flags'\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
ui_config_line = <<~CONF
|
47
|
+
#{UI_CLASS_NAME}.configure do |config|
|
48
|
+
config.instance = FEATURE_FLAGS
|
49
|
+
config.featurable_class_names = %w[User]
|
50
|
+
end
|
51
|
+
CONF
|
52
|
+
|
53
|
+
file_append(initializer_file, ui_config_line)
|
54
|
+
file_append(gemfile, %(gem '#{UI_GEM}'))
|
55
|
+
|
56
|
+
puts "\nModified:"
|
57
|
+
puts '----------'
|
58
|
+
puts "* #{routes_rb}"
|
59
|
+
puts "* #{gemfile}"
|
60
|
+
|
61
|
+
puts "\nBundling..."
|
62
|
+
system 'bundle'
|
63
|
+
end
|
64
|
+
|
65
|
+
def file_gsub(file_path, regexp, &block)
|
66
|
+
new_content = File.read(file_path).gsub(regexp, &block)
|
67
|
+
File.open(file_path, 'wb') { |file| file.write(new_content) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def file_append(file_path, line)
|
71
|
+
new_content = File.read(file_path)
|
72
|
+
new_content = "#{new_content}\n#{line}\n"
|
73
|
+
File.open(file_path, 'wb') { |file| file.write(new_content) }
|
40
74
|
end
|
41
75
|
|
42
76
|
def print_dir_tree(dir, embed_level = 0)
|
@@ -54,6 +88,18 @@ module SimpleFeatureFlags
|
|
54
88
|
end
|
55
89
|
end
|
56
90
|
|
91
|
+
def initializer_file
|
92
|
+
::File.join(destination_dir, 'config', 'initializers', 'simple_feature_flags.rb')
|
93
|
+
end
|
94
|
+
|
95
|
+
def gemfile
|
96
|
+
::File.join(destination_dir, 'Gemfile')
|
97
|
+
end
|
98
|
+
|
99
|
+
def routes_rb
|
100
|
+
::File.join(destination_dir, 'config', 'routes.rb')
|
101
|
+
end
|
102
|
+
|
57
103
|
def example_config_dir
|
58
104
|
::File.join(::File.expand_path(__dir__), '..', '..', '..', 'example_files', 'config')
|
59
105
|
end
|
@@ -5,10 +5,11 @@ require 'optparse'
|
|
5
5
|
module SimpleFeatureFlags
|
6
6
|
module Cli
|
7
7
|
class Options
|
8
|
-
attr_reader :opt_parser, :generate, :help, :rails
|
8
|
+
attr_reader :opt_parser, :generate, :help, :rails, :ui
|
9
9
|
|
10
10
|
def initialize(args)
|
11
11
|
@rails = true
|
12
|
+
@ui = false
|
12
13
|
|
13
14
|
@opt_parser = ::OptionParser.new do |opts|
|
14
15
|
opts.banner = 'Usage: simple_feature_flags [options]'
|
@@ -31,6 +32,7 @@ module SimpleFeatureFlags
|
|
31
32
|
opts.separator ''
|
32
33
|
opts.separator 'Modifiers:'
|
33
34
|
|
35
|
+
opts.on('--[no-]ui', '--[no-]web-ui', "Add the #{UI_GEM} gem and mount it in routes") { |u| @ui = u }
|
34
36
|
opts.on('--[no-]rails', 'Use generators suited for Rails apps') { |r| @rails = r }
|
35
37
|
end
|
36
38
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'yaml'
|
4
|
+
|
3
5
|
module SimpleFeatureFlags
|
4
6
|
class RamStorage
|
5
7
|
attr_reader :file, :mandatory_flags, :flags
|
@@ -13,15 +15,34 @@ module SimpleFeatureFlags
|
|
13
15
|
import_flags_from_file
|
14
16
|
end
|
15
17
|
|
16
|
-
def active
|
17
|
-
|
18
|
+
def active(feature)
|
19
|
+
case flags.dig(feature.to_sym, 'active')
|
20
|
+
when 'globally', :globally
|
21
|
+
:globally
|
22
|
+
when 'partially', :partially
|
23
|
+
:partially
|
24
|
+
when 'true', true
|
25
|
+
true
|
26
|
+
when 'false', false
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def active?(feature)
|
32
|
+
return true if active(feature)
|
33
|
+
|
34
|
+
false
|
18
35
|
end
|
19
36
|
|
20
37
|
def active_globally?(feature)
|
21
|
-
flags.dig(feature.to_sym, 'active')
|
38
|
+
ACTIVE_GLOBALLY.include? flags.dig(feature.to_sym, 'active')
|
22
39
|
end
|
23
40
|
|
24
|
-
def
|
41
|
+
def active_partially?(feature)
|
42
|
+
ACTIVE_PARTIALLY.include? flags.dig(feature.to_sym, 'active')
|
43
|
+
end
|
44
|
+
|
45
|
+
def active_for?(feature, object, object_id_method = CONFIG.default_id_method)
|
25
46
|
return false unless active?(feature)
|
26
47
|
return true if active_globally?(feature)
|
27
48
|
|
@@ -43,19 +64,31 @@ module SimpleFeatureFlags
|
|
43
64
|
flags.dig(feature.to_sym, 'description')
|
44
65
|
end
|
45
66
|
|
46
|
-
def when_active(feature,
|
47
|
-
return unless active?(feature
|
67
|
+
def when_active(feature, &block)
|
68
|
+
return unless active?(feature)
|
69
|
+
|
70
|
+
block.call
|
71
|
+
end
|
72
|
+
|
73
|
+
def when_active_globally(feature, &block)
|
74
|
+
return unless active_globally?(feature)
|
75
|
+
|
76
|
+
block.call
|
77
|
+
end
|
78
|
+
|
79
|
+
def when_active_partially(feature, &block)
|
80
|
+
return unless active_partially?(feature)
|
48
81
|
|
49
82
|
block.call
|
50
83
|
end
|
51
84
|
|
52
|
-
def when_active_for(feature, object, object_id_method =
|
85
|
+
def when_active_for(feature, object, object_id_method = CONFIG.default_id_method, &block)
|
53
86
|
return unless active_for?(feature, object, object_id_method)
|
54
87
|
|
55
88
|
block.call
|
56
89
|
end
|
57
90
|
|
58
|
-
def activate
|
91
|
+
def activate(feature)
|
59
92
|
return false unless exists?(feature)
|
60
93
|
|
61
94
|
flags[feature.to_sym]['active'] = 'globally'
|
@@ -63,15 +96,17 @@ module SimpleFeatureFlags
|
|
63
96
|
true
|
64
97
|
end
|
65
98
|
|
66
|
-
|
99
|
+
alias activate_globally activate
|
100
|
+
|
101
|
+
def activate_partially(feature)
|
67
102
|
return false unless exists?(feature)
|
68
103
|
|
69
|
-
flags[feature.to_sym]['active'] = '
|
104
|
+
flags[feature.to_sym]['active'] = 'partially'
|
70
105
|
|
71
106
|
true
|
72
107
|
end
|
73
108
|
|
74
|
-
def activate_for(feature, objects, object_id_method =
|
109
|
+
def activate_for(feature, objects, object_id_method = CONFIG.default_id_method)
|
75
110
|
return false unless exists?(feature)
|
76
111
|
|
77
112
|
objects = [objects] unless objects.is_a? ::Array
|
@@ -81,7 +116,7 @@ module SimpleFeatureFlags
|
|
81
116
|
to_activate_hash.each do |klass, ids|
|
82
117
|
(active_objects_hash[klass] = ids) && next unless active_objects_hash[klass]
|
83
118
|
|
84
|
-
active_objects_hash[klass].concat(ids).sort!
|
119
|
+
active_objects_hash[klass].concat(ids).uniq!.sort!
|
85
120
|
end
|
86
121
|
|
87
122
|
flags[feature.to_sym]['active_for_objects'] = active_objects_hash
|
@@ -89,10 +124,10 @@ module SimpleFeatureFlags
|
|
89
124
|
true
|
90
125
|
end
|
91
126
|
|
92
|
-
def activate_for!(feature, objects, object_id_method =
|
127
|
+
def activate_for!(feature, objects, object_id_method = CONFIG.default_id_method)
|
93
128
|
return false unless activate_for(feature, objects, object_id_method)
|
94
129
|
|
95
|
-
|
130
|
+
activate_partially(feature)
|
96
131
|
end
|
97
132
|
|
98
133
|
def deactivate!(feature)
|
@@ -116,7 +151,7 @@ module SimpleFeatureFlags
|
|
116
151
|
flags.dig(feature.to_sym, 'active_for_objects') || {}
|
117
152
|
end
|
118
153
|
|
119
|
-
def deactivate_for(feature, objects, object_id_method =
|
154
|
+
def deactivate_for(feature, objects, object_id_method = CONFIG.default_id_method)
|
120
155
|
return false unless exists?(feature)
|
121
156
|
|
122
157
|
active_objects_hash = active_objects(feature)
|
@@ -190,16 +225,12 @@ module SimpleFeatureFlags
|
|
190
225
|
|
191
226
|
private
|
192
227
|
|
193
|
-
def objects_to_hash(objects, object_id_method =
|
228
|
+
def objects_to_hash(objects, object_id_method = CONFIG.default_id_method)
|
194
229
|
objects = [objects] unless objects.is_a? ::Array
|
195
230
|
|
196
231
|
objects.group_by { |ob| ob.class.to_s }.transform_values { |arr| arr.map(&object_id_method) }
|
197
232
|
end
|
198
233
|
|
199
|
-
def __active__(feature)
|
200
|
-
%w[true globally].include? flags.dig(feature.to_sym, 'active')
|
201
|
-
end
|
202
|
-
|
203
234
|
def import_flags_from_file
|
204
235
|
changes = YAML.load_file(file)
|
205
236
|
changes = { mandatory: [], remove: [] } unless changes.is_a? ::Hash
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'yaml'
|
4
|
+
|
3
5
|
module SimpleFeatureFlags
|
4
6
|
class RedisStorage
|
5
7
|
attr_reader :file, :redis, :mandatory_flags
|
@@ -12,20 +14,34 @@ module SimpleFeatureFlags
|
|
12
14
|
import_flags_from_file
|
13
15
|
end
|
14
16
|
|
15
|
-
def active
|
16
|
-
__active__(feature)
|
17
|
-
end
|
18
|
-
|
19
|
-
def active_globally?(feature)
|
17
|
+
def active(feature)
|
20
18
|
case redis.hget(feature.to_s, 'active')
|
21
19
|
when 'globally'
|
20
|
+
:globally
|
21
|
+
when 'partially'
|
22
|
+
:partially
|
23
|
+
when 'true'
|
22
24
|
true
|
23
|
-
|
25
|
+
when 'false'
|
24
26
|
false
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
|
-
def
|
30
|
+
def active?(feature)
|
31
|
+
return true if active(feature)
|
32
|
+
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
def active_globally?(feature)
|
37
|
+
ACTIVE_GLOBALLY.include? redis.hget(feature.to_s, 'active')
|
38
|
+
end
|
39
|
+
|
40
|
+
def active_partially?(feature)
|
41
|
+
ACTIVE_PARTIALLY.include? redis.hget(feature.to_s, 'active')
|
42
|
+
end
|
43
|
+
|
44
|
+
def active_for?(feature, object, object_id_method = CONFIG.default_id_method)
|
29
45
|
return false unless active?(feature)
|
30
46
|
return true if active_globally?(feature)
|
31
47
|
|
@@ -47,19 +63,31 @@ module SimpleFeatureFlags
|
|
47
63
|
redis.hget(feature.to_s, 'description')
|
48
64
|
end
|
49
65
|
|
50
|
-
def when_active(feature,
|
66
|
+
def when_active(feature, &block)
|
51
67
|
return unless active?(feature)
|
52
68
|
|
53
69
|
block.call
|
54
70
|
end
|
55
71
|
|
56
|
-
def
|
72
|
+
def when_active_globally(feature, &block)
|
73
|
+
return unless active_globally?(feature)
|
74
|
+
|
75
|
+
block.call
|
76
|
+
end
|
77
|
+
|
78
|
+
def when_active_partially(feature, &block)
|
79
|
+
return unless active_partially?(feature)
|
80
|
+
|
81
|
+
block.call
|
82
|
+
end
|
83
|
+
|
84
|
+
def when_active_for(feature, object, object_id_method = CONFIG.default_id_method, &block)
|
57
85
|
return unless active_for?(feature, object, object_id_method)
|
58
86
|
|
59
87
|
block.call
|
60
88
|
end
|
61
89
|
|
62
|
-
def activate
|
90
|
+
def activate(feature)
|
63
91
|
return false unless exists?(feature)
|
64
92
|
|
65
93
|
redis.hset(feature.to_s, 'active', 'globally')
|
@@ -67,17 +95,17 @@ module SimpleFeatureFlags
|
|
67
95
|
true
|
68
96
|
end
|
69
97
|
|
70
|
-
alias activate_globally activate
|
98
|
+
alias activate_globally activate
|
71
99
|
|
72
|
-
def
|
100
|
+
def activate_partially(feature)
|
73
101
|
return false unless exists?(feature)
|
74
102
|
|
75
|
-
redis.hset(feature.to_s, 'active', '
|
103
|
+
redis.hset(feature.to_s, 'active', 'partially')
|
76
104
|
|
77
105
|
true
|
78
106
|
end
|
79
107
|
|
80
|
-
def activate_for(feature, objects, object_id_method =
|
108
|
+
def activate_for(feature, objects, object_id_method = CONFIG.default_id_method)
|
81
109
|
return false unless exists?(feature)
|
82
110
|
|
83
111
|
objects = [objects] unless objects.is_a? ::Array
|
@@ -87,7 +115,7 @@ module SimpleFeatureFlags
|
|
87
115
|
to_activate_hash.each do |klass, ids|
|
88
116
|
(active_objects_hash[klass] = ids) && next unless active_objects_hash[klass]
|
89
117
|
|
90
|
-
active_objects_hash[klass].concat(ids).sort!
|
118
|
+
active_objects_hash[klass].concat(ids).uniq!.sort!
|
91
119
|
end
|
92
120
|
|
93
121
|
redis.hset(feature.to_s, 'active_for_objects', active_objects_hash.to_json)
|
@@ -95,10 +123,10 @@ module SimpleFeatureFlags
|
|
95
123
|
true
|
96
124
|
end
|
97
125
|
|
98
|
-
def activate_for!(feature, objects, object_id_method =
|
126
|
+
def activate_for!(feature, objects, object_id_method = CONFIG.default_id_method)
|
99
127
|
return false unless activate_for(feature, objects, object_id_method)
|
100
128
|
|
101
|
-
|
129
|
+
activate_partially(feature)
|
102
130
|
end
|
103
131
|
|
104
132
|
def deactivate!(feature)
|
@@ -124,7 +152,7 @@ module SimpleFeatureFlags
|
|
124
152
|
{}
|
125
153
|
end
|
126
154
|
|
127
|
-
def deactivate_for(feature, objects, object_id_method =
|
155
|
+
def deactivate_for(feature, objects, object_id_method = CONFIG.default_id_method)
|
128
156
|
return false unless exists?(feature)
|
129
157
|
|
130
158
|
active_objects_hash = active_objects(feature)
|
@@ -203,23 +231,14 @@ module SimpleFeatureFlags
|
|
203
231
|
|
204
232
|
private
|
205
233
|
|
206
|
-
def objects_to_hash(objects, object_id_method =
|
234
|
+
def objects_to_hash(objects, object_id_method = CONFIG.default_id_method)
|
207
235
|
objects = [objects] unless objects.is_a? ::Array
|
208
236
|
|
209
237
|
objects.group_by { |ob| ob.class.to_s }.transform_values { |arr| arr.map(&object_id_method) }
|
210
238
|
end
|
211
239
|
|
212
|
-
def __active__(feature)
|
213
|
-
case redis.hget(feature.to_s, 'active')
|
214
|
-
when 'true', 'globally'
|
215
|
-
true
|
216
|
-
when 'false'
|
217
|
-
false
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
240
|
def import_flags_from_file
|
222
|
-
changes = YAML.load_file(file)
|
241
|
+
changes = ::YAML.load_file(file)
|
223
242
|
changes = { mandatory: [], remove: [] } unless changes.is_a? ::Hash
|
224
243
|
|
225
244
|
changes[:mandatory].each do |el|
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module SimpleFeatureFlags
|
4
4
|
class TestRamStorage < RamStorage
|
5
|
-
def active?(feature
|
6
|
-
raise(FlagNotDefinedError, "Feature Flag `#{feature}` is not defined as mandatory in #{file}")
|
5
|
+
def active?(feature)
|
6
|
+
raise(FlagNotDefinedError, "Feature Flag `#{feature}` is not defined as mandatory in #{file}") unless mandatory_flags.include?(feature.to_s)
|
7
7
|
|
8
|
-
|
8
|
+
super
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_feature_flags
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Espago
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-08-
|
12
|
+
date: 2021-08-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -169,6 +169,7 @@ files:
|
|
169
169
|
- lib/simple_feature_flags/cli/command/generate.rb
|
170
170
|
- lib/simple_feature_flags/cli/options.rb
|
171
171
|
- lib/simple_feature_flags/cli/runner.rb
|
172
|
+
- lib/simple_feature_flags/configuration.rb
|
172
173
|
- lib/simple_feature_flags/ram_storage.rb
|
173
174
|
- lib/simple_feature_flags/redis_storage.rb
|
174
175
|
- lib/simple_feature_flags/test_ram_storage.rb
|