simple_feature_flags 1.1.1 → 1.3.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/.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
|