simple_feature_flags 1.3.0 → 1.4.1
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/.github/workflows/ci.yml +1 -1
- data/.rubocop.yml +4 -1
- data/.ruby-version +1 -1
- data/Gemfile +4 -4
- data/Gemfile.lock +55 -39
- data/README.md +30 -0
- data/lib/simple_feature_flags/base_storage.rb +52 -6
- data/lib/simple_feature_flags/cli/command/generate.rb +15 -21
- data/lib/simple_feature_flags/cli/options.rb +8 -8
- data/lib/simple_feature_flags/cli/runner.rb +3 -3
- data/lib/simple_feature_flags/configuration.rb +2 -2
- data/lib/simple_feature_flags/ram_storage.rb +112 -156
- data/lib/simple_feature_flags/redis_storage.rb +105 -149
- data/lib/simple_feature_flags/test_ram_storage.rb +2 -1
- data/lib/simple_feature_flags/version.rb +2 -1
- data/lib/simple_feature_flags.rb +7 -13
- data/simple_feature_flags.gemspec +1 -1
- metadata +4 -7
@@ -6,16 +6,18 @@ require 'yaml'
|
|
6
6
|
module SimpleFeatureFlags
|
7
7
|
# Stores feature flags in memory.
|
8
8
|
class RamStorage < BaseStorage
|
9
|
-
|
9
|
+
# @override
|
10
|
+
#: String
|
10
11
|
attr_reader :file
|
11
12
|
|
12
|
-
|
13
|
+
# @override
|
14
|
+
#: Array[String]
|
13
15
|
attr_reader :mandatory_flags
|
14
16
|
|
15
|
-
|
17
|
+
#: Hash[Symbol, Hash[String, Object]]
|
16
18
|
attr_reader :flags
|
17
19
|
|
18
|
-
|
20
|
+
#: (String file) -> void
|
19
21
|
def initialize(file)
|
20
22
|
@file = file
|
21
23
|
@mandatory_flags = []
|
@@ -25,7 +27,8 @@ module SimpleFeatureFlags
|
|
25
27
|
end
|
26
28
|
|
27
29
|
# Checks whether the flag is active. Returns `true`, `false`, `:globally` or `:partially`
|
28
|
-
|
30
|
+
# @override
|
31
|
+
#: ((Symbol | String) feature) -> (Symbol | bool)
|
29
32
|
def active(feature)
|
30
33
|
case flags.dig(feature.to_sym, 'active')
|
31
34
|
when 'globally', :globally
|
@@ -40,7 +43,8 @@ module SimpleFeatureFlags
|
|
40
43
|
end
|
41
44
|
|
42
45
|
# Checks whether the flag is active.
|
43
|
-
|
46
|
+
# @override
|
47
|
+
#: ((Symbol | String) feature) -> bool
|
44
48
|
def active?(feature)
|
45
49
|
return true if active(feature)
|
46
50
|
|
@@ -48,45 +52,43 @@ module SimpleFeatureFlags
|
|
48
52
|
end
|
49
53
|
|
50
54
|
# Checks whether the flag is inactive.
|
51
|
-
|
55
|
+
# @override
|
56
|
+
#: ((Symbol | String) feature) -> bool
|
52
57
|
def inactive?(feature)
|
53
58
|
!active?(feature)
|
54
59
|
end
|
55
60
|
|
56
61
|
# Checks whether the flag is active globally, for every object.
|
57
|
-
|
62
|
+
# @override
|
63
|
+
#: ((Symbol | String) feature) -> bool
|
58
64
|
def active_globally?(feature)
|
59
65
|
ACTIVE_GLOBALLY.include? T.unsafe(flags.dig(feature.to_sym, 'active'))
|
60
66
|
end
|
61
67
|
|
62
68
|
# Checks whether the flag is inactive globally, for every object.
|
63
|
-
|
69
|
+
# @override
|
70
|
+
#: ((Symbol | String) feature) -> bool
|
64
71
|
def inactive_globally?(feature)
|
65
72
|
!active_globally?(feature)
|
66
73
|
end
|
67
74
|
|
68
75
|
# Checks whether the flag is active partially, only for certain objects.
|
69
|
-
|
76
|
+
# @override
|
77
|
+
#: ((Symbol | String) feature) -> bool
|
70
78
|
def active_partially?(feature)
|
71
79
|
ACTIVE_PARTIALLY.include? T.unsafe(flags.dig(feature.to_sym, 'active'))
|
72
80
|
end
|
73
81
|
|
74
82
|
# Checks whether the flag is inactive partially, only for certain objects.
|
75
|
-
|
83
|
+
# @override
|
84
|
+
#: ((Symbol | String) feature) -> bool
|
76
85
|
def inactive_partially?(feature)
|
77
86
|
!active_partially?(feature)
|
78
87
|
end
|
79
88
|
|
80
89
|
# Checks whether the flag is active for the given object.
|
81
|
-
|
82
|
-
|
83
|
-
.params(
|
84
|
-
feature: T.any(Symbol, String),
|
85
|
-
object: Object,
|
86
|
-
object_id_method: Symbol,
|
87
|
-
)
|
88
|
-
.returns(T::Boolean)
|
89
|
-
end
|
90
|
+
# @override
|
91
|
+
#: ((Symbol | String) feature, Object object, ?object_id_method: Symbol) -> bool
|
90
92
|
def active_for?(feature, object, object_id_method: CONFIG.default_id_method)
|
91
93
|
return false unless active?(feature)
|
92
94
|
return true if active_globally?(feature)
|
@@ -100,21 +102,15 @@ module SimpleFeatureFlags
|
|
100
102
|
end
|
101
103
|
|
102
104
|
# Checks whether the flag is inactive for the given object.
|
103
|
-
|
104
|
-
|
105
|
-
.params(
|
106
|
-
feature: T.any(Symbol, String),
|
107
|
-
object: Object,
|
108
|
-
object_id_method: Symbol,
|
109
|
-
)
|
110
|
-
.returns(T::Boolean)
|
111
|
-
end
|
105
|
+
# @override
|
106
|
+
#: ((Symbol | String) feature, Object object, ?object_id_method: Symbol) -> bool
|
112
107
|
def inactive_for?(feature, object, object_id_method: CONFIG.default_id_method)
|
113
108
|
!active_for?(feature, object, object_id_method: object_id_method)
|
114
109
|
end
|
115
110
|
|
116
111
|
# Checks whether the flag exists.
|
117
|
-
|
112
|
+
# @override
|
113
|
+
#: ((Symbol | String) feature) -> bool
|
118
114
|
def exists?(feature)
|
119
115
|
return false if [nil, ''].include? flags[feature.to_sym]
|
120
116
|
|
@@ -122,19 +118,15 @@ module SimpleFeatureFlags
|
|
122
118
|
end
|
123
119
|
|
124
120
|
# Returns the description of the flag if it exists.
|
125
|
-
|
121
|
+
# @override
|
122
|
+
#: ((Symbol | String) feature) -> String?
|
126
123
|
def description(feature)
|
127
|
-
|
124
|
+
flags.dig(feature.to_sym, 'description') #: as untyped
|
128
125
|
end
|
129
126
|
|
130
127
|
# Calls the given block if the flag is active.
|
131
|
-
|
132
|
-
|
133
|
-
.params(
|
134
|
-
feature: T.any(Symbol, String),
|
135
|
-
block: T.proc.void,
|
136
|
-
).void
|
137
|
-
end
|
128
|
+
# @override
|
129
|
+
#: ((Symbol | String) feature) { -> void } -> void
|
138
130
|
def when_active(feature, &block)
|
139
131
|
return unless active?(feature)
|
140
132
|
|
@@ -142,13 +134,8 @@ module SimpleFeatureFlags
|
|
142
134
|
end
|
143
135
|
|
144
136
|
# Calls the given block if the flag is inactive.
|
145
|
-
|
146
|
-
|
147
|
-
.params(
|
148
|
-
feature: T.any(Symbol, String),
|
149
|
-
block: T.proc.void,
|
150
|
-
).void
|
151
|
-
end
|
137
|
+
# @override
|
138
|
+
#: ((Symbol | String) feature) { -> void } -> void
|
152
139
|
def when_inactive(feature, &block)
|
153
140
|
return unless inactive?(feature)
|
154
141
|
|
@@ -156,13 +143,8 @@ module SimpleFeatureFlags
|
|
156
143
|
end
|
157
144
|
|
158
145
|
# Calls the given block if the flag is active globally.
|
159
|
-
|
160
|
-
|
161
|
-
.params(
|
162
|
-
feature: T.any(Symbol, String),
|
163
|
-
block: T.proc.void,
|
164
|
-
).void
|
165
|
-
end
|
146
|
+
# @override
|
147
|
+
#: ((Symbol | String) feature) { -> void } -> void
|
166
148
|
def when_active_globally(feature, &block)
|
167
149
|
return unless active_globally?(feature)
|
168
150
|
|
@@ -170,13 +152,8 @@ module SimpleFeatureFlags
|
|
170
152
|
end
|
171
153
|
|
172
154
|
# Calls the given block if the flag is inactive globally.
|
173
|
-
|
174
|
-
|
175
|
-
.params(
|
176
|
-
feature: T.any(Symbol, String),
|
177
|
-
block: T.proc.void,
|
178
|
-
).void
|
179
|
-
end
|
155
|
+
# @override
|
156
|
+
#: ((Symbol | String) feature) { -> void } -> void
|
180
157
|
def when_inactive_globally(feature, &block)
|
181
158
|
return unless inactive_globally?(feature)
|
182
159
|
|
@@ -184,13 +161,8 @@ module SimpleFeatureFlags
|
|
184
161
|
end
|
185
162
|
|
186
163
|
# Calls the given block if the flag is active partially.
|
187
|
-
|
188
|
-
|
189
|
-
.params(
|
190
|
-
feature: T.any(Symbol, String),
|
191
|
-
block: T.proc.void,
|
192
|
-
).void
|
193
|
-
end
|
164
|
+
# @override
|
165
|
+
#: ((Symbol | String) feature) { -> void } -> void
|
194
166
|
def when_active_partially(feature, &block)
|
195
167
|
return unless active_partially?(feature)
|
196
168
|
|
@@ -198,13 +170,8 @@ module SimpleFeatureFlags
|
|
198
170
|
end
|
199
171
|
|
200
172
|
# Calls the given block if the flag is inactive partially.
|
201
|
-
|
202
|
-
|
203
|
-
.params(
|
204
|
-
feature: T.any(Symbol, String),
|
205
|
-
block: T.proc.void,
|
206
|
-
).void
|
207
|
-
end
|
173
|
+
# @override
|
174
|
+
#: ((Symbol | String) feature) { -> void } -> void
|
208
175
|
def when_inactive_partially(feature, &block)
|
209
176
|
return unless inactive_partially?(feature)
|
210
177
|
|
@@ -212,15 +179,8 @@ module SimpleFeatureFlags
|
|
212
179
|
end
|
213
180
|
|
214
181
|
# Calls the given block if the flag is active for the given object.
|
215
|
-
|
216
|
-
|
217
|
-
.params(
|
218
|
-
feature: T.any(Symbol, String),
|
219
|
-
object: Object,
|
220
|
-
object_id_method: Symbol,
|
221
|
-
block: T.proc.void,
|
222
|
-
).void
|
223
|
-
end
|
182
|
+
# @override
|
183
|
+
#: ((Symbol | String) feature, Object object, ?object_id_method: Symbol) { -> void } -> void
|
224
184
|
def when_active_for(feature, object, object_id_method: CONFIG.default_id_method, &block)
|
225
185
|
return unless active_for?(feature, object, object_id_method: object_id_method)
|
226
186
|
|
@@ -228,15 +188,8 @@ module SimpleFeatureFlags
|
|
228
188
|
end
|
229
189
|
|
230
190
|
# Calls the given block if the flag is inactive for the given object.
|
231
|
-
|
232
|
-
|
233
|
-
.params(
|
234
|
-
feature: T.any(Symbol, String),
|
235
|
-
object: Object,
|
236
|
-
object_id_method: Symbol,
|
237
|
-
block: T.proc.void,
|
238
|
-
).void
|
239
|
-
end
|
191
|
+
# @override
|
192
|
+
#: ((Symbol | String) feature, Object object, ?object_id_method: Symbol) { -> void } -> void
|
240
193
|
def when_inactive_for(feature, object, object_id_method: CONFIG.default_id_method, &block)
|
241
194
|
return unless inactive_for?(feature, object, object_id_method: object_id_method)
|
242
195
|
|
@@ -244,11 +197,12 @@ module SimpleFeatureFlags
|
|
244
197
|
end
|
245
198
|
|
246
199
|
# Activates the given flag. Returns `false` if it does not exist.
|
247
|
-
|
200
|
+
# @override
|
201
|
+
#: ((Symbol | String) feature) -> bool
|
248
202
|
def activate(feature)
|
249
203
|
return false unless exists?(feature)
|
250
204
|
|
251
|
-
flag =
|
205
|
+
flag = flags[feature.to_sym] #: as !nil
|
252
206
|
flag['active'] = 'globally'
|
253
207
|
|
254
208
|
true
|
@@ -256,26 +210,45 @@ module SimpleFeatureFlags
|
|
256
210
|
|
257
211
|
alias activate_globally activate
|
258
212
|
|
213
|
+
# @override
|
214
|
+
#: [R] ((Symbol | String) feature) { -> R } -> R
|
215
|
+
def do_activate(feature, &block)
|
216
|
+
feature = feature.to_sym
|
217
|
+
prev_value = flags.dig(feature, 'active')
|
218
|
+
activate(feature)
|
219
|
+
block.call
|
220
|
+
ensure
|
221
|
+
T.unsafe(flags)[feature]['active'] = prev_value
|
222
|
+
end
|
223
|
+
|
224
|
+
alias do_activate_globally do_activate
|
225
|
+
|
259
226
|
# Activates the given flag partially. Returns `false` if it does not exist.
|
260
|
-
|
227
|
+
# @override
|
228
|
+
#: ((Symbol | String) feature) -> bool
|
261
229
|
def activate_partially(feature)
|
262
230
|
return false unless exists?(feature)
|
263
231
|
|
264
|
-
flag =
|
232
|
+
flag = flags[feature.to_sym] #: as !nil
|
265
233
|
flag['active'] = 'partially'
|
266
234
|
|
267
235
|
true
|
268
236
|
end
|
269
237
|
|
270
|
-
#
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
238
|
+
# @override
|
239
|
+
#: [R] ((Symbol | String) feature) { -> R } -> R
|
240
|
+
def do_activate_partially(feature, &block)
|
241
|
+
feature = feature.to_sym
|
242
|
+
prev_value = flags.dig(feature, 'active')
|
243
|
+
activate_partially(feature)
|
244
|
+
block.call
|
245
|
+
ensure
|
246
|
+
T.unsafe(flags)[feature]['active'] = prev_value
|
278
247
|
end
|
248
|
+
|
249
|
+
# Activates the given flag for the given objects. Returns `false` if it does not exist.
|
250
|
+
# @override
|
251
|
+
#: ((Symbol | String) feature, *Object objects, ?object_id_method: Symbol) -> void
|
279
252
|
def activate_for(feature, *objects, object_id_method: CONFIG.default_id_method)
|
280
253
|
return false unless exists?(feature)
|
281
254
|
|
@@ -288,7 +261,7 @@ module SimpleFeatureFlags
|
|
288
261
|
active_objects_hash[klass]&.concat(ids)&.uniq!&.sort! # rubocop:disable Style/SafeNavigationChainLength
|
289
262
|
end
|
290
263
|
|
291
|
-
flag =
|
264
|
+
flag = flags[feature.to_sym] #: as !nil
|
292
265
|
flag['active_for_objects'] = active_objects_hash
|
293
266
|
|
294
267
|
true
|
@@ -296,14 +269,8 @@ module SimpleFeatureFlags
|
|
296
269
|
|
297
270
|
# Activates the given flag for the given objects and sets the flag as partially active.
|
298
271
|
# Returns `false` if it does not exist.
|
299
|
-
|
300
|
-
|
301
|
-
.params(
|
302
|
-
feature: T.any(Symbol, String),
|
303
|
-
objects: Object,
|
304
|
-
object_id_method: Symbol,
|
305
|
-
).void
|
306
|
-
end
|
272
|
+
# @override
|
273
|
+
#: ((Symbol | String) feature, *Object objects, ?object_id_method: Symbol) -> void
|
307
274
|
def activate_for!(feature, *objects, object_id_method: CONFIG.default_id_method)
|
308
275
|
return false unless T.unsafe(self).activate_for(feature, *objects, object_id_method: object_id_method)
|
309
276
|
|
@@ -313,11 +280,12 @@ module SimpleFeatureFlags
|
|
313
280
|
# Deactivates the given flag for all objects.
|
314
281
|
# Resets the list of objects that this flag has been turned on for.
|
315
282
|
# Returns `false` if it does not exist.
|
316
|
-
|
283
|
+
# @override
|
284
|
+
#: ((Symbol | String) feature) -> bool
|
317
285
|
def deactivate!(feature)
|
318
286
|
return false unless exists?(feature)
|
319
287
|
|
320
|
-
flag =
|
288
|
+
flag = flags[feature.to_sym] #: as !nil
|
321
289
|
flag['active'] = 'false'
|
322
290
|
flag['active_for_objects'] = nil
|
323
291
|
|
@@ -327,16 +295,28 @@ module SimpleFeatureFlags
|
|
327
295
|
# Deactivates the given flag globally.
|
328
296
|
# Does not reset the list of objects that this flag has been turned on for.
|
329
297
|
# Returns `false` if it does not exist.
|
330
|
-
|
298
|
+
# @override
|
299
|
+
#: ((Symbol | String) feature) -> bool
|
331
300
|
def deactivate(feature)
|
332
301
|
return false unless exists?(feature)
|
333
302
|
|
334
|
-
flag =
|
303
|
+
flag = flags[feature.to_sym] #: as !nil
|
335
304
|
flag['active'] = 'false'
|
336
305
|
|
337
306
|
true
|
338
307
|
end
|
339
308
|
|
309
|
+
# @override
|
310
|
+
#: [R] ((Symbol | String) feature) { -> R } -> R
|
311
|
+
def do_deactivate(feature, &block)
|
312
|
+
feature = feature.to_sym
|
313
|
+
prev_value = flags.dig(feature, 'active')
|
314
|
+
deactivate(feature)
|
315
|
+
block.call
|
316
|
+
ensure
|
317
|
+
T.unsafe(flags)[feature]['active'] = prev_value
|
318
|
+
end
|
319
|
+
|
340
320
|
# Returns a hash of Objects that the given flag is turned on for.
|
341
321
|
# The keys are class/model names, values are arrays of IDs of instances/records.
|
342
322
|
#
|
@@ -344,24 +324,15 @@ module SimpleFeatureFlags
|
|
344
324
|
#
|
345
325
|
# { "Page" => [25, 89], "Book" => [152] }
|
346
326
|
#
|
347
|
-
|
348
|
-
|
349
|
-
.params(feature: T.any(Symbol, String))
|
350
|
-
.returns(T::Hash[String, T::Array[Object]])
|
351
|
-
end
|
327
|
+
# @override
|
328
|
+
#: ((Symbol | String) feature) -> Hash[String, Array[Object]]
|
352
329
|
def active_objects(feature)
|
353
330
|
T.unsafe(flags.dig(feature.to_sym, 'active_for_objects')) || {}
|
354
331
|
end
|
355
332
|
|
356
333
|
# Deactivates the given flag for the given objects. Returns `false` if it does not exist.
|
357
|
-
|
358
|
-
|
359
|
-
.params(
|
360
|
-
feature: T.any(Symbol, String),
|
361
|
-
objects: Object,
|
362
|
-
object_id_method: Symbol,
|
363
|
-
).void
|
364
|
-
end
|
334
|
+
# @override
|
335
|
+
#: ((Symbol | String) feature, *Object objects, ?object_id_method: Symbol) -> void
|
365
336
|
def deactivate_for(feature, *objects, object_id_method: CONFIG.default_id_method)
|
366
337
|
return false unless exists?(feature)
|
367
338
|
|
@@ -376,38 +347,28 @@ module SimpleFeatureFlags
|
|
376
347
|
active_ids.reject! { |id| ids_to_remove.include? id }
|
377
348
|
end
|
378
349
|
|
379
|
-
flag =
|
350
|
+
flag = flags[feature.to_sym] #: as !nil
|
380
351
|
flag['active_for_objects'] = active_objects_hash
|
381
352
|
|
382
353
|
true
|
383
354
|
end
|
384
355
|
|
385
356
|
# Returns the data of the flag in a hash.
|
386
|
-
|
387
|
-
|
388
|
-
.params(
|
389
|
-
feature: T.any(Symbol, String),
|
390
|
-
).returns(T.nilable(T::Hash[String, T.anything]))
|
391
|
-
end
|
357
|
+
# @override
|
358
|
+
#: ((Symbol | String) feature) -> Hash[String, top]?
|
392
359
|
def get(feature)
|
393
360
|
return unless exists?(feature)
|
394
361
|
|
395
|
-
flag =
|
362
|
+
flag = flags[feature.to_sym] #: as !nil
|
396
363
|
flag['mandatory'] = mandatory_flags.include?(feature.to_s)
|
397
364
|
|
398
365
|
flag
|
399
366
|
end
|
400
367
|
|
401
368
|
# Adds the given feature flag.
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
feature: T.any(Symbol, String),
|
406
|
-
description: String,
|
407
|
-
active: T.any(String, Symbol, T::Boolean, NilClass),
|
408
|
-
).returns(T.nilable(T::Hash[String, T.anything]))
|
409
|
-
end
|
410
|
-
def add(feature, description, active = 'false')
|
369
|
+
# @override
|
370
|
+
#: ((Symbol | String) feature, ?String description, ?(String | Symbol | bool)? active) -> Hash[String, top]?
|
371
|
+
def add(feature, description = '', active = 'false')
|
411
372
|
return if exists?(feature)
|
412
373
|
|
413
374
|
active = if ACTIVE_GLOBALLY.include?(active)
|
@@ -429,12 +390,8 @@ module SimpleFeatureFlags
|
|
429
390
|
|
430
391
|
# Removes the given feature flag.
|
431
392
|
# Returns its data or nil if it does not exist.
|
432
|
-
|
433
|
-
|
434
|
-
.params(
|
435
|
-
feature: T.any(Symbol, String),
|
436
|
-
).returns(T.nilable(T::Hash[String, T.anything]))
|
437
|
-
end
|
393
|
+
# @override
|
394
|
+
#: ((Symbol | String) feature) -> Hash[String, top]?
|
438
395
|
def remove(feature)
|
439
396
|
return unless exists?(feature)
|
440
397
|
|
@@ -445,9 +402,8 @@ module SimpleFeatureFlags
|
|
445
402
|
end
|
446
403
|
|
447
404
|
# Returns the data of all feature flags.
|
448
|
-
|
449
|
-
|
450
|
-
end
|
405
|
+
# @override
|
406
|
+
#: -> Array[Hash[String, top]]
|
451
407
|
def all
|
452
408
|
hashes = []
|
453
409
|
|