Module: Feature
- Defined in:
- lib/feature/shared.rb,
lib/feature.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, Logger, OptOut, Target
Constant Summary
collapse
- 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
76
77
78
|
# File 'lib/feature.rb', line 76
def all
flipper.features.to_a
end
|
.current_pod ⇒ Object
266
267
268
|
# File 'lib/feature.rb', line 266
def current_pod
@flipper_pod ||= FlipperPod.new
end
|
.current_request ⇒ Object
258
259
260
261
262
263
264
|
# File 'lib/feature.rb', line 258
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
158
159
160
161
162
163
164
|
# File 'lib/feature.rb', line 158
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
223
224
225
226
|
# File 'lib/feature.rb', line 223
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
209
210
211
212
|
# File 'lib/feature.rb', line 209
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
136
137
138
139
140
141
|
# File 'lib/feature.rb', line 136
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
# File 'lib/feature.rb', line 143
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
214
215
216
217
218
219
220
221
|
# File 'lib/feature.rb', line 214
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
200
201
202
203
204
205
206
207
|
# File 'lib/feature.rb', line 200
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`
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
# File 'lib/feature.rb', line 119
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
91
92
93
|
# File 'lib/feature.rb', line 91
def get(key)
with_feature(key, &:itself)
end
|
.gitlab_instance ⇒ Object
270
271
272
|
# File 'lib/feature.rb', line 270
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.
291
292
293
294
295
296
|
# File 'lib/feature.rb', line 291
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
282
283
284
|
# File 'lib/feature.rb', line 282
def log_feature_flag_state(key, feature_value)
logged_states[key] ||= feature_value
end
|
.log_feature_flag_states?(key) ⇒ Boolean
.logged_states ⇒ Object
286
287
288
|
# File 'lib/feature.rb', line 286
def logged_states
RequestStore.fetch(:feature_flag_events) { {} }
end
|
.logger ⇒ Object
274
275
276
|
# File 'lib/feature.rb', line 274
def logger
@logger ||= Feature::Logger.build
end
|
.opt_out(key, thing) ⇒ Object
177
178
179
180
181
182
183
184
185
186
|
# File 'lib/feature.rb', line 177
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
166
167
168
169
170
171
172
173
174
175
|
# File 'lib/feature.rb', line 166
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
107
108
109
110
111
112
|
# File 'lib/feature.rb', line 107
def persisted_name?(feature_name)
persisted_names.include?(feature_name.to_s)
end
|
.persisted_names ⇒ Object
95
96
97
98
99
100
101
102
103
104
105
|
# File 'lib/feature.rb', line 95
def persisted_names
return [] unless FlipperRecord.database.exists?
flipper.adapter.features
end
|
.preload(names) ⇒ Object
85
86
87
|
# File 'lib/feature.rb', line 85
def preload(names)
flipper.preload(names) end
|
.register_definitions ⇒ Object
.register_feature_groups ⇒ Object
246
|
# File 'lib/feature.rb', line 246
def register_feature_groups; end
|
.register_hot_reloader ⇒ Object
.remove(key) ⇒ Object
228
229
230
231
232
233
234
|
# File 'lib/feature.rb', line 228
def remove(key)
return unless persisted_name?(key)
log(key: key, action: __method__)
with_feature(key, &:remove)
end
|
.remove_opt_out(key, thing) ⇒ Object
188
189
190
191
192
193
194
195
196
197
198
|
# File 'lib/feature.rb', line 188
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
236
237
238
239
|
# File 'lib/feature.rb', line 236
def reset
Gitlab::SafeRequestStore.delete(:flipper) if Gitlab::SafeRequestStore.active?
@flipper = nil
end
|