Module: Feature
- Defined in:
- lib/feature/shared.rb,
lib/feature.rb,
lib/feature/kas.rb,
lib/feature/gitaly.rb,
lib/feature/logger.rb,
lib/feature/definition.rb,
lib/feature/actor_wrapper.rb
Overview
This file can contain only simple constructs as it is shared between:
-
‘Pure Ruby`: bin/feature-flag
-
‘GitLab Rails`: lib/feature/definition.rb
Defined Under Namespace
Modules: Shared
Classes: ActiveSupportCacheStoreAdapter, ActorWrapper, Definition, FlipperFeature, FlipperGate, FlipperGitlabInstance, FlipperPod, FlipperRecord, FlipperRequest, Gitaly, Kas, Logger, OptOut, Target
Constant Summary
collapse
- SUPPORTED_MODELS =
%w[
User
Project
Namespace
Ci::Runner
Organizations::Organization
].freeze
- InvalidFeatureFlagError =
rubocop:disable Lint/InheritException
Class.new(Exception)
- InvalidOperation =
Class.new(ArgumentError)
- RecursionError =
Class.new(RuntimeError)
Class Method Summary
collapse
-
.all ⇒ Object
-
.current_pod ⇒ Object
-
.current_request ⇒ Object
-
.disable(key, thing = false) ⇒ Object
-
.disable_percentage_of_actors(key) ⇒ Object
-
.disable_percentage_of_time(key) ⇒ Object
-
.disabled?(key, thing = nil, type: nil, default_enabled_if_undefined: nil) ⇒ Boolean
-
.enable(key, thing = true) ⇒ Object
-
.enable_percentage_of_actors(key, percentage) ⇒ Object
-
.enable_percentage_of_time(key, percentage) ⇒ Object
-
.enabled?(key, thing = nil, type: nil, default_enabled_if_undefined: nil) ⇒ Boolean
The default state of feature flag is read from YAML: 1.
-
.get(key) ⇒ Object
-
.gitlab_instance ⇒ Object
-
.group_ids_for(feature_key) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord – rubocop doesn’t recognize Flipper::Adapters::ActiveRecord::Gate as ActiveRecord.
-
.log_feature_flag_state(key, feature_value) ⇒ Object
-
.log_feature_flag_states?(key) ⇒ Boolean
-
.logged_states ⇒ Object
-
.logger ⇒ Object
-
.opt_out(key, thing) ⇒ Object
-
.opted_out?(key, thing) ⇒ Boolean
-
.persisted_name?(feature_name) ⇒ Boolean
-
.persisted_names ⇒ Object
-
.preload(names) ⇒ Object
Preload the features with the given names.
-
.register_definitions ⇒ Object
-
.register_feature_groups ⇒ Object
This method is called from config/initializers/0_inject_feature_flags.rb and can be used to register Flipper groups.
-
.register_hot_reloader ⇒ Object
-
.remove(key) ⇒ Object
-
.remove_opt_out(key, thing) ⇒ Object
-
.reset ⇒ Object
Class Method Details
.all ⇒ Object
85
86
87
|
# File 'lib/feature.rb', line 85
def all
flipper.features.to_a
end
|
.current_pod ⇒ Object
275
276
277
|
# File 'lib/feature.rb', line 275
def current_pod
@flipper_pod ||= FlipperPod.new
end
|
.current_request ⇒ Object
267
268
269
270
271
272
273
|
# File 'lib/feature.rb', line 267
def current_request
if Gitlab::SafeRequestStore.active?
Gitlab::SafeRequestStore[:flipper_request] ||= FlipperRequest.new
else
@flipper_request ||= FlipperRequest.new
end
end
|
.disable(key, thing = false) ⇒ Object
167
168
169
170
171
172
173
|
# File 'lib/feature.rb', line 167
def disable(key, thing = false)
thing = sanitized_thing(thing)
log(key: key, action: __method__, thing: thing)
with_feature(key) { _1.disable(thing) }
end
|
.disable_percentage_of_actors(key) ⇒ Object
232
233
234
235
|
# File 'lib/feature.rb', line 232
def disable_percentage_of_actors(key)
log(key: key, action: __method__)
with_feature(key, &:disable_percentage_of_actors)
end
|
.disable_percentage_of_time(key) ⇒ Object
218
219
220
221
|
# File 'lib/feature.rb', line 218
def disable_percentage_of_time(key)
log(key: key, action: __method__)
with_feature(key, &:disable_percentage_of_time)
end
|
.disabled?(key, thing = nil, type: nil, default_enabled_if_undefined: nil) ⇒ Boolean
145
146
147
148
149
150
|
# File 'lib/feature.rb', line 145
def disabled?(key, thing = nil, type: nil, default_enabled_if_undefined: nil)
thing = sanitized_thing(thing)
thing.nil? ? !enabled?(key, type: type, default_enabled_if_undefined: default_enabled_if_undefined) : !enabled?(key, thing, type: type, default_enabled_if_undefined: default_enabled_if_undefined)
end
|
.enable(key, thing = true) ⇒ Object
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
# File 'lib/feature.rb', line 152
def enable(key, thing = true)
thing = sanitized_thing(thing)
log(key: key, action: __method__, thing: thing)
return_value = with_feature(key) { _1.enable(thing) }
Rails.logger.warn('WARNING: Understand the stability and security risks of enabling in-development features with feature flags.')
Rails.logger.warn('See https://docs.gitlab.com/ee/administration/feature_flags.html#risks-when-enabling-features-still-in-development for more information.')
return_value
end
|
.enable_percentage_of_actors(key, percentage) ⇒ Object
223
224
225
226
227
228
229
230
|
# File 'lib/feature.rb', line 223
def enable_percentage_of_actors(key, percentage)
log(key: key, action: __method__, percentage: percentage)
with_feature(key) do |flag|
raise InvalidOperation, 'Cannot enable percentage of actors for a fully-enabled flag' if flag.state == :on
flag.enable_percentage_of_actors(percentage)
end
end
|
.enable_percentage_of_time(key, percentage) ⇒ Object
209
210
211
212
213
214
215
216
|
# File 'lib/feature.rb', line 209
def enable_percentage_of_time(key, percentage)
log(key: key, action: __method__, percentage: percentage)
with_feature(key) do |flag|
raise InvalidOperation, 'Cannot enable percentage of time for a fully-enabled flag' if flag.state == :on
flag.enable_percentage_of_time(percentage)
end
end
|
.enabled?(key, thing = nil, type: nil, default_enabled_if_undefined: nil) ⇒ Boolean
The default state of feature flag is read from YAML:
-
If feature flag does not have YAML it will fallback to ‘default_enabled: false` in production environment, but raise exception in development or tests.
-
The default_enabled_if_undefined: is tech debt related to Gitaly flags and should not be used outside of Gitaly’s lib/feature/gitaly.rb
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
# File 'lib/feature.rb', line 128
def enabled?(key, thing = nil, type: nil, default_enabled_if_undefined: nil)
thing = sanitized_thing(thing)
check_feature_flags_definition!(key, thing, type)
default_enabled = Feature::Definition.default_enabled?(key, default_enabled_if_undefined: default_enabled_if_undefined)
feature_value = current_feature_value(key, thing, default_enabled: default_enabled)
feature_value = default_enabled if feature_value.nil?
log_feature_flag_state(key, feature_value) if log_feature_flag_states?(key)
feature_value
end
|
.get(key) ⇒ Object
100
101
102
|
# File 'lib/feature.rb', line 100
def get(key)
with_feature(key, &:itself)
end
|
.gitlab_instance ⇒ Object
279
280
281
|
# File 'lib/feature.rb', line 279
def gitlab_instance
@flipper_gitlab_instance ||= FlipperGitlabInstance.new
end
|
.group_ids_for(feature_key) ⇒ Object
rubocop: disable CodeReuse/ActiveRecord – rubocop doesn’t recognize Flipper::Adapters::ActiveRecord::Gate as ActiveRecord.
300
301
302
303
304
305
|
# File 'lib/feature.rb', line 300
def group_ids_for(feature_key)
FlipperGate.where(feature_key: feature_key)
.pluck(:value)
.select { |v| v.start_with?("Group:") }
.map { |v| v.sub("Group:", "") }
end
|
.log_feature_flag_state(key, feature_value) ⇒ Object
291
292
293
|
# File 'lib/feature.rb', line 291
def log_feature_flag_state(key, feature_value)
logged_states[key] ||= feature_value
end
|
.log_feature_flag_states?(key) ⇒ Boolean
287
288
289
|
# File 'lib/feature.rb', line 287
def log_feature_flag_states?(key)
Feature::Definition.log_states?(key)
end
|
.logged_states ⇒ Object
295
296
297
|
# File 'lib/feature.rb', line 295
def logged_states
RequestStore.fetch(:feature_flag_events) { {} }
end
|
.logger ⇒ Object
283
284
285
|
# File 'lib/feature.rb', line 283
def logger
@logger ||= Feature::Logger.build
end
|
.opt_out(key, thing) ⇒ Object
186
187
188
189
190
191
192
193
194
195
|
# File 'lib/feature.rb', line 186
def opt_out(key, thing)
thing = sanitized_thing(thing)
return unless thing.respond_to?(:flipper_id)
log(key: key, action: __method__, thing: thing)
opt_out = OptOut.new(thing)
with_feature(key) { _1.enable(opt_out) }
end
|
.opted_out?(key, thing) ⇒ Boolean
175
176
177
178
179
180
181
182
183
184
|
# File 'lib/feature.rb', line 175
def opted_out?(key, thing)
thing = sanitized_thing(thing)
return false unless thing.respond_to?(:flipper_id)
return false unless persisted_name?(key)
opt_out = OptOut.new(thing)
with_feature(key) { _1.actors_value.include?(opt_out.flipper_id) }
end
|
.persisted_name?(feature_name) ⇒ Boolean
116
117
118
119
120
121
|
# File 'lib/feature.rb', line 116
def persisted_name?(feature_name)
persisted_names.include?(feature_name.to_s)
end
|
.persisted_names ⇒ Object
104
105
106
107
108
109
110
111
112
113
114
|
# File 'lib/feature.rb', line 104
def persisted_names
return [] unless database_exists?
flipper.adapter.features
end
|
.preload(names) ⇒ Object
94
95
96
|
# File 'lib/feature.rb', line 94
def preload(names)
flipper.preload(names)
end
|
.register_definitions ⇒ Object
257
258
259
|
# File 'lib/feature.rb', line 257
def register_definitions
Feature::Definition.reload!
end
|
.register_feature_groups ⇒ Object
This method is called from config/initializers/0_inject_feature_flags.rb and can be used to register Flipper groups. See docs.gitlab.com/ee/development/feature_flags/
EE feature groups should go inside the ee/lib/ee/feature.rb version of this method.
255
|
# File 'lib/feature.rb', line 255
def register_feature_groups; end
|
.register_hot_reloader ⇒ Object
261
262
263
264
265
|
# File 'lib/feature.rb', line 261
def register_hot_reloader
return unless check_feature_flags_definition?
Feature::Definition.register_hot_reloader!
end
|
.remove(key) ⇒ Object
237
238
239
240
241
242
243
|
# File 'lib/feature.rb', line 237
def remove(key)
return unless persisted_name?(key)
log(key: key, action: __method__)
with_feature(key, &:remove)
end
|
.remove_opt_out(key, thing) ⇒ Object
197
198
199
200
201
202
203
204
205
206
207
|
# File 'lib/feature.rb', line 197
def remove_opt_out(key, thing)
thing = sanitized_thing(thing)
return unless thing.respond_to?(:flipper_id)
return unless persisted_name?(key)
log(key: key, action: __method__, thing: thing)
opt_out = OptOut.new(thing)
with_feature(key) { _1.disable(opt_out) }
end
|
.reset ⇒ Object
245
246
247
248
|
# File 'lib/feature.rb', line 245
def reset
Gitlab::SafeRequestStore.delete(:flipper) if Gitlab::SafeRequestStore.active?
@flipper = nil
end
|