Module: Feature
- Defined in:
- lib/feature/shared.rb,
lib/feature.rb,
lib/feature/gitaly.rb,
lib/feature/logger.rb,
lib/feature/definition.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, Definition, FlipperFeature, FlipperGate, FlipperRequest, Gitaly, Logger, OptOut, Target
Constant Summary
collapse
- InvalidFeatureFlagError =
rubocop:disable Lint/InheritException
Class.new(Exception)
- InvalidOperation =
rubocop:disable Lint/InheritException
Class.new(ArgumentError)
- RecursionError =
Class.new(RuntimeError)
Class Method Summary
collapse
-
.all ⇒ 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: :development, 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: :development, default_enabled_if_undefined: nil) ⇒ Boolean
The default state of feature flag is read from ‘YAML`: 1.
-
.get(key) ⇒ Object
-
.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
-
.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
57
58
59
|
# File 'lib/feature.rb', line 57
def all
flipper.features.to_a
end
|
.current_request ⇒ Object
223
224
225
226
227
228
229
|
# File 'lib/feature.rb', line 223
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
131
132
133
134
135
|
# File 'lib/feature.rb', line 131
def disable(key, thing = false)
log(key: key, action: __method__, thing: thing)
with_feature(key) { _1.disable(thing) }
end
|
.disable_percentage_of_actors(key) ⇒ Object
188
189
190
191
|
# File 'lib/feature.rb', line 188
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
174
175
176
177
|
# File 'lib/feature.rb', line 174
def disable_percentage_of_time(key)
log(key: key, action: __method__)
with_feature(key, &:disable_percentage_of_time)
end
|
.disabled?(key, thing = nil, type: :development, default_enabled_if_undefined: nil) ⇒ Boolean
113
114
115
116
|
# File 'lib/feature.rb', line 113
def disabled?(key, thing = nil, type: :development, default_enabled_if_undefined: nil)
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
118
119
120
121
122
123
124
125
126
127
128
129
|
# File 'lib/feature.rb', line 118
def enable(key, thing = true)
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
179
180
181
182
183
184
185
186
|
# File 'lib/feature.rb', line 179
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
165
166
167
168
169
170
171
172
|
# File 'lib/feature.rb', line 165
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: :development, 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`
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
# File 'lib/feature.rb', line 91
def enabled?(key, thing = nil, type: :development, default_enabled_if_undefined: nil)
if check_feature_flags_definition?
if thing && !thing.respond_to?(:flipper_id) && !thing.is_a?(Flipper::Types::Group)
raise InvalidFeatureFlagError,
"The thing '#{thing.class.name}' for feature flag '#{key}' needs to include `FeatureGate` or implement `flipper_id`"
end
Feature::Definition.valid_usage!(key, type: type)
end
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
63
64
65
|
# File 'lib/feature.rb', line 63
def get(key)
with_feature(key, &:itself)
end
|
.log_feature_flag_state(key, feature_value) ⇒ Object
239
240
241
|
# File 'lib/feature.rb', line 239
def log_feature_flag_state(key, feature_value)
logged_states[key] ||= feature_value
end
|
.log_feature_flag_states?(key) ⇒ Boolean
.logged_states ⇒ Object
243
244
245
|
# File 'lib/feature.rb', line 243
def logged_states
RequestStore.fetch(:feature_flag_events) { {} }
end
|
.logger ⇒ Object
231
232
233
|
# File 'lib/feature.rb', line 231
def logger
@logger ||= Feature::Logger.build
end
|
.opt_out(key, thing) ⇒ Object
146
147
148
149
150
151
152
153
|
# File 'lib/feature.rb', line 146
def opt_out(key, 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
137
138
139
140
141
142
143
144
|
# File 'lib/feature.rb', line 137
def opted_out?(key, 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
79
80
81
82
83
84
|
# File 'lib/feature.rb', line 79
def persisted_name?(feature_name)
persisted_names.include?(feature_name.to_s)
end
|
.persisted_names ⇒ Object
67
68
69
70
71
72
73
74
75
76
77
|
# File 'lib/feature.rb', line 67
def persisted_names
return [] unless ApplicationRecord.database.exists?
flipper.adapter.features
end
|
.register_definitions ⇒ Object
.register_feature_groups ⇒ Object
211
|
# File 'lib/feature.rb', line 211
def register_feature_groups; end
|
.register_hot_reloader ⇒ Object
.remove(key) ⇒ Object
193
194
195
196
197
198
199
|
# File 'lib/feature.rb', line 193
def remove(key)
return unless persisted_name?(key)
log(key: key, action: __method__)
with_feature(key, &:remove)
end
|
.remove_opt_out(key, thing) ⇒ Object
155
156
157
158
159
160
161
162
163
|
# File 'lib/feature.rb', line 155
def remove_opt_out(key, 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
201
202
203
204
|
# File 'lib/feature.rb', line 201
def reset
Gitlab::SafeRequestStore.delete(:flipper) if Gitlab::SafeRequestStore.active?
@flipper = nil
end
|