simple_feature_flags 1.2.0 → 1.4.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 +15 -58
- data/.ruby-version +1 -1
- data/.vscode/settings.json +5 -1
- data/Gemfile +11 -1
- data/Gemfile.lock +84 -70
- 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 +332 -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 +292 -80
- data/lib/simple_feature_flags/redis_storage.rb +282 -63
- 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 +23 -6
- data/simple_feature_flags.gemspec +17 -22
- metadata +19 -128
- data/.travis.yml +0 -6
@@ -0,0 +1,332 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module SimpleFeatureFlags
|
7
|
+
# Abstract class for all storage adapters.
|
8
|
+
class BaseStorage
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Helpers
|
11
|
+
|
12
|
+
abstract!
|
13
|
+
|
14
|
+
# Path to the file with feature flags
|
15
|
+
sig { abstract.returns(String) }
|
16
|
+
def file; end
|
17
|
+
|
18
|
+
sig { abstract.returns(T::Array[String]) }
|
19
|
+
def mandatory_flags; end
|
20
|
+
|
21
|
+
# Checks whether the flag is active. Returns `true`, `false`, `:globally` or `:partially`
|
22
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T.any(Symbol, T::Boolean)) }
|
23
|
+
def active(feature); end
|
24
|
+
|
25
|
+
# Checks whether the flag is active.
|
26
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
27
|
+
def active?(feature); end
|
28
|
+
|
29
|
+
# Checks whether the flag is inactive.
|
30
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
31
|
+
def inactive?(feature); end
|
32
|
+
|
33
|
+
# Checks whether the flag is active globally, for every object.
|
34
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
35
|
+
def active_globally?(feature); end
|
36
|
+
|
37
|
+
# Checks whether the flag is inactive globally, for every object.
|
38
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
39
|
+
def inactive_globally?(feature); end
|
40
|
+
|
41
|
+
# Checks whether the flag is active partially, only for certain objects.
|
42
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
43
|
+
def active_partially?(feature); end
|
44
|
+
|
45
|
+
# Checks whether the flag is inactive partially, only for certain objects.
|
46
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
47
|
+
def inactive_partially?(feature); end
|
48
|
+
|
49
|
+
# Checks whether the flag is active for the given object.
|
50
|
+
sig do
|
51
|
+
abstract
|
52
|
+
.params(
|
53
|
+
feature: T.any(Symbol, String),
|
54
|
+
object: Object,
|
55
|
+
object_id_method: Symbol,
|
56
|
+
)
|
57
|
+
.returns(T::Boolean)
|
58
|
+
end
|
59
|
+
def active_for?(feature, object, object_id_method: :id); end
|
60
|
+
|
61
|
+
# Checks whether the flag is inactive for the given object.
|
62
|
+
sig do
|
63
|
+
abstract
|
64
|
+
.params(
|
65
|
+
feature: T.any(Symbol, String),
|
66
|
+
object: Object,
|
67
|
+
object_id_method: Symbol,
|
68
|
+
)
|
69
|
+
.returns(T::Boolean)
|
70
|
+
end
|
71
|
+
def inactive_for?(feature, object, object_id_method: :id); end
|
72
|
+
|
73
|
+
# Checks whether the flag exists.
|
74
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
75
|
+
def exists?(feature); end
|
76
|
+
|
77
|
+
# Returns the description of the flag if it exists.
|
78
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T.nilable(String)) }
|
79
|
+
def description(feature); end
|
80
|
+
|
81
|
+
# Calls the given block if the flag is active.
|
82
|
+
sig do
|
83
|
+
abstract
|
84
|
+
.params(
|
85
|
+
feature: T.any(Symbol, String),
|
86
|
+
block: T.proc.void,
|
87
|
+
).void
|
88
|
+
end
|
89
|
+
def when_active(feature, &block); end
|
90
|
+
|
91
|
+
# Calls the given block if the flag is inactive.
|
92
|
+
sig do
|
93
|
+
abstract
|
94
|
+
.params(
|
95
|
+
feature: T.any(Symbol, String),
|
96
|
+
block: T.proc.void,
|
97
|
+
).void
|
98
|
+
end
|
99
|
+
def when_inactive(feature, &block); end
|
100
|
+
|
101
|
+
# Calls the given block if the flag is active globally.
|
102
|
+
sig do
|
103
|
+
abstract
|
104
|
+
.params(
|
105
|
+
feature: T.any(Symbol, String),
|
106
|
+
block: T.proc.void,
|
107
|
+
).void
|
108
|
+
end
|
109
|
+
def when_active_globally(feature, &block); end
|
110
|
+
|
111
|
+
# Calls the given block if the flag is inactive globally.
|
112
|
+
sig do
|
113
|
+
abstract
|
114
|
+
.params(
|
115
|
+
feature: T.any(Symbol, String),
|
116
|
+
block: T.proc.void,
|
117
|
+
).void
|
118
|
+
end
|
119
|
+
def when_inactive_globally(feature, &block); end
|
120
|
+
|
121
|
+
# Calls the given block if the flag is active partially.
|
122
|
+
sig do
|
123
|
+
abstract
|
124
|
+
.params(
|
125
|
+
feature: T.any(Symbol, String),
|
126
|
+
block: T.proc.void,
|
127
|
+
).void
|
128
|
+
end
|
129
|
+
def when_active_partially(feature, &block); end
|
130
|
+
|
131
|
+
# Calls the given block if the flag is inactive partially.
|
132
|
+
sig do
|
133
|
+
abstract
|
134
|
+
.params(
|
135
|
+
feature: T.any(Symbol, String),
|
136
|
+
block: T.proc.void,
|
137
|
+
).void
|
138
|
+
end
|
139
|
+
def when_inactive_partially(feature, &block); end
|
140
|
+
|
141
|
+
# Calls the given block if the flag is active for the given object.
|
142
|
+
sig do
|
143
|
+
abstract
|
144
|
+
.params(
|
145
|
+
feature: T.any(Symbol, String),
|
146
|
+
object: Object,
|
147
|
+
object_id_method: Symbol,
|
148
|
+
block: T.proc.void,
|
149
|
+
).void
|
150
|
+
end
|
151
|
+
def when_active_for(feature, object, object_id_method: CONFIG.default_id_method, &block); end
|
152
|
+
|
153
|
+
# Calls the given block if the flag is inactive for the given object.
|
154
|
+
sig do
|
155
|
+
abstract
|
156
|
+
.params(
|
157
|
+
feature: T.any(Symbol, String),
|
158
|
+
object: Object,
|
159
|
+
object_id_method: Symbol,
|
160
|
+
block: T.proc.void,
|
161
|
+
).void
|
162
|
+
end
|
163
|
+
def when_inactive_for(feature, object, object_id_method: CONFIG.default_id_method, &block); end
|
164
|
+
|
165
|
+
# Activates the given flag. Returns `false` if it does not exist.
|
166
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
167
|
+
def activate(feature); end
|
168
|
+
|
169
|
+
# Activates the flag, calls the block and restores the previous state of the flag.
|
170
|
+
sig do
|
171
|
+
abstract
|
172
|
+
.type_parameters(:R)
|
173
|
+
.params(
|
174
|
+
feature: T.any(Symbol, String),
|
175
|
+
block: T.proc.returns(T.type_parameter(:R)),
|
176
|
+
)
|
177
|
+
.returns(T.type_parameter(:R))
|
178
|
+
end
|
179
|
+
def do_activate(feature, &block); end
|
180
|
+
|
181
|
+
# Activates the given flag globally. Returns `false` if it does not exist.
|
182
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
183
|
+
def activate_globally(feature); end
|
184
|
+
|
185
|
+
# Activates the flag globally, calls the block and restores the previous state of the flag.
|
186
|
+
sig do
|
187
|
+
abstract
|
188
|
+
.type_parameters(:R)
|
189
|
+
.params(
|
190
|
+
feature: T.any(Symbol, String),
|
191
|
+
block: T.proc.returns(T.type_parameter(:R)),
|
192
|
+
)
|
193
|
+
.returns(T.type_parameter(:R))
|
194
|
+
end
|
195
|
+
def do_activate_globally(feature, &block); end
|
196
|
+
|
197
|
+
# Activates the given flag partially. Returns `false` if it does not exist.
|
198
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
199
|
+
def activate_partially(feature); end
|
200
|
+
|
201
|
+
# Activates the flag partially, calls the block and restores the previous state of the flag.
|
202
|
+
sig do
|
203
|
+
abstract
|
204
|
+
.type_parameters(:R)
|
205
|
+
.params(
|
206
|
+
feature: T.any(Symbol, String),
|
207
|
+
block: T.proc.returns(T.type_parameter(:R)),
|
208
|
+
)
|
209
|
+
.returns(T.type_parameter(:R))
|
210
|
+
end
|
211
|
+
def do_activate_partially(feature, &block); end
|
212
|
+
|
213
|
+
# Activates the given flag for the given objects. Returns `false` if it does not exist.
|
214
|
+
sig do
|
215
|
+
abstract
|
216
|
+
.params(
|
217
|
+
feature: T.any(Symbol, String),
|
218
|
+
objects: Object,
|
219
|
+
object_id_method: Symbol,
|
220
|
+
).void
|
221
|
+
end
|
222
|
+
def activate_for(feature, *objects, object_id_method: CONFIG.default_id_method); end
|
223
|
+
|
224
|
+
# Activates the given flag for the given objects and sets the flag as partially active.
|
225
|
+
# Returns `false` if it does not exist.
|
226
|
+
sig do
|
227
|
+
abstract
|
228
|
+
.params(
|
229
|
+
feature: T.any(Symbol, String),
|
230
|
+
objects: Object,
|
231
|
+
object_id_method: Symbol,
|
232
|
+
).void
|
233
|
+
end
|
234
|
+
def activate_for!(feature, *objects, object_id_method: CONFIG.default_id_method); end
|
235
|
+
|
236
|
+
# Deactivates the given flag for all objects.
|
237
|
+
# Resets the list of objects that this flag has been turned on for.
|
238
|
+
# Returns `false` if it does not exist.
|
239
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
240
|
+
def deactivate!(feature); end
|
241
|
+
|
242
|
+
# Deactivates the given flag globally.
|
243
|
+
# Does not reset the list of objects that this flag has been turned on for.
|
244
|
+
# Returns `false` if it does not exist.
|
245
|
+
sig { abstract.params(feature: T.any(Symbol, String)).returns(T::Boolean) }
|
246
|
+
def deactivate(feature); end
|
247
|
+
|
248
|
+
# Returns a hash of Objects that the given flag is turned on for.
|
249
|
+
# The keys are class/model names, values are arrays of IDs of instances/records.
|
250
|
+
#
|
251
|
+
# looks like this:
|
252
|
+
#
|
253
|
+
# { "Page" => [25, 89], "Book" => [152] }
|
254
|
+
#
|
255
|
+
sig do
|
256
|
+
abstract
|
257
|
+
.params(feature: T.any(Symbol, String))
|
258
|
+
.returns(T::Hash[String, T::Array[Object]])
|
259
|
+
end
|
260
|
+
def active_objects(feature); end
|
261
|
+
|
262
|
+
# Deactivates the given flag for the given objects. Returns `false` if it does not exist.
|
263
|
+
sig do
|
264
|
+
abstract
|
265
|
+
.params(
|
266
|
+
feature: T.any(Symbol, String),
|
267
|
+
objects: Object,
|
268
|
+
object_id_method: Symbol,
|
269
|
+
).void
|
270
|
+
end
|
271
|
+
def deactivate_for(feature, *objects, object_id_method: CONFIG.default_id_method); end
|
272
|
+
|
273
|
+
# Returns the data of the flag in a hash.
|
274
|
+
sig do
|
275
|
+
abstract
|
276
|
+
.params(
|
277
|
+
feature: T.any(Symbol, String),
|
278
|
+
).returns(T.nilable(T::Hash[String, T.anything]))
|
279
|
+
end
|
280
|
+
def get(feature); end
|
281
|
+
|
282
|
+
# Adds the given feature flag.
|
283
|
+
sig do
|
284
|
+
abstract
|
285
|
+
.params(
|
286
|
+
feature: T.any(Symbol, String),
|
287
|
+
description: String,
|
288
|
+
active: T.any(String, Symbol, T::Boolean, NilClass),
|
289
|
+
).returns(T.nilable(T::Hash[String, T.anything]))
|
290
|
+
end
|
291
|
+
def add(feature, description = '', active = 'false'); end
|
292
|
+
|
293
|
+
# Removes the given feature flag.
|
294
|
+
# Returns its data or nil if it does not exist.
|
295
|
+
sig do
|
296
|
+
abstract
|
297
|
+
.params(
|
298
|
+
feature: T.any(Symbol, String),
|
299
|
+
).returns(T.nilable(T::Hash[String, T.anything]))
|
300
|
+
end
|
301
|
+
def remove(feature); end
|
302
|
+
|
303
|
+
# Returns the data of all feature flags.
|
304
|
+
sig do
|
305
|
+
abstract.returns(T::Array[T::Hash[String, T.anything]])
|
306
|
+
end
|
307
|
+
def all; end
|
308
|
+
|
309
|
+
private
|
310
|
+
|
311
|
+
sig { params(objects: T::Array[Object], object_id_method: Symbol).returns(T::Hash[String, T::Array[Object]]) }
|
312
|
+
def objects_to_hash(objects, object_id_method: CONFIG.default_id_method)
|
313
|
+
objects.group_by { |ob| ob.class.to_s }
|
314
|
+
.transform_values { |arr| arr.map(&object_id_method) }
|
315
|
+
end
|
316
|
+
|
317
|
+
sig { void }
|
318
|
+
def import_flags_from_file
|
319
|
+
changes = YAML.load_file(file)
|
320
|
+
changes = { mandatory: [], remove: [] } unless changes.is_a? ::Hash
|
321
|
+
|
322
|
+
changes[:mandatory].each do |el|
|
323
|
+
mandatory_flags << el['name']
|
324
|
+
add(el['name'], el['description'], el['active'])
|
325
|
+
end
|
326
|
+
|
327
|
+
changes[:remove].each do |el|
|
328
|
+
remove(el)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'fileutils'
|
@@ -5,15 +6,21 @@ require 'fileutils'
|
|
5
6
|
module SimpleFeatureFlags
|
6
7
|
module Cli
|
7
8
|
module Command
|
9
|
+
# Implements the `generate` CLI command
|
8
10
|
class Generate
|
9
|
-
|
11
|
+
extend T::Sig
|
10
12
|
|
13
|
+
CONFIG_FILE = T.let('simple_feature_flags.yml', String)
|
14
|
+
|
15
|
+
sig { returns(Options) }
|
11
16
|
attr_reader :options
|
12
17
|
|
18
|
+
sig { params(options: Options).void }
|
13
19
|
def initialize(options)
|
14
20
|
@options = options
|
15
21
|
end
|
16
22
|
|
23
|
+
sig { void }
|
17
24
|
def run
|
18
25
|
if options.rails
|
19
26
|
generate_for_rails
|
@@ -29,10 +36,11 @@ module SimpleFeatureFlags
|
|
29
36
|
|
30
37
|
private
|
31
38
|
|
39
|
+
sig { void }
|
32
40
|
def generate_for_rails
|
33
41
|
::FileUtils.cp_r example_config_dir, destination_dir
|
34
42
|
|
35
|
-
puts
|
43
|
+
puts 'Generated:'
|
36
44
|
puts '----------'
|
37
45
|
puts "- #{::File.join(destination_dir, 'config')}"
|
38
46
|
print_dir_tree(example_config_dir, 1)
|
@@ -62,21 +70,30 @@ module SimpleFeatureFlags
|
|
62
70
|
system 'bundle'
|
63
71
|
end
|
64
72
|
|
73
|
+
sig do
|
74
|
+
params(
|
75
|
+
file_path: String,
|
76
|
+
regexp: Regexp,
|
77
|
+
block: T.proc.params(arg0: String).returns(String),
|
78
|
+
).void
|
79
|
+
end
|
65
80
|
def file_gsub(file_path, regexp, &block)
|
66
81
|
new_content = File.read(file_path).gsub(regexp, &block)
|
67
|
-
File.
|
82
|
+
File.binwrite(file_path, new_content)
|
68
83
|
end
|
69
84
|
|
85
|
+
sig { params(file_path: String, line: String).void }
|
70
86
|
def file_append(file_path, line)
|
71
87
|
new_content = File.read(file_path)
|
72
88
|
new_content = "#{new_content}\n#{line}\n"
|
73
|
-
File.
|
89
|
+
File.binwrite(file_path, new_content)
|
74
90
|
end
|
75
91
|
|
92
|
+
sig { params(dir: String, embed_level: Integer).void }
|
76
93
|
def print_dir_tree(dir, embed_level = 0)
|
77
94
|
padding = ' ' * (embed_level * 2)
|
78
95
|
|
79
|
-
children = ::Dir.new(dir).entries.
|
96
|
+
children = ::Dir.new(dir).entries.grep_v(/^\.{1,2}$/)
|
80
97
|
|
81
98
|
children.each do |child|
|
82
99
|
child_dir = ::File.join(dir, child)
|
@@ -88,32 +105,42 @@ module SimpleFeatureFlags
|
|
88
105
|
end
|
89
106
|
end
|
90
107
|
|
108
|
+
sig { returns String }
|
91
109
|
def initializer_file
|
92
110
|
::File.join(destination_dir, 'config', 'initializers', 'simple_feature_flags.rb')
|
93
111
|
end
|
94
112
|
|
113
|
+
sig { returns String }
|
95
114
|
def gemfile
|
96
115
|
::File.join(destination_dir, 'Gemfile')
|
97
116
|
end
|
98
117
|
|
118
|
+
sig { returns String }
|
99
119
|
def routes_rb
|
100
120
|
::File.join(destination_dir, 'config', 'routes.rb')
|
101
121
|
end
|
102
122
|
|
123
|
+
sig { returns String }
|
103
124
|
def example_config_dir
|
104
125
|
::File.join(::File.expand_path(__dir__), '..', '..', '..', 'example_files', 'config')
|
105
126
|
end
|
106
127
|
|
128
|
+
sig { returns String }
|
107
129
|
def example_config_file
|
108
130
|
::File.join(example_config_dir, CONFIG_FILE)
|
109
131
|
end
|
110
132
|
|
133
|
+
sig { returns String }
|
111
134
|
def destination_dir
|
112
|
-
|
135
|
+
if options.rails && !::Dir.new(::Dir.pwd).entries.include?('config')
|
136
|
+
raise IncorrectWorkingDirectoryError,
|
137
|
+
'You should enter the main directory of your Rails project!'
|
138
|
+
end
|
113
139
|
|
114
140
|
::Dir.pwd
|
115
141
|
end
|
116
142
|
|
143
|
+
sig { returns String }
|
117
144
|
def destination_file
|
118
145
|
@destination_file ||= ::File.join(destination_dir, CONFIG_FILE)
|
119
146
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module SimpleFeatureFlags
|
4
5
|
module Cli
|
6
|
+
# Contains CLI commands
|
5
7
|
module Command; end
|
6
8
|
end
|
7
9
|
end
|
8
10
|
|
9
|
-
Dir[File.expand_path('command/*.rb', __dir__)].
|
11
|
+
Dir[File.expand_path('command/*.rb', __dir__)].each { |file| require file }
|
@@ -1,15 +1,31 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'optparse'
|
4
5
|
|
5
6
|
module SimpleFeatureFlags
|
6
7
|
module Cli
|
8
|
+
# Parses CLI options.
|
7
9
|
class Options
|
8
|
-
|
10
|
+
extend T::Sig
|
9
11
|
|
12
|
+
sig { returns(OptionParser) }
|
13
|
+
attr_reader :opt_parser
|
14
|
+
|
15
|
+
sig { returns(T::Boolean) }
|
16
|
+
attr_reader :generate
|
17
|
+
|
18
|
+
sig { returns(T::Boolean) }
|
19
|
+
attr_reader :rails
|
20
|
+
|
21
|
+
sig { returns(T::Boolean) }
|
22
|
+
attr_reader :ui
|
23
|
+
|
24
|
+
sig { params(args: T::Array[String]).void }
|
10
25
|
def initialize(args)
|
11
|
-
@rails = true
|
12
|
-
@ui = false
|
26
|
+
@rails = T.let(true, T::Boolean)
|
27
|
+
@ui = T.let(false, T::Boolean)
|
28
|
+
@generate = T.let(false, T::Boolean)
|
13
29
|
|
14
30
|
@opt_parser = ::OptionParser.new do |opts|
|
15
31
|
opts.banner = 'Usage: simple_feature_flags [options]'
|
@@ -1,20 +1,28 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module SimpleFeatureFlags
|
4
5
|
module Cli
|
6
|
+
# Runs CLI commands
|
5
7
|
class Runner
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { returns(Options) }
|
6
11
|
attr_reader :options
|
7
12
|
|
13
|
+
sig { params(args: T::Array[String]).void }
|
8
14
|
def initialize(args = ARGV)
|
9
15
|
@options = Options.new(args)
|
10
16
|
end
|
11
17
|
|
18
|
+
sig { void }
|
12
19
|
def run
|
13
|
-
command_class =
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
20
|
+
command_class =
|
21
|
+
if @options.generate
|
22
|
+
::SimpleFeatureFlags::Cli::Command::Generate
|
23
|
+
else
|
24
|
+
raise NoSuchCommandError, 'No such command!'
|
25
|
+
end
|
18
26
|
|
19
27
|
command_class.new(options).run
|
20
28
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module SimpleFeatureFlags
|
5
|
+
# Handles the CLI
|
4
6
|
module Cli; end
|
5
7
|
end
|
6
8
|
|
7
|
-
Dir[File.expand_path('cli/*.rb', __dir__)].
|
9
|
+
Dir[File.expand_path('cli/*.rb', __dir__)].each { |file| require file }
|
@@ -1,9 +1,15 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module SimpleFeatureFlags
|
5
|
+
# The main configuration object of the library.
|
4
6
|
class Configuration
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { returns(Symbol) }
|
5
10
|
attr_accessor :default_id_method
|
6
11
|
|
12
|
+
sig { void }
|
7
13
|
def initialize
|
8
14
|
@default_id_method = :id
|
9
15
|
end
|