simple_feature_flags 1.1.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +74 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +14 -60
- data/.ruby-version +1 -1
- data/.vscode/settings.json +5 -1
- data/Gemfile +11 -1
- data/Gemfile.lock +80 -68
- data/README.md +53 -2
- data/Rakefile +5 -5
- data/bin/tapioca +27 -0
- data/bin/test +8 -0
- data/lib/example_files/config/initializers/simple_feature_flags.rb +4 -3
- data/lib/simple_feature_flags/base_storage.rb +296 -0
- data/lib/simple_feature_flags/cli/command/generate.rb +33 -6
- data/lib/simple_feature_flags/cli/command.rb +3 -1
- data/lib/simple_feature_flags/cli/options.rb +19 -3
- data/lib/simple_feature_flags/cli/runner.rb +13 -5
- data/lib/simple_feature_flags/cli.rb +3 -1
- data/lib/simple_feature_flags/configuration.rb +6 -0
- data/lib/simple_feature_flags/ram_storage.rb +275 -61
- data/lib/simple_feature_flags/redis_storage.rb +265 -44
- data/lib/simple_feature_flags/test_ram_storage.rb +7 -1
- data/lib/simple_feature_flags/version.rb +1 -1
- data/lib/simple_feature_flags.rb +22 -9
- data/simple_feature_flags.gemspec +17 -22
- metadata +19 -125
- data/.travis.yml +0 -6
@@ -1,11 +1,21 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'yaml'
|
4
5
|
|
5
6
|
module SimpleFeatureFlags
|
6
|
-
|
7
|
-
|
7
|
+
# Stores feature flags in Redis.
|
8
|
+
class RedisStorage < BaseStorage
|
9
|
+
sig { override.returns(String) }
|
10
|
+
attr_reader :file
|
8
11
|
|
12
|
+
sig { override.returns(T::Array[String]) }
|
13
|
+
attr_reader :mandatory_flags
|
14
|
+
|
15
|
+
sig { returns(T.any(::Redis, ::Redis::Namespace)) }
|
16
|
+
attr_reader :redis
|
17
|
+
|
18
|
+
sig { params(redis: T.any(::Redis, ::Redis::Namespace), file: String).void }
|
9
19
|
def initialize(redis, file)
|
10
20
|
@file = file
|
11
21
|
@redis = redis
|
@@ -14,34 +24,70 @@ module SimpleFeatureFlags
|
|
14
24
|
import_flags_from_file
|
15
25
|
end
|
16
26
|
|
27
|
+
# Checks whether the flag is active. Returns `true`, `false`, `:globally` or `:partially`
|
28
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T.any(Symbol, T::Boolean)) }
|
17
29
|
def active(feature)
|
18
30
|
case redis.hget(feature.to_s, 'active')
|
19
31
|
when 'globally'
|
20
32
|
:globally
|
21
33
|
when 'partially'
|
22
34
|
:partially
|
23
|
-
when 'true'
|
35
|
+
when 'true', true
|
24
36
|
true
|
25
|
-
|
37
|
+
else
|
26
38
|
false
|
27
39
|
end
|
28
40
|
end
|
29
41
|
|
42
|
+
# Checks whether the flag is active.
|
43
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
30
44
|
def active?(feature)
|
31
45
|
return true if active(feature)
|
32
46
|
|
33
47
|
false
|
34
48
|
end
|
35
49
|
|
50
|
+
# Checks whether the flag is inactive.
|
51
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
52
|
+
def inactive?(feature)
|
53
|
+
!active?(feature)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Checks whether the flag is active globally, for every object.
|
57
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
36
58
|
def active_globally?(feature)
|
37
59
|
ACTIVE_GLOBALLY.include? redis.hget(feature.to_s, 'active')
|
38
60
|
end
|
39
61
|
|
62
|
+
# Checks whether the flag is inactive globally, for every object.
|
63
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
64
|
+
def inactive_globally?(feature)
|
65
|
+
!active_globally?(feature)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Checks whether the flag is active partially, only for certain objects.
|
69
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
40
70
|
def active_partially?(feature)
|
41
71
|
ACTIVE_PARTIALLY.include? redis.hget(feature.to_s, 'active')
|
42
72
|
end
|
43
73
|
|
44
|
-
|
74
|
+
# Checks whether the flag is inactive partially, only for certain objects.
|
75
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
76
|
+
def inactive_partially?(feature)
|
77
|
+
!active_partially?(feature)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Checks whether the flag is active for the given object.
|
81
|
+
sig do
|
82
|
+
override
|
83
|
+
.params(
|
84
|
+
feature: T.any(Symbol, String),
|
85
|
+
object: Object,
|
86
|
+
object_id_method: Symbol,
|
87
|
+
)
|
88
|
+
.returns(T::Boolean)
|
89
|
+
end
|
90
|
+
def active_for?(feature, object, object_id_method: CONFIG.default_id_method)
|
45
91
|
return false unless active?(feature)
|
46
92
|
return true if active_globally?(feature)
|
47
93
|
|
@@ -53,40 +99,152 @@ module SimpleFeatureFlags
|
|
53
99
|
active_ids.include? object.public_send(object_id_method)
|
54
100
|
end
|
55
101
|
|
102
|
+
# Checks whether the flag is inactive for the given object.
|
103
|
+
sig do
|
104
|
+
override
|
105
|
+
.params(
|
106
|
+
feature: T.any(Symbol, String),
|
107
|
+
object: Object,
|
108
|
+
object_id_method: Symbol,
|
109
|
+
)
|
110
|
+
.returns(T::Boolean)
|
111
|
+
end
|
112
|
+
def inactive_for?(feature, object, object_id_method: CONFIG.default_id_method)
|
113
|
+
!active_for?(feature, object, object_id_method: object_id_method)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Checks whether the flag exists.
|
117
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
56
118
|
def exists?(feature)
|
57
119
|
return false if [nil, ''].include? redis.hget(feature.to_s, 'name')
|
58
120
|
|
59
121
|
true
|
60
122
|
end
|
61
123
|
|
124
|
+
# Returns the description of the flag if it exists.
|
125
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T.nilable(String)) }
|
62
126
|
def description(feature)
|
63
127
|
redis.hget(feature.to_s, 'description')
|
64
128
|
end
|
65
129
|
|
130
|
+
# Calls the given block if the flag is active.
|
131
|
+
sig do
|
132
|
+
override
|
133
|
+
.params(
|
134
|
+
feature: T.any(Symbol, String),
|
135
|
+
block: T.proc.void,
|
136
|
+
).void
|
137
|
+
end
|
66
138
|
def when_active(feature, &block)
|
67
139
|
return unless active?(feature)
|
68
140
|
|
69
141
|
block.call
|
70
142
|
end
|
71
143
|
|
144
|
+
# Calls the given block if the flag is inactive.
|
145
|
+
sig do
|
146
|
+
override
|
147
|
+
.params(
|
148
|
+
feature: T.any(Symbol, String),
|
149
|
+
block: T.proc.void,
|
150
|
+
).void
|
151
|
+
end
|
152
|
+
def when_inactive(feature, &block)
|
153
|
+
return unless inactive?(feature)
|
154
|
+
|
155
|
+
block.call
|
156
|
+
end
|
157
|
+
|
158
|
+
# Calls the given block if the flag is active globally.
|
159
|
+
sig do
|
160
|
+
override
|
161
|
+
.params(
|
162
|
+
feature: T.any(Symbol, String),
|
163
|
+
block: T.proc.void,
|
164
|
+
).void
|
165
|
+
end
|
72
166
|
def when_active_globally(feature, &block)
|
73
167
|
return unless active_globally?(feature)
|
74
168
|
|
75
169
|
block.call
|
76
170
|
end
|
77
171
|
|
172
|
+
# Calls the given block if the flag is inactive globally.
|
173
|
+
sig do
|
174
|
+
override
|
175
|
+
.params(
|
176
|
+
feature: T.any(Symbol, String),
|
177
|
+
block: T.proc.void,
|
178
|
+
).void
|
179
|
+
end
|
180
|
+
def when_inactive_globally(feature, &block)
|
181
|
+
return unless inactive_globally?(feature)
|
182
|
+
|
183
|
+
block.call
|
184
|
+
end
|
185
|
+
|
186
|
+
# Calls the given block if the flag is active partially.
|
187
|
+
sig do
|
188
|
+
override
|
189
|
+
.params(
|
190
|
+
feature: T.any(Symbol, String),
|
191
|
+
block: T.proc.void,
|
192
|
+
).void
|
193
|
+
end
|
78
194
|
def when_active_partially(feature, &block)
|
79
195
|
return unless active_partially?(feature)
|
80
196
|
|
81
197
|
block.call
|
82
198
|
end
|
83
199
|
|
84
|
-
|
85
|
-
|
200
|
+
# Calls the given block if the flag is inactive partially.
|
201
|
+
sig do
|
202
|
+
override
|
203
|
+
.params(
|
204
|
+
feature: T.any(Symbol, String),
|
205
|
+
block: T.proc.void,
|
206
|
+
).void
|
207
|
+
end
|
208
|
+
def when_inactive_partially(feature, &block)
|
209
|
+
return unless inactive_partially?(feature)
|
210
|
+
|
211
|
+
block.call
|
212
|
+
end
|
213
|
+
|
214
|
+
# Calls the given block if the flag is active for the given object.
|
215
|
+
sig do
|
216
|
+
override
|
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
|
224
|
+
def when_active_for(feature, object, object_id_method: CONFIG.default_id_method, &block)
|
225
|
+
return unless active_for?(feature, object, object_id_method: object_id_method)
|
226
|
+
|
227
|
+
block.call
|
228
|
+
end
|
229
|
+
|
230
|
+
# Calls the given block if the flag is inactive for the given object.
|
231
|
+
sig do
|
232
|
+
override
|
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
|
240
|
+
def when_inactive_for(feature, object, object_id_method: CONFIG.default_id_method, &block)
|
241
|
+
return unless inactive_for?(feature, object, object_id_method: object_id_method)
|
86
242
|
|
87
243
|
block.call
|
88
244
|
end
|
89
245
|
|
246
|
+
# Activates the given flag. Returns `false` if it does not exist.
|
247
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
90
248
|
def activate(feature)
|
91
249
|
return false unless exists?(feature)
|
92
250
|
|
@@ -97,6 +255,8 @@ module SimpleFeatureFlags
|
|
97
255
|
|
98
256
|
alias activate_globally activate
|
99
257
|
|
258
|
+
# Activates the given flag partially. Returns `false` if it does not exist.
|
259
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
100
260
|
def activate_partially(feature)
|
101
261
|
return false unless exists?(feature)
|
102
262
|
|
@@ -105,17 +265,25 @@ module SimpleFeatureFlags
|
|
105
265
|
true
|
106
266
|
end
|
107
267
|
|
108
|
-
|
268
|
+
# Activates the given flag for the given objects. Returns `false` if it does not exist.
|
269
|
+
sig do
|
270
|
+
override
|
271
|
+
.params(
|
272
|
+
feature: T.any(Symbol, String),
|
273
|
+
objects: Object,
|
274
|
+
object_id_method: Symbol,
|
275
|
+
).void
|
276
|
+
end
|
277
|
+
def activate_for(feature, *objects, object_id_method: CONFIG.default_id_method)
|
109
278
|
return false unless exists?(feature)
|
110
279
|
|
111
|
-
|
112
|
-
to_activate_hash = objects_to_hash(objects, object_id_method)
|
280
|
+
to_activate_hash = objects_to_hash(objects, object_id_method: object_id_method)
|
113
281
|
active_objects_hash = active_objects(feature)
|
114
282
|
|
115
283
|
to_activate_hash.each do |klass, ids|
|
116
284
|
(active_objects_hash[klass] = ids) && next unless active_objects_hash[klass]
|
117
285
|
|
118
|
-
active_objects_hash[klass]
|
286
|
+
active_objects_hash[klass]&.concat(ids)&.uniq!&.sort! # rubocop:disable Style/SafeNavigationChainLength
|
119
287
|
end
|
120
288
|
|
121
289
|
redis.hset(feature.to_s, 'active_for_objects', active_objects_hash.to_json)
|
@@ -123,12 +291,26 @@ module SimpleFeatureFlags
|
|
123
291
|
true
|
124
292
|
end
|
125
293
|
|
126
|
-
|
127
|
-
|
294
|
+
# Activates the given flag for the given objects and sets the flag as partially active.
|
295
|
+
# Returns `false` if it does not exist.
|
296
|
+
sig do
|
297
|
+
override
|
298
|
+
.params(
|
299
|
+
feature: T.any(Symbol, String),
|
300
|
+
objects: Object,
|
301
|
+
object_id_method: Symbol,
|
302
|
+
).void
|
303
|
+
end
|
304
|
+
def activate_for!(feature, *objects, object_id_method: CONFIG.default_id_method)
|
305
|
+
return false unless T.unsafe(self).activate_for(feature, *objects, object_id_method: object_id_method)
|
128
306
|
|
129
307
|
activate_partially(feature)
|
130
308
|
end
|
131
309
|
|
310
|
+
# Deactivates the given flag for all objects.
|
311
|
+
# Resets the list of objects that this flag has been turned on for.
|
312
|
+
# Returns `false` if it does not exist.
|
313
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
132
314
|
def deactivate!(feature)
|
133
315
|
return false unless exists?(feature)
|
134
316
|
|
@@ -138,6 +320,10 @@ module SimpleFeatureFlags
|
|
138
320
|
true
|
139
321
|
end
|
140
322
|
|
323
|
+
# Deactivates the given flag globally.
|
324
|
+
# Does not reset the list of objects that this flag has been turned on for.
|
325
|
+
# Returns `false` if it does not exist.
|
326
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
141
327
|
def deactivate(feature)
|
142
328
|
return false unless exists?(feature)
|
143
329
|
|
@@ -146,18 +332,39 @@ module SimpleFeatureFlags
|
|
146
332
|
true
|
147
333
|
end
|
148
334
|
|
335
|
+
# Returns a hash of Objects that the given flag is turned on for.
|
336
|
+
# The keys are class/model names, values are arrays of IDs of instances/records.
|
337
|
+
#
|
338
|
+
# looks like this:
|
339
|
+
#
|
340
|
+
# { "Page" => [25, 89], "Book" => [152] }
|
341
|
+
#
|
342
|
+
sig do
|
343
|
+
override
|
344
|
+
.params(feature: T.any(Symbol, String))
|
345
|
+
.returns(T::Hash[String, T::Array[Object]])
|
346
|
+
end
|
149
347
|
def active_objects(feature)
|
150
348
|
::JSON.parse(redis.hget(feature.to_s, 'active_for_objects').to_s)
|
151
349
|
rescue ::JSON::ParserError
|
152
350
|
{}
|
153
351
|
end
|
154
352
|
|
155
|
-
|
353
|
+
# Deactivates the given flag for the given objects. Returns `false` if it does not exist.
|
354
|
+
sig do
|
355
|
+
override
|
356
|
+
.params(
|
357
|
+
feature: T.any(Symbol, String),
|
358
|
+
objects: Object,
|
359
|
+
object_id_method: Symbol,
|
360
|
+
).void
|
361
|
+
end
|
362
|
+
def deactivate_for(feature, *objects, object_id_method: CONFIG.default_id_method)
|
156
363
|
return false unless exists?(feature)
|
157
364
|
|
158
365
|
active_objects_hash = active_objects(feature)
|
159
366
|
|
160
|
-
objects_to_deactivate_hash = objects_to_hash(objects, object_id_method)
|
367
|
+
objects_to_deactivate_hash = objects_to_hash(objects, object_id_method: object_id_method)
|
161
368
|
|
162
369
|
objects_to_deactivate_hash.each do |klass, ids_to_remove|
|
163
370
|
active_ids = active_objects_hash[klass]
|
@@ -171,18 +378,38 @@ module SimpleFeatureFlags
|
|
171
378
|
true
|
172
379
|
end
|
173
380
|
|
381
|
+
# Returns the data of the flag in a hash.
|
382
|
+
sig do
|
383
|
+
override
|
384
|
+
.params(
|
385
|
+
feature: T.any(Symbol, String),
|
386
|
+
).returns(T.nilable(T::Hash[String, T.anything]))
|
387
|
+
end
|
174
388
|
def get(feature)
|
175
389
|
return unless exists?(feature)
|
176
390
|
|
177
391
|
hash = redis.hgetall(feature.to_s)
|
178
392
|
hash['mandatory'] = mandatory_flags.include?(feature.to_s)
|
179
|
-
hash['active_for_objects'] =
|
393
|
+
hash['active_for_objects'] = begin
|
394
|
+
::JSON.parse(hash['active_for_objects'])
|
395
|
+
rescue StandardError
|
396
|
+
{}
|
397
|
+
end
|
180
398
|
|
181
399
|
hash
|
182
400
|
end
|
183
401
|
|
402
|
+
# Adds the given feature flag.
|
403
|
+
sig do
|
404
|
+
override
|
405
|
+
.params(
|
406
|
+
feature: T.any(Symbol, String),
|
407
|
+
description: String,
|
408
|
+
active: T.any(String, Symbol, T::Boolean, NilClass),
|
409
|
+
).returns(T.nilable(T::Hash[String, T.anything]))
|
410
|
+
end
|
184
411
|
def add(feature, description, active = 'false')
|
185
|
-
return
|
412
|
+
return if exists?(feature)
|
186
413
|
|
187
414
|
active = if ACTIVE_GLOBALLY.include?(active)
|
188
415
|
'globally'
|
@@ -193,17 +420,25 @@ module SimpleFeatureFlags
|
|
193
420
|
end
|
194
421
|
|
195
422
|
hash = {
|
196
|
-
'name'
|
197
|
-
'active'
|
198
|
-
'description' => description
|
423
|
+
'name' => feature.to_s,
|
424
|
+
'active' => active,
|
425
|
+
'description' => description,
|
199
426
|
}
|
200
427
|
|
201
428
|
redis.hset(feature.to_s, hash)
|
202
429
|
hash
|
203
430
|
end
|
204
431
|
|
432
|
+
# Removes the given feature flag.
|
433
|
+
# Returns its data or nil if it does not exist.
|
434
|
+
sig do
|
435
|
+
override
|
436
|
+
.params(
|
437
|
+
feature: T.any(Symbol, String),
|
438
|
+
).returns(T.nilable(T::Hash[String, T.anything]))
|
439
|
+
end
|
205
440
|
def remove(feature)
|
206
|
-
return
|
441
|
+
return unless exists?(feature)
|
207
442
|
|
208
443
|
removed = get(feature)
|
209
444
|
redis.del(feature.to_s)
|
@@ -211,10 +446,14 @@ module SimpleFeatureFlags
|
|
211
446
|
removed
|
212
447
|
end
|
213
448
|
|
449
|
+
# Returns the data of all feature flags.
|
450
|
+
sig do
|
451
|
+
override.returns(T::Array[T::Hash[String, T.anything]])
|
452
|
+
end
|
214
453
|
def all
|
215
454
|
keys = []
|
216
455
|
hashes = []
|
217
|
-
redis.scan_each(match:
|
456
|
+
redis.scan_each(match: '*') do |key|
|
218
457
|
next if keys.include?(key)
|
219
458
|
|
220
459
|
keys << key
|
@@ -224,30 +463,12 @@ module SimpleFeatureFlags
|
|
224
463
|
hashes
|
225
464
|
end
|
226
465
|
|
466
|
+
sig { returns(T.nilable(Redis::Namespace)) }
|
227
467
|
def namespaced_redis
|
228
|
-
redis
|
229
|
-
|
230
|
-
|
231
|
-
private
|
468
|
+
r = redis
|
469
|
+
return unless r.is_a?(Redis::Namespace)
|
232
470
|
|
233
|
-
|
234
|
-
objects = [objects] unless objects.is_a? ::Array
|
235
|
-
|
236
|
-
objects.group_by { |ob| ob.class.to_s }.transform_values { |arr| arr.map(&object_id_method) }
|
237
|
-
end
|
238
|
-
|
239
|
-
def import_flags_from_file
|
240
|
-
changes = ::YAML.load_file(file)
|
241
|
-
changes = { mandatory: [], remove: [] } unless changes.is_a? ::Hash
|
242
|
-
|
243
|
-
changes[:mandatory].each do |el|
|
244
|
-
mandatory_flags << el['name']
|
245
|
-
add(el['name'], el['description'], el['active'])
|
246
|
-
end
|
247
|
-
|
248
|
-
changes[:remove].each do |el|
|
249
|
-
remove(el)
|
250
|
-
end
|
471
|
+
r
|
251
472
|
end
|
252
473
|
end
|
253
474
|
end
|
@@ -1,9 +1,15 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module SimpleFeatureFlags
|
5
|
+
# Used in tests
|
4
6
|
class TestRamStorage < RamStorage
|
7
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
5
8
|
def active?(feature)
|
6
|
-
|
9
|
+
unless mandatory_flags.include?(feature.to_s)
|
10
|
+
raise(FlagNotDefinedError,
|
11
|
+
"Feature Flag `#{feature}` is not defined as mandatory in #{file}",)
|
12
|
+
end
|
7
13
|
|
8
14
|
super
|
9
15
|
end
|
data/lib/simple_feature_flags.rb
CHANGED
@@ -1,17 +1,24 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'json'
|
5
|
+
require 'set'
|
6
|
+
require 'sorbet-runtime'
|
4
7
|
|
5
|
-
Dir[File.expand_path('simple_feature_flags/*.rb', __dir__)].
|
8
|
+
Dir[File.expand_path('simple_feature_flags/*.rb', __dir__)].each { |file| require file }
|
6
9
|
|
10
|
+
# Tha main namespace of the `simple_feature_flags` gem.
|
7
11
|
module SimpleFeatureFlags
|
12
|
+
extend T::Sig
|
13
|
+
|
8
14
|
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'
|
15
|
+
UI_GEM = T.let('simple_feature_flags-ui', String)
|
16
|
+
UI_CLASS_NAME = T.let('::SimpleFeatureFlags::Ui', String)
|
17
|
+
WEB_UI_CLASS_NAME = T.let('::SimpleFeatureFlags::Ui::Web', String)
|
12
18
|
|
13
|
-
ACTIVE_GLOBALLY = ['globally', :globally, 'true', true].freeze
|
14
|
-
|
19
|
+
ACTIVE_GLOBALLY = T.let(::Set['globally', :globally, 'true', true].freeze,
|
20
|
+
T::Set[T.any(String, Symbol, T::Boolean, NilClass)],)
|
21
|
+
ACTIVE_PARTIALLY = T.let(::Set['partially', :partially].freeze, T::Set[T.any(String, Symbol, T::Boolean, NilClass)])
|
15
22
|
|
16
23
|
class NoSuchCommandError < StandardError; end
|
17
24
|
|
@@ -19,9 +26,15 @@ module SimpleFeatureFlags
|
|
19
26
|
|
20
27
|
class FlagNotDefinedError < StandardError; end
|
21
28
|
|
22
|
-
CONFIG = Configuration.new
|
29
|
+
CONFIG = T.let(Configuration.new, Configuration)
|
30
|
+
|
31
|
+
class << self
|
32
|
+
extend T::Sig
|
23
33
|
|
24
|
-
|
25
|
-
block
|
34
|
+
sig { params(block: T.proc.params(arg0: Configuration).void).returns(Configuration) }
|
35
|
+
def configure(&block)
|
36
|
+
block.call(CONFIG)
|
37
|
+
CONFIG
|
38
|
+
end
|
26
39
|
end
|
27
40
|
end
|
@@ -3,36 +3,31 @@
|
|
3
3
|
require_relative 'lib/simple_feature_flags/version'
|
4
4
|
|
5
5
|
::Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
6
|
+
spec.name = 'simple_feature_flags'
|
7
7
|
spec.version = ::SimpleFeatureFlags::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
8
|
+
spec.authors = ['Espago', 'Mateusz Drewniak']
|
9
|
+
spec.email = ['m.drewniak@espago.com']
|
10
10
|
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
|
14
|
-
|
15
|
-
spec.
|
11
|
+
spec.summary = 'Simple feature flag functionality for your Ruby/Rails/Sinatra app!'
|
12
|
+
spec.description = <<~DESC
|
13
|
+
A simple Ruby gem which lets you dynamically enable/disable parts of your code using Redis or your server's RAM!
|
14
|
+
DESC
|
15
|
+
spec.homepage = 'https://github.com/espago/simple_feature_flags'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
spec.required_ruby_version = '>= 3.1.0'
|
16
18
|
|
17
|
-
spec.metadata[
|
18
|
-
spec.metadata[
|
19
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/espago/simple_feature_flags'
|
19
21
|
|
20
22
|
# Specify which files should be added to the gem when it is released.
|
21
23
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
24
|
spec.files = ::Dir.chdir(::File.expand_path(__dir__)) do
|
23
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|sorbet)/}) }
|
24
26
|
end
|
25
|
-
spec.bindir =
|
27
|
+
spec.bindir = 'exe'
|
26
28
|
spec.executables = ['simple_feature_flags']
|
27
|
-
spec.require_paths = [
|
29
|
+
spec.require_paths = ['lib']
|
28
30
|
|
29
|
-
spec.
|
30
|
-
spec.
|
31
|
-
spec.add_development_dependency 'byebug'
|
32
|
-
spec.add_development_dependency 'minitest', '~> 5.0'
|
33
|
-
spec.add_development_dependency 'rake', '~> 12.0'
|
34
|
-
spec.add_development_dependency 'redis'
|
35
|
-
spec.add_development_dependency 'redis-namespace'
|
36
|
-
spec.add_development_dependency 'rubocop'
|
37
|
-
spec.add_development_dependency 'solargraph'
|
31
|
+
spec.add_dependency 'sorbet-runtime', '> 0.5'
|
32
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
38
33
|
end
|