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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a31152f17215cd2905c721f27217aaa187c006345a5c811ca6194ae36fabef89
|
4
|
+
data.tar.gz: 9df276339edd478dc86e0ce590518c21ebc115f63899fe493ad328d11180ae5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b02dde3a77725c376a0375d8c291e308ee5ce8e7b885e2e051a08e8205eb8407b64e460e43ee6a404a504dd9f9bf1248f387a6764870ec444c3c44f0b423f71
|
7
|
+
data.tar.gz: f3f1cdecad7872b6c10c3a1e086f773844e04bf3869fc07d6661fef81dcf33fdb78f2d731318b3f6410cc6ca039fb10625bedb4ffc854f865b0ccf3cfe98f1b6
|
data/.github/workflows/ci.yml
CHANGED
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.3
|
1
|
+
3.4.3
|
data/Gemfile
CHANGED
@@ -10,7 +10,7 @@ gem 'minitest', '~> 5.0' # test library
|
|
10
10
|
gem 'rake', '~> 13.0' # automation tasks
|
11
11
|
gem 'redis', '~> 5.3' # redis client
|
12
12
|
gem 'redis-namespace', '~> 1.11' # namespaces for redis
|
13
|
-
gem 'rubocop-espago', '~> 1.
|
14
|
-
gem 'rubocop-sorbet', '~> 0.
|
15
|
-
gem 'sorbet', '
|
16
|
-
gem 'tapioca', '
|
13
|
+
gem 'rubocop-espago', '~> 1.1' # ruby linter
|
14
|
+
gem 'rubocop-sorbet', '~> 0.10' # rubocop for sorbet
|
15
|
+
gem 'sorbet', '~> 0.5' # static typechecker
|
16
|
+
gem 'tapioca', '~> 0.17' # RBI generator for sorbet
|
data/Gemfile.lock
CHANGED
@@ -1,80 +1,96 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
simple_feature_flags (1.
|
4
|
+
simple_feature_flags (1.4.1)
|
5
5
|
sorbet-runtime (> 0.5)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
ast (2.4.
|
10
|
+
ast (2.4.3)
|
11
|
+
benchmark (0.4.1)
|
11
12
|
byebug (11.1.3)
|
12
13
|
connection_pool (2.4.1)
|
13
|
-
erubi (1.13.
|
14
|
-
json (2.
|
15
|
-
language_server-protocol (3.17.0.
|
14
|
+
erubi (1.13.1)
|
15
|
+
json (2.10.2)
|
16
|
+
language_server-protocol (3.17.0.4)
|
17
|
+
lint_roller (1.1.0)
|
18
|
+
logger (1.7.0)
|
16
19
|
minitest (5.25.1)
|
17
20
|
netrc (0.11.0)
|
18
|
-
parallel (1.
|
19
|
-
parser (3.3.
|
21
|
+
parallel (1.27.0)
|
22
|
+
parser (3.3.8.0)
|
20
23
|
ast (~> 2.4.1)
|
21
24
|
racc
|
22
|
-
prism (1.
|
25
|
+
prism (1.4.0)
|
23
26
|
racc (1.8.1)
|
24
27
|
rainbow (3.1.1)
|
25
28
|
rake (13.2.1)
|
26
|
-
rbi (0.
|
29
|
+
rbi (0.3.6)
|
27
30
|
prism (~> 1.0)
|
28
|
-
|
31
|
+
rbs (>= 3.4.4)
|
32
|
+
rbs (4.0.0.dev.4)
|
33
|
+
logger
|
34
|
+
prism (>= 1.3.0)
|
29
35
|
redis (5.3.0)
|
30
36
|
redis-client (>= 0.22.0)
|
31
37
|
redis-client (0.22.2)
|
32
38
|
connection_pool
|
33
39
|
redis-namespace (1.11.0)
|
34
40
|
redis (>= 4)
|
35
|
-
regexp_parser (2.
|
36
|
-
|
41
|
+
regexp_parser (2.10.0)
|
42
|
+
require-hooks (0.2.2)
|
43
|
+
rexml (3.4.1)
|
44
|
+
rubocop (1.74.0)
|
37
45
|
json (~> 2.3)
|
38
|
-
language_server-protocol (
|
46
|
+
language_server-protocol (~> 3.17.0.2)
|
47
|
+
lint_roller (~> 1.1.0)
|
39
48
|
parallel (~> 1.10)
|
40
49
|
parser (>= 3.3.0.2)
|
41
50
|
rainbow (>= 2.2.2, < 4.0)
|
42
|
-
regexp_parser (>= 2.
|
43
|
-
rubocop-ast (>= 1.
|
51
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
52
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
44
53
|
ruby-progressbar (~> 1.7)
|
45
|
-
unicode-display_width (>= 2.4.0, <
|
46
|
-
rubocop-ast (1.
|
47
|
-
parser (>= 3.3.
|
48
|
-
rubocop-espago (1.1.
|
54
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
55
|
+
rubocop-ast (1.42.0)
|
56
|
+
parser (>= 3.3.7.2)
|
57
|
+
rubocop-espago (1.1.8)
|
49
58
|
rubocop
|
50
|
-
rubocop-sorbet (0.
|
59
|
+
rubocop-sorbet (0.10.0)
|
51
60
|
rubocop (>= 1)
|
52
61
|
ruby-progressbar (1.13.0)
|
53
|
-
sorbet (0.5.
|
54
|
-
sorbet-static (= 0.5.
|
55
|
-
sorbet-runtime (0.5.
|
56
|
-
sorbet-static (0.5.
|
57
|
-
sorbet-static (0.5.
|
58
|
-
sorbet-static (0.5.
|
59
|
-
sorbet-static-and-runtime (0.5.
|
60
|
-
sorbet (= 0.5.
|
61
|
-
sorbet-runtime (= 0.5.
|
62
|
-
spoom (1.
|
62
|
+
sorbet (0.5.12210)
|
63
|
+
sorbet-static (= 0.5.12210)
|
64
|
+
sorbet-runtime (0.5.12210)
|
65
|
+
sorbet-static (0.5.12210-aarch64-linux)
|
66
|
+
sorbet-static (0.5.12210-universal-darwin)
|
67
|
+
sorbet-static (0.5.12210-x86_64-linux)
|
68
|
+
sorbet-static-and-runtime (0.5.12210)
|
69
|
+
sorbet (= 0.5.12210)
|
70
|
+
sorbet-runtime (= 0.5.12210)
|
71
|
+
spoom (1.7.4)
|
63
72
|
erubi (>= 1.10.0)
|
64
73
|
prism (>= 0.28.0)
|
74
|
+
rbi (>= 0.3.3)
|
75
|
+
rbs (>= 4.0.0.dev.4)
|
76
|
+
rexml (>= 3.2.6)
|
65
77
|
sorbet-static-and-runtime (>= 0.5.10187)
|
66
78
|
thor (>= 0.19.2)
|
67
|
-
tapioca (0.
|
79
|
+
tapioca (0.17.5)
|
80
|
+
benchmark
|
68
81
|
bundler (>= 2.2.25)
|
69
82
|
netrc (>= 0.11.0)
|
70
83
|
parallel (>= 1.21.0)
|
71
|
-
rbi (
|
84
|
+
rbi (>= 0.3.1)
|
85
|
+
require-hooks (>= 0.2.2)
|
72
86
|
sorbet-static-and-runtime (>= 0.5.11087)
|
73
|
-
spoom (>= 1.
|
87
|
+
spoom (>= 1.7.0)
|
74
88
|
thor (>= 1.2.0)
|
75
89
|
yard-sorbet
|
76
90
|
thor (1.3.2)
|
77
|
-
unicode-display_width (
|
91
|
+
unicode-display_width (3.1.4)
|
92
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
93
|
+
unicode-emoji (4.0.4)
|
78
94
|
yard (0.9.37)
|
79
95
|
yard-sorbet (0.9.0)
|
80
96
|
sorbet-runtime
|
@@ -91,11 +107,11 @@ DEPENDENCIES
|
|
91
107
|
rake (~> 13.0)
|
92
108
|
redis (~> 5.3)
|
93
109
|
redis-namespace (~> 1.11)
|
94
|
-
rubocop-espago (~> 1.
|
95
|
-
rubocop-sorbet (~> 0.
|
110
|
+
rubocop-espago (~> 1.1)
|
111
|
+
rubocop-sorbet (~> 0.10)
|
96
112
|
simple_feature_flags!
|
97
|
-
sorbet (
|
98
|
-
tapioca (
|
113
|
+
sorbet (~> 0.5)
|
114
|
+
tapioca (~> 0.17)
|
99
115
|
|
100
116
|
BUNDLED WITH
|
101
|
-
2.
|
117
|
+
2.6.8
|
data/README.md
CHANGED
@@ -151,6 +151,21 @@ FEATURE_FLAGS.inactive_globally?(:feature_name) #=> false
|
|
151
151
|
FEATURE_FLAGS.inactive_partially?(:feature_name) #=> true
|
152
152
|
```
|
153
153
|
|
154
|
+
#### Activate a feature in a block
|
155
|
+
|
156
|
+
Activates a feature for the code in the given block
|
157
|
+
then restores the original state of the feature.
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
FEATURE_FLAGS.active?(:feature_name) #=> false
|
161
|
+
|
162
|
+
FEATURE_FLAGS.do_activate(:feature_name) do
|
163
|
+
FEATURE_FLAGS.active?(:feature_name) #=> true
|
164
|
+
end
|
165
|
+
|
166
|
+
FEATURE_FLAGS.active?(:feature_name) #=> false
|
167
|
+
```
|
168
|
+
|
154
169
|
#### Deactivate a feature
|
155
170
|
|
156
171
|
Deactivates a feature in the global scope
|
@@ -165,6 +180,21 @@ FEATURE_FLAGS.active?(:feature_name) #=> false
|
|
165
180
|
FEATURE_FLAGS.inactive?(:feature_name) #=> true
|
166
181
|
```
|
167
182
|
|
183
|
+
#### Deactivate a feature in a block
|
184
|
+
|
185
|
+
Activates a feature for the code in the given block
|
186
|
+
then restores the original state of the feature.
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
FEATURE_FLAGS.active?(:feature_name) #=> true
|
190
|
+
|
191
|
+
FEATURE_FLAGS.do_deactivate(:feature_name) do
|
192
|
+
FEATURE_FLAGS.active?(:feature_name) #=> false
|
193
|
+
end
|
194
|
+
|
195
|
+
FEATURE_FLAGS.active?(:feature_name) #=> true
|
196
|
+
```
|
197
|
+
|
168
198
|
#### Activate a feature for a particular record/object
|
169
199
|
|
170
200
|
```ruby
|
@@ -5,11 +5,9 @@ require 'yaml'
|
|
5
5
|
|
6
6
|
module SimpleFeatureFlags
|
7
7
|
# Abstract class for all storage adapters.
|
8
|
+
# @abstract
|
8
9
|
class BaseStorage
|
9
10
|
extend T::Sig
|
10
|
-
extend T::Helpers
|
11
|
-
|
12
|
-
abstract!
|
13
11
|
|
14
12
|
# Path to the file with feature flags
|
15
13
|
sig { abstract.returns(String) }
|
@@ -166,14 +164,50 @@ module SimpleFeatureFlags
|
|
166
164
|
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
167
165
|
def activate(feature); end
|
168
166
|
|
167
|
+
# Activates the flag, calls the block and restores the previous state of the flag.
|
168
|
+
sig do
|
169
|
+
abstract
|
170
|
+
.type_parameters(:R)
|
171
|
+
.params(
|
172
|
+
feature: T.any(Symbol, String),
|
173
|
+
block: T.proc.returns(T.type_parameter(:R)),
|
174
|
+
)
|
175
|
+
.returns(T.type_parameter(:R))
|
176
|
+
end
|
177
|
+
def do_activate(feature, &block); end
|
178
|
+
|
169
179
|
# Activates the given flag globally. Returns `false` if it does not exist.
|
170
180
|
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
171
181
|
def activate_globally(feature); end
|
172
182
|
|
183
|
+
# Activates the flag globally, calls the block and restores the previous state of the flag.
|
184
|
+
sig do
|
185
|
+
abstract
|
186
|
+
.type_parameters(:R)
|
187
|
+
.params(
|
188
|
+
feature: T.any(Symbol, String),
|
189
|
+
block: T.proc.returns(T.type_parameter(:R)),
|
190
|
+
)
|
191
|
+
.returns(T.type_parameter(:R))
|
192
|
+
end
|
193
|
+
def do_activate_globally(feature, &block); end
|
194
|
+
|
173
195
|
# Activates the given flag partially. Returns `false` if it does not exist.
|
174
196
|
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
175
197
|
def activate_partially(feature); end
|
176
198
|
|
199
|
+
# Activates the flag partially, calls the block and restores the previous state of the flag.
|
200
|
+
sig do
|
201
|
+
abstract
|
202
|
+
.type_parameters(:R)
|
203
|
+
.params(
|
204
|
+
feature: T.any(Symbol, String),
|
205
|
+
block: T.proc.returns(T.type_parameter(:R)),
|
206
|
+
)
|
207
|
+
.returns(T.type_parameter(:R))
|
208
|
+
end
|
209
|
+
def do_activate_partially(feature, &block); end
|
210
|
+
|
177
211
|
# Activates the given flag for the given objects. Returns `false` if it does not exist.
|
178
212
|
sig do
|
179
213
|
abstract
|
@@ -209,6 +243,18 @@ module SimpleFeatureFlags
|
|
209
243
|
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
210
244
|
def deactivate(feature); end
|
211
245
|
|
246
|
+
# Deactivates the flag, calls the block and restores the previous state of the flag.
|
247
|
+
sig do
|
248
|
+
abstract
|
249
|
+
.type_parameters(:R)
|
250
|
+
.params(
|
251
|
+
feature: T.any(Symbol, String),
|
252
|
+
block: T.proc.returns(T.type_parameter(:R)),
|
253
|
+
)
|
254
|
+
.returns(T.type_parameter(:R))
|
255
|
+
end
|
256
|
+
def do_deactivate(feature, &block); end
|
257
|
+
|
212
258
|
# Returns a hash of Objects that the given flag is turned on for.
|
213
259
|
# The keys are class/model names, values are arrays of IDs of instances/records.
|
214
260
|
#
|
@@ -252,7 +298,7 @@ module SimpleFeatureFlags
|
|
252
298
|
active: T.any(String, Symbol, T::Boolean, NilClass),
|
253
299
|
).returns(T.nilable(T::Hash[String, T.anything]))
|
254
300
|
end
|
255
|
-
def add(feature, description, active = 'false'); end
|
301
|
+
def add(feature, description = '', active = 'false'); end
|
256
302
|
|
257
303
|
# Removes the given feature flag.
|
258
304
|
# Returns its data or nil if it does not exist.
|
@@ -272,13 +318,13 @@ module SimpleFeatureFlags
|
|
272
318
|
|
273
319
|
private
|
274
320
|
|
275
|
-
|
321
|
+
#: (Array[Object] objects, ?object_id_method: Symbol) -> Hash[String, Array[Object]]
|
276
322
|
def objects_to_hash(objects, object_id_method: CONFIG.default_id_method)
|
277
323
|
objects.group_by { |ob| ob.class.to_s }
|
278
324
|
.transform_values { |arr| arr.map(&object_id_method) }
|
279
325
|
end
|
280
326
|
|
281
|
-
|
327
|
+
#: -> void
|
282
328
|
def import_flags_from_file
|
283
329
|
changes = YAML.load_file(file)
|
284
330
|
changes = { mandatory: [], remove: [] } unless changes.is_a? ::Hash
|
@@ -10,17 +10,17 @@ module SimpleFeatureFlags
|
|
10
10
|
class Generate
|
11
11
|
extend T::Sig
|
12
12
|
|
13
|
-
CONFIG_FILE =
|
13
|
+
CONFIG_FILE = 'simple_feature_flags.yml' #: String
|
14
14
|
|
15
|
-
|
15
|
+
#: Options
|
16
16
|
attr_reader :options
|
17
17
|
|
18
|
-
|
18
|
+
#: (Options options) -> void
|
19
19
|
def initialize(options)
|
20
20
|
@options = options
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
#: -> void
|
24
24
|
def run
|
25
25
|
if options.rails
|
26
26
|
generate_for_rails
|
@@ -36,7 +36,7 @@ module SimpleFeatureFlags
|
|
36
36
|
|
37
37
|
private
|
38
38
|
|
39
|
-
|
39
|
+
#: -> void
|
40
40
|
def generate_for_rails
|
41
41
|
::FileUtils.cp_r example_config_dir, destination_dir
|
42
42
|
|
@@ -70,26 +70,20 @@ module SimpleFeatureFlags
|
|
70
70
|
system 'bundle'
|
71
71
|
end
|
72
72
|
|
73
|
-
|
74
|
-
params(
|
75
|
-
file_path: String,
|
76
|
-
regexp: Regexp,
|
77
|
-
block: T.proc.params(arg0: String).returns(String),
|
78
|
-
).void
|
79
|
-
end
|
73
|
+
#: (String file_path, Regexp regexp) { (String arg0) -> String } -> void
|
80
74
|
def file_gsub(file_path, regexp, &block)
|
81
75
|
new_content = File.read(file_path).gsub(regexp, &block)
|
82
76
|
File.binwrite(file_path, new_content)
|
83
77
|
end
|
84
78
|
|
85
|
-
|
79
|
+
#: (String file_path, String line) -> void
|
86
80
|
def file_append(file_path, line)
|
87
81
|
new_content = File.read(file_path)
|
88
82
|
new_content = "#{new_content}\n#{line}\n"
|
89
83
|
File.binwrite(file_path, new_content)
|
90
84
|
end
|
91
85
|
|
92
|
-
|
86
|
+
#: (String dir, ?Integer embed_level) -> void
|
93
87
|
def print_dir_tree(dir, embed_level = 0)
|
94
88
|
padding = ' ' * (embed_level * 2)
|
95
89
|
|
@@ -105,32 +99,32 @@ module SimpleFeatureFlags
|
|
105
99
|
end
|
106
100
|
end
|
107
101
|
|
108
|
-
|
102
|
+
#: -> String
|
109
103
|
def initializer_file
|
110
104
|
::File.join(destination_dir, 'config', 'initializers', 'simple_feature_flags.rb')
|
111
105
|
end
|
112
106
|
|
113
|
-
|
107
|
+
#: -> String
|
114
108
|
def gemfile
|
115
109
|
::File.join(destination_dir, 'Gemfile')
|
116
110
|
end
|
117
111
|
|
118
|
-
|
112
|
+
#: -> String
|
119
113
|
def routes_rb
|
120
114
|
::File.join(destination_dir, 'config', 'routes.rb')
|
121
115
|
end
|
122
116
|
|
123
|
-
|
117
|
+
#: -> String
|
124
118
|
def example_config_dir
|
125
119
|
::File.join(::File.expand_path(__dir__), '..', '..', '..', 'example_files', 'config')
|
126
120
|
end
|
127
121
|
|
128
|
-
|
122
|
+
#: -> String
|
129
123
|
def example_config_file
|
130
124
|
::File.join(example_config_dir, CONFIG_FILE)
|
131
125
|
end
|
132
126
|
|
133
|
-
|
127
|
+
#: -> String
|
134
128
|
def destination_dir
|
135
129
|
if options.rails && !::Dir.new(::Dir.pwd).entries.include?('config')
|
136
130
|
raise IncorrectWorkingDirectoryError,
|
@@ -140,7 +134,7 @@ module SimpleFeatureFlags
|
|
140
134
|
::Dir.pwd
|
141
135
|
end
|
142
136
|
|
143
|
-
|
137
|
+
#: -> String
|
144
138
|
def destination_file
|
145
139
|
@destination_file ||= ::File.join(destination_dir, CONFIG_FILE)
|
146
140
|
end
|
@@ -9,23 +9,23 @@ module SimpleFeatureFlags
|
|
9
9
|
class Options
|
10
10
|
extend T::Sig
|
11
11
|
|
12
|
-
|
12
|
+
#: OptionParser
|
13
13
|
attr_reader :opt_parser
|
14
14
|
|
15
|
-
|
15
|
+
#: bool
|
16
16
|
attr_reader :generate
|
17
17
|
|
18
|
-
|
18
|
+
#: bool
|
19
19
|
attr_reader :rails
|
20
20
|
|
21
|
-
|
21
|
+
#: bool
|
22
22
|
attr_reader :ui
|
23
23
|
|
24
|
-
|
24
|
+
#: (Array[String] args) -> void
|
25
25
|
def initialize(args)
|
26
|
-
@rails =
|
27
|
-
@ui =
|
28
|
-
@generate =
|
26
|
+
@rails = true #: bool
|
27
|
+
@ui = false #: bool
|
28
|
+
@generate = false #: bool
|
29
29
|
|
30
30
|
@opt_parser = ::OptionParser.new do |opts|
|
31
31
|
opts.banner = 'Usage: simple_feature_flags [options]'
|
@@ -7,15 +7,15 @@ module SimpleFeatureFlags
|
|
7
7
|
class Runner
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
|
10
|
+
#: Options
|
11
11
|
attr_reader :options
|
12
12
|
|
13
|
-
|
13
|
+
#: (?Array[String] args) -> void
|
14
14
|
def initialize(args = ARGV)
|
15
15
|
@options = Options.new(args)
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
#: -> void
|
19
19
|
def run
|
20
20
|
command_class =
|
21
21
|
if @options.generate
|