simple_feature_flags 1.2.0 → 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 +79 -69
- 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 +253 -79
- data/lib/simple_feature_flags/redis_storage.rb +243 -62
- 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,46 +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) }
|
36
52
|
def inactive?(feature)
|
37
53
|
!active?(feature)
|
38
54
|
end
|
39
55
|
|
56
|
+
# Checks whether the flag is active globally, for every object.
|
57
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
40
58
|
def active_globally?(feature)
|
41
59
|
ACTIVE_GLOBALLY.include? redis.hget(feature.to_s, 'active')
|
42
60
|
end
|
43
61
|
|
62
|
+
# Checks whether the flag is inactive globally, for every object.
|
63
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
44
64
|
def inactive_globally?(feature)
|
45
65
|
!active_globally?(feature)
|
46
66
|
end
|
47
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) }
|
48
70
|
def active_partially?(feature)
|
49
71
|
ACTIVE_PARTIALLY.include? redis.hget(feature.to_s, 'active')
|
50
72
|
end
|
51
73
|
|
74
|
+
# Checks whether the flag is inactive partially, only for certain objects.
|
75
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
52
76
|
def inactive_partially?(feature)
|
53
77
|
!active_partially?(feature)
|
54
78
|
end
|
55
79
|
|
56
|
-
|
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)
|
57
91
|
return false unless active?(feature)
|
58
92
|
return true if active_globally?(feature)
|
59
93
|
|
@@ -65,68 +99,152 @@ module SimpleFeatureFlags
|
|
65
99
|
active_ids.include? object.public_send(object_id_method)
|
66
100
|
end
|
67
101
|
|
68
|
-
|
69
|
-
|
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)
|
70
114
|
end
|
71
115
|
|
116
|
+
# Checks whether the flag exists.
|
117
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
72
118
|
def exists?(feature)
|
73
119
|
return false if [nil, ''].include? redis.hget(feature.to_s, 'name')
|
74
120
|
|
75
121
|
true
|
76
122
|
end
|
77
123
|
|
124
|
+
# Returns the description of the flag if it exists.
|
125
|
+
sig { override.params(feature: T.any(Symbol, String)).returns(T.nilable(String)) }
|
78
126
|
def description(feature)
|
79
127
|
redis.hget(feature.to_s, 'description')
|
80
128
|
end
|
81
129
|
|
82
|
-
|
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
|
138
|
+
def when_active(feature, &block)
|
83
139
|
return unless active?(feature)
|
84
140
|
|
85
|
-
|
141
|
+
block.call
|
86
142
|
end
|
87
143
|
|
88
|
-
|
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)
|
89
153
|
return unless inactive?(feature)
|
90
154
|
|
91
|
-
|
155
|
+
block.call
|
92
156
|
end
|
93
157
|
|
94
|
-
|
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
|
166
|
+
def when_active_globally(feature, &block)
|
95
167
|
return unless active_globally?(feature)
|
96
168
|
|
97
|
-
|
169
|
+
block.call
|
98
170
|
end
|
99
171
|
|
100
|
-
|
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)
|
101
181
|
return unless inactive_globally?(feature)
|
102
182
|
|
103
|
-
|
183
|
+
block.call
|
104
184
|
end
|
105
185
|
|
106
|
-
|
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
|
194
|
+
def when_active_partially(feature, &block)
|
107
195
|
return unless active_partially?(feature)
|
108
196
|
|
109
|
-
|
197
|
+
block.call
|
110
198
|
end
|
111
199
|
|
112
|
-
|
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)
|
113
209
|
return unless inactive_partially?(feature)
|
114
210
|
|
115
|
-
|
211
|
+
block.call
|
116
212
|
end
|
117
213
|
|
118
|
-
|
119
|
-
|
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)
|
120
226
|
|
121
|
-
|
227
|
+
block.call
|
122
228
|
end
|
123
229
|
|
124
|
-
|
125
|
-
|
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)
|
126
242
|
|
127
|
-
|
243
|
+
block.call
|
128
244
|
end
|
129
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) }
|
130
248
|
def activate(feature)
|
131
249
|
return false unless exists?(feature)
|
132
250
|
|
@@ -137,6 +255,8 @@ module SimpleFeatureFlags
|
|
137
255
|
|
138
256
|
alias activate_globally activate
|
139
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) }
|
140
260
|
def activate_partially(feature)
|
141
261
|
return false unless exists?(feature)
|
142
262
|
|
@@ -145,17 +265,25 @@ module SimpleFeatureFlags
|
|
145
265
|
true
|
146
266
|
end
|
147
267
|
|
148
|
-
|
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)
|
149
278
|
return false unless exists?(feature)
|
150
279
|
|
151
|
-
|
152
|
-
to_activate_hash = objects_to_hash(objects, object_id_method)
|
280
|
+
to_activate_hash = objects_to_hash(objects, object_id_method: object_id_method)
|
153
281
|
active_objects_hash = active_objects(feature)
|
154
282
|
|
155
283
|
to_activate_hash.each do |klass, ids|
|
156
284
|
(active_objects_hash[klass] = ids) && next unless active_objects_hash[klass]
|
157
285
|
|
158
|
-
active_objects_hash[klass]
|
286
|
+
active_objects_hash[klass]&.concat(ids)&.uniq!&.sort! # rubocop:disable Style/SafeNavigationChainLength
|
159
287
|
end
|
160
288
|
|
161
289
|
redis.hset(feature.to_s, 'active_for_objects', active_objects_hash.to_json)
|
@@ -163,12 +291,26 @@ module SimpleFeatureFlags
|
|
163
291
|
true
|
164
292
|
end
|
165
293
|
|
166
|
-
|
167
|
-
|
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)
|
168
306
|
|
169
307
|
activate_partially(feature)
|
170
308
|
end
|
171
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) }
|
172
314
|
def deactivate!(feature)
|
173
315
|
return false unless exists?(feature)
|
174
316
|
|
@@ -178,6 +320,10 @@ module SimpleFeatureFlags
|
|
178
320
|
true
|
179
321
|
end
|
180
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) }
|
181
327
|
def deactivate(feature)
|
182
328
|
return false unless exists?(feature)
|
183
329
|
|
@@ -186,18 +332,39 @@ module SimpleFeatureFlags
|
|
186
332
|
true
|
187
333
|
end
|
188
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
|
189
347
|
def active_objects(feature)
|
190
348
|
::JSON.parse(redis.hget(feature.to_s, 'active_for_objects').to_s)
|
191
349
|
rescue ::JSON::ParserError
|
192
350
|
{}
|
193
351
|
end
|
194
352
|
|
195
|
-
|
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)
|
196
363
|
return false unless exists?(feature)
|
197
364
|
|
198
365
|
active_objects_hash = active_objects(feature)
|
199
366
|
|
200
|
-
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)
|
201
368
|
|
202
369
|
objects_to_deactivate_hash.each do |klass, ids_to_remove|
|
203
370
|
active_ids = active_objects_hash[klass]
|
@@ -211,18 +378,38 @@ module SimpleFeatureFlags
|
|
211
378
|
true
|
212
379
|
end
|
213
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
|
214
388
|
def get(feature)
|
215
389
|
return unless exists?(feature)
|
216
390
|
|
217
391
|
hash = redis.hgetall(feature.to_s)
|
218
392
|
hash['mandatory'] = mandatory_flags.include?(feature.to_s)
|
219
|
-
hash['active_for_objects'] =
|
393
|
+
hash['active_for_objects'] = begin
|
394
|
+
::JSON.parse(hash['active_for_objects'])
|
395
|
+
rescue StandardError
|
396
|
+
{}
|
397
|
+
end
|
220
398
|
|
221
399
|
hash
|
222
400
|
end
|
223
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
|
224
411
|
def add(feature, description, active = 'false')
|
225
|
-
return
|
412
|
+
return if exists?(feature)
|
226
413
|
|
227
414
|
active = if ACTIVE_GLOBALLY.include?(active)
|
228
415
|
'globally'
|
@@ -233,17 +420,25 @@ module SimpleFeatureFlags
|
|
233
420
|
end
|
234
421
|
|
235
422
|
hash = {
|
236
|
-
'name'
|
237
|
-
'active'
|
238
|
-
'description' => description
|
423
|
+
'name' => feature.to_s,
|
424
|
+
'active' => active,
|
425
|
+
'description' => description,
|
239
426
|
}
|
240
427
|
|
241
428
|
redis.hset(feature.to_s, hash)
|
242
429
|
hash
|
243
430
|
end
|
244
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
|
245
440
|
def remove(feature)
|
246
|
-
return
|
441
|
+
return unless exists?(feature)
|
247
442
|
|
248
443
|
removed = get(feature)
|
249
444
|
redis.del(feature.to_s)
|
@@ -251,10 +446,14 @@ module SimpleFeatureFlags
|
|
251
446
|
removed
|
252
447
|
end
|
253
448
|
|
449
|
+
# Returns the data of all feature flags.
|
450
|
+
sig do
|
451
|
+
override.returns(T::Array[T::Hash[String, T.anything]])
|
452
|
+
end
|
254
453
|
def all
|
255
454
|
keys = []
|
256
455
|
hashes = []
|
257
|
-
redis.scan_each(match:
|
456
|
+
redis.scan_each(match: '*') do |key|
|
258
457
|
next if keys.include?(key)
|
259
458
|
|
260
459
|
keys << key
|
@@ -264,30 +463,12 @@ module SimpleFeatureFlags
|
|
264
463
|
hashes
|
265
464
|
end
|
266
465
|
|
466
|
+
sig { returns(T.nilable(Redis::Namespace)) }
|
267
467
|
def namespaced_redis
|
268
|
-
redis
|
269
|
-
|
468
|
+
r = redis
|
469
|
+
return unless r.is_a?(Redis::Namespace)
|
270
470
|
|
271
|
-
|
272
|
-
|
273
|
-
def objects_to_hash(objects, object_id_method = CONFIG.default_id_method)
|
274
|
-
objects = [objects] unless objects.is_a? ::Array
|
275
|
-
|
276
|
-
objects.group_by { |ob| ob.class.to_s }.transform_values { |arr| arr.map(&object_id_method) }
|
277
|
-
end
|
278
|
-
|
279
|
-
def import_flags_from_file
|
280
|
-
changes = ::YAML.load_file(file)
|
281
|
-
changes = { mandatory: [], remove: [] } unless changes.is_a? ::Hash
|
282
|
-
|
283
|
-
changes[:mandatory].each do |el|
|
284
|
-
mandatory_flags << el['name']
|
285
|
-
add(el['name'], el['description'], el['active'])
|
286
|
-
end
|
287
|
-
|
288
|
-
changes[:remove].each do |el|
|
289
|
-
remove(el)
|
290
|
-
end
|
471
|
+
r
|
291
472
|
end
|
292
473
|
end
|
293
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 = ::Set['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
|