gitlab-experiment 0.7.1 → 0.8.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/lib/generators/gitlab/experiment/install/templates/initializer.rb.tt +1 -1
- data/lib/gitlab/experiment/base_interface.rb +17 -63
- data/lib/gitlab/experiment/cache.rb +1 -1
- data/lib/gitlab/experiment/callbacks.rb +2 -2
- data/lib/gitlab/experiment/configuration.rb +2 -67
- data/lib/gitlab/experiment/engine.rb +1 -0
- data/lib/gitlab/experiment/rollout/percent.rb +1 -1
- data/lib/gitlab/experiment/rollout.rb +8 -30
- data/lib/gitlab/experiment/rspec.rb +15 -18
- data/lib/gitlab/experiment/version.rb +1 -1
- data/lib/gitlab/experiment.rb +20 -67
- metadata +27 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c92b7e2d77e534a23b8233f11539fa9c2767b21adab9c247e916933724dd7f5d
|
4
|
+
data.tar.gz: 7a1144676a16835f64eaefb9ea7bac2b7055c5cf8e329d55dba6c115be17eb50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7a645b7d2c3cf13d7031fc61dfdaef2626d0092661093a19ff5e21d346adda8cbe031b5c360de21fda3ecee828eaa7bda0a6afe839b6c5ce7ba57f9e10232eb
|
7
|
+
data.tar.gz: 1a8e4672a903746f5f31f2cf5dcf815bb6e2c3cb8c8fa73430c93462977310fcd6ed2f6f8676d5e2ccf856289c6befde6469e9f29715341c4bde7fd0b14fa51b
|
@@ -51,7 +51,7 @@ Gitlab::Experiment.configure do |config|
|
|
51
51
|
# :percent (recommended), :round_robin, or :random
|
52
52
|
config.default_rollout = :percent, {
|
53
53
|
include_control: true # include control in possible assignments
|
54
|
-
|
54
|
+
}
|
55
55
|
|
56
56
|
# Secret seed used in generating context keys.
|
57
57
|
#
|
@@ -78,79 +78,33 @@ module Gitlab
|
|
78
78
|
|
79
79
|
alias_method :to_param, :id
|
80
80
|
|
81
|
-
def
|
82
|
-
|
83
|
-
end
|
81
|
+
def process_redirect_url(url)
|
82
|
+
return unless Configuration.redirect_url_validator&.call(url)
|
84
83
|
|
85
|
-
|
86
|
-
|
84
|
+
track('visited', url: url)
|
85
|
+
url # return the url, which allows for mutation
|
87
86
|
end
|
88
87
|
|
89
|
-
|
90
|
-
|
91
|
-
named_variants = %w[control candidate]
|
92
|
-
public_methods.each_with_object(behaviors) do |name, behaviors|
|
93
|
-
name = name.to_s # fixes compatibility for ruby 2.6.x
|
94
|
-
next unless name.end_with?('_behavior')
|
95
|
-
|
96
|
-
behavior_name = name.sub(/_behavior$/, '')
|
97
|
-
registration = named_variants.include?(behavior_name) ? behavior_name : "variant :#{behavior_name}"
|
98
|
-
|
99
|
-
Configuration.deprecated(<<~MESSAGE, version: '0.7.0', stack: 2)
|
100
|
-
using a public `#{name}` method is deprecated and will be removed from {{release}}, instead register variants using:
|
101
|
-
|
102
|
-
class #{self.class.name} < #{Configuration.base_class}
|
103
|
-
#{registration}
|
88
|
+
def key_for(source, seed = name)
|
89
|
+
return source if source.is_a?(String)
|
104
90
|
|
105
|
-
|
91
|
+
source = source.keys + source.values if source.is_a?(Hash)
|
106
92
|
|
107
|
-
|
108
|
-
|
109
|
-
end
|
110
|
-
end
|
111
|
-
MESSAGE
|
112
|
-
|
113
|
-
behaviors[behavior_name] ||= -> { send(name) } # rubocop:disable GitlabSecurity/PublicSend
|
114
|
-
end
|
115
|
-
end
|
93
|
+
ingredients = Array(source).map { |v| identify(v) }
|
94
|
+
ingredients.unshift(seed).unshift(Configuration.context_key_secret)
|
116
95
|
|
117
|
-
|
118
|
-
def session_id
|
119
|
-
Configuration.deprecated(:session_id, 'instead use `id` or use a custom rollout strategy', version: '0.7.0')
|
120
|
-
id
|
96
|
+
Digest::SHA2.new(Configuration.context_key_bit_length).hexdigest(ingredients.join('|')) # rubocop:disable Fips/OpenSSL
|
121
97
|
end
|
122
98
|
|
123
99
|
# @deprecated
|
124
|
-
def
|
125
|
-
Configuration.deprecated(
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
def use(&block)
|
131
|
-
Configuration.deprecated(:use, 'instead use `control`', version: '0.7.0')
|
132
|
-
|
133
|
-
control(&block)
|
134
|
-
end
|
135
|
-
|
136
|
-
# @deprecated
|
137
|
-
def try(name = nil, &block)
|
138
|
-
if name.present?
|
139
|
-
Configuration.deprecated(:try, "instead use `variant(:#{name})`", version: '0.7.0')
|
140
|
-
variant(name, &block)
|
141
|
-
else
|
142
|
-
Configuration.deprecated(:try, 'instead use `candidate`', version: '0.7.0')
|
143
|
-
candidate(&block)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
protected
|
148
|
-
|
149
|
-
def cached_variant_resolver(provided_variant)
|
150
|
-
return :control if excluded?
|
100
|
+
def variant_names
|
101
|
+
Configuration.deprecated(
|
102
|
+
:variant_names,
|
103
|
+
'instead use `behavior.names`, which includes :control',
|
104
|
+
version: '0.8.0'
|
105
|
+
)
|
151
106
|
|
152
|
-
|
153
|
-
result.to_sym if result.present?
|
107
|
+
behaviors.keys - [:control]
|
154
108
|
end
|
155
109
|
end
|
156
110
|
end
|
@@ -47,14 +47,14 @@ module Gitlab
|
|
47
47
|
private
|
48
48
|
|
49
49
|
def build_behavior_callback(filters, variant, **options, &block)
|
50
|
-
if registered_behavior_callbacks[variant
|
50
|
+
if registered_behavior_callbacks[variant]
|
51
51
|
raise ExistingBehaviorError, "a behavior for the `#{variant}` variant has already been registered"
|
52
52
|
end
|
53
53
|
|
54
54
|
callback_behavior = "#{variant}_behavior".to_sym
|
55
55
|
|
56
56
|
# Register a the behavior so we can define the block later.
|
57
|
-
registered_behavior_callbacks[variant
|
57
|
+
registered_behavior_callbacks[variant] = callback_behavior
|
58
58
|
|
59
59
|
# Add our block or default behavior method.
|
60
60
|
filters.push(block) if block.present?
|
@@ -57,7 +57,7 @@ module Gitlab
|
|
57
57
|
#
|
58
58
|
# Included rollout strategies:
|
59
59
|
# :percent, (recommended), :round_robin, or :random
|
60
|
-
@default_rollout = Rollout.resolve(:percent
|
60
|
+
@default_rollout = Rollout.resolve(:percent)
|
61
61
|
|
62
62
|
# Secret seed used in generating context keys.
|
63
63
|
#
|
@@ -165,61 +165,6 @@ module Gitlab
|
|
165
165
|
end
|
166
166
|
|
167
167
|
class << self
|
168
|
-
# @deprecated
|
169
|
-
def context_hash_strategy=(block)
|
170
|
-
deprecated(
|
171
|
-
:context_hash_strategy,
|
172
|
-
'instead use `context_key_secret` and `context_key_bit_length`',
|
173
|
-
version: '0.7.0'
|
174
|
-
)
|
175
|
-
|
176
|
-
@__context_hash_strategy = block
|
177
|
-
end
|
178
|
-
|
179
|
-
# @deprecated
|
180
|
-
def variant_resolver
|
181
|
-
deprecated(
|
182
|
-
:variant_resolver,
|
183
|
-
'instead use `inclusion_resolver` with a block that returns a boolean',
|
184
|
-
version: '0.6.5'
|
185
|
-
)
|
186
|
-
|
187
|
-
@__inclusion_resolver
|
188
|
-
end
|
189
|
-
|
190
|
-
# @deprecated
|
191
|
-
def variant_resolver=(block)
|
192
|
-
deprecated(
|
193
|
-
:variant_resolver,
|
194
|
-
'instead use `inclusion_resolver` with a block that returns a boolean',
|
195
|
-
version: '0.6.5'
|
196
|
-
)
|
197
|
-
|
198
|
-
@__inclusion_resolver = block
|
199
|
-
end
|
200
|
-
|
201
|
-
# @deprecated
|
202
|
-
def inclusion_resolver=(block)
|
203
|
-
deprecated(
|
204
|
-
:inclusion_resolver,
|
205
|
-
'instead put this logic into custom rollout strategies',
|
206
|
-
version: '0.7.0'
|
207
|
-
)
|
208
|
-
|
209
|
-
@__inclusion_resolver = block
|
210
|
-
end
|
211
|
-
|
212
|
-
# @deprecated
|
213
|
-
def inclusion_resolver
|
214
|
-
deprecated(
|
215
|
-
:inclusion_resolver,
|
216
|
-
'instead put this logic into custom rollout strategies',
|
217
|
-
version: '0.7.0'
|
218
|
-
)
|
219
|
-
|
220
|
-
@__inclusion_resolver
|
221
|
-
end
|
222
|
-
|
223
168
|
attr_accessor(
|
224
169
|
:name_prefix,
|
225
170
|
:logger,
|
@@ -240,17 +185,7 @@ module Gitlab
|
|
240
185
|
# Attribute method overrides.
|
241
186
|
|
242
187
|
def default_rollout=(args) # rubocop:disable Lint/DuplicateMethods
|
243
|
-
|
244
|
-
if rollout.is_a?(Rollout::Base)
|
245
|
-
options = rollout.options
|
246
|
-
rollout = rollout.class
|
247
|
-
|
248
|
-
deprecated(<<~MESSAGE, version: '0.7.0')
|
249
|
-
using a rollout instance with `default_rollout` is deprecated and will be removed from {{release}} (instead use `default_rollout = #{rollout.name}, #{options.inspect}`)
|
250
|
-
MESSAGE
|
251
|
-
end
|
252
|
-
|
253
|
-
@default_rollout = Rollout.resolve(rollout, options || {})
|
188
|
+
@default_rollout = Rollout.resolve(*args)
|
254
189
|
end
|
255
190
|
|
256
191
|
# Internal warning helpers.
|
@@ -21,6 +21,7 @@ module Gitlab
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def include_dsl
|
24
|
+
Dsl.include_in(ActionController::API, with_helper: false) if defined?(ActionController)
|
24
25
|
Dsl.include_in(ActionController::Base, with_helper: true) if defined?(ActionController)
|
25
26
|
Dsl.include_in(ActionMailer::Base, with_helper: true) if defined?(ActionMailer)
|
26
27
|
end
|
@@ -36,7 +36,7 @@ module Gitlab
|
|
36
36
|
when nil then nil
|
37
37
|
when Array, Hash
|
38
38
|
if distribution_rules.length != behavior_names.length
|
39
|
-
raise InvalidRolloutRules, "the distribution rules don't match the number of
|
39
|
+
raise InvalidRolloutRules, "the distribution rules don't match the number of behaviors defined"
|
40
40
|
end
|
41
41
|
else
|
42
42
|
raise InvalidRolloutRules, 'unknown distribution options type'
|
@@ -8,6 +8,7 @@ module Gitlab
|
|
8
8
|
autoload :RoundRobin, 'gitlab/experiment/rollout/round_robin.rb'
|
9
9
|
|
10
10
|
def self.resolve(klass, options = {})
|
11
|
+
options ||= {}
|
11
12
|
case klass
|
12
13
|
when String
|
13
14
|
Strategy.new(klass.classify.constantize, options)
|
@@ -21,44 +22,29 @@ module Gitlab
|
|
21
22
|
end
|
22
23
|
|
23
24
|
class Base
|
24
|
-
DEFAULT_OPTIONS = {
|
25
|
-
include_control: false
|
26
|
-
}.freeze
|
27
|
-
|
28
25
|
attr_reader :experiment, :options
|
29
26
|
|
30
|
-
delegate :
|
31
|
-
|
32
|
-
def initialize(options = {})
|
33
|
-
@options = DEFAULT_OPTIONS.merge(options)
|
34
|
-
end
|
27
|
+
delegate :cache, :id, to: :experiment
|
35
28
|
|
36
|
-
def
|
29
|
+
def initialize(experiment, options = {})
|
37
30
|
raise ArgumentError, 'you must provide an experiment instance' unless experiment.class <= Gitlab::Experiment
|
38
31
|
|
39
32
|
@experiment = experiment
|
40
|
-
|
41
|
-
self
|
33
|
+
@options = options
|
42
34
|
end
|
43
35
|
|
44
36
|
def enabled?
|
45
|
-
require_experiment(__method__)
|
46
|
-
|
47
37
|
true
|
48
38
|
end
|
49
39
|
|
50
40
|
def resolve
|
51
|
-
require_experiment(__method__)
|
52
|
-
|
53
|
-
return nil if @experiment.respond_to?(:experiment_group?) && !@experiment.experiment_group?
|
54
|
-
|
55
41
|
validate! # allow the rollout strategy to validate itself
|
56
42
|
|
57
43
|
assignment = execute_assignment
|
58
|
-
assignment == :control ? nil : assignment # avoid caching control
|
44
|
+
assignment == :control ? nil : assignment # avoid caching control by returning nil
|
59
45
|
end
|
60
46
|
|
61
|
-
|
47
|
+
private
|
62
48
|
|
63
49
|
def validate!
|
64
50
|
# base is always valid
|
@@ -68,22 +54,14 @@ module Gitlab
|
|
68
54
|
behavior_names.first
|
69
55
|
end
|
70
56
|
|
71
|
-
private
|
72
|
-
|
73
|
-
def require_experiment(method_name)
|
74
|
-
return if @experiment.present?
|
75
|
-
|
76
|
-
raise ArgumentError, "you need to call `for` with an experiment instance before chaining `#{method_name}`"
|
77
|
-
end
|
78
|
-
|
79
57
|
def behavior_names
|
80
|
-
|
58
|
+
experiment.behaviors.keys
|
81
59
|
end
|
82
60
|
end
|
83
61
|
|
84
62
|
Strategy = Struct.new(:klass, :options) do
|
85
63
|
def for(experiment)
|
86
|
-
klass.new(options)
|
64
|
+
klass.new(experiment, options)
|
87
65
|
end
|
88
66
|
end
|
89
67
|
end
|
@@ -14,13 +14,13 @@ module Gitlab
|
|
14
14
|
def self.track_gitlab_experiment_receiver(method, receiver)
|
15
15
|
# Leverage the `>=` method on Gitlab::Experiment to determine if the receiver is an experiment, not the other
|
16
16
|
# way round -- `receiver.<=` could be mocked and we want to be extra careful.
|
17
|
-
(@__gitlab_experiment_receivers[method
|
17
|
+
(@__gitlab_experiment_receivers[method] ||= []) << receiver if Gitlab::Experiment >= receiver
|
18
18
|
rescue StandardError # again, let's just be extra careful
|
19
19
|
false
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.bind_gitlab_experiment_receiver(method)
|
23
|
-
method.unbind.bind(@__gitlab_experiment_receivers[method
|
23
|
+
method.unbind.bind(@__gitlab_experiment_receivers[method].pop)
|
24
24
|
end
|
25
25
|
|
26
26
|
module MethodDouble
|
@@ -28,6 +28,7 @@ module Gitlab
|
|
28
28
|
RSpecMocks.track_gitlab_experiment_receiver(original_method, receiver)
|
29
29
|
super
|
30
30
|
end
|
31
|
+
ruby2_keywords :proxy_method_invoked if respond_to?(:ruby2_keywords, true)
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
@@ -37,9 +38,6 @@ module Gitlab
|
|
37
38
|
wrapped_experiment(experiment, remock: true) do |instance, wrapped|
|
38
39
|
# Stub internal methods that will make it behave as we've instructed.
|
39
40
|
allow(instance).to receive(:enabled?) { wrapped.variant_name != false }
|
40
|
-
if instance.respond_to?(:experiment_group?, true)
|
41
|
-
allow(instance).to receive(:experiment_group?) { !(wrapped.variant_name == false) }
|
42
|
-
end
|
43
41
|
|
44
42
|
# Stub the variant resolution logic to handle true/false, and named variants.
|
45
43
|
allow(instance).to receive(:resolve_variant_name).and_wrap_original { |method|
|
@@ -72,8 +70,8 @@ module Gitlab
|
|
72
70
|
def wrapped_experiment_chain_for(klass)
|
73
71
|
@__wrapped_experiment_chains ||= {}
|
74
72
|
@__wrapped_experiment_chains[klass.name || klass.object_id] ||= begin
|
75
|
-
allow(klass).to receive(:new).and_wrap_original do |method, *args, &original_block|
|
76
|
-
RSpecMocks.bind_gitlab_experiment_receiver(method).call(*args).tap do |instance|
|
73
|
+
allow(klass).to receive(:new).and_wrap_original do |method, *args, **kwargs, &original_block|
|
74
|
+
RSpecMocks.bind_gitlab_experiment_receiver(method).call(*args, **kwargs).tap do |instance|
|
77
75
|
wrapped = @__wrapped_experiments[instance.instance_variable_get(:@_name)]
|
78
76
|
wrapped&.blocks&.each { |b| b.call(instance, wrapped) }
|
79
77
|
|
@@ -128,7 +126,7 @@ module Gitlab
|
|
128
126
|
match do |experiment|
|
129
127
|
@experiment = require_experiment(experiment, 'register_behavior')
|
130
128
|
|
131
|
-
block = @experiment.behaviors[behavior_name
|
129
|
+
block = @experiment.behaviors[behavior_name]
|
132
130
|
@return_expected = false unless block
|
133
131
|
|
134
132
|
if @return_expected
|
@@ -193,13 +191,13 @@ module Gitlab
|
|
193
191
|
@experiment.run_callbacks(:segmentation)
|
194
192
|
|
195
193
|
@actual_variant = @experiment.instance_variable_get(:@_assigned_variant_name)
|
196
|
-
@expected_variant ? @actual_variant
|
194
|
+
@expected_variant ? @actual_variant == @expected_variant : @actual_variant.present?
|
197
195
|
end
|
198
196
|
|
199
197
|
chain :into do |expected|
|
200
198
|
raise ArgumentError, 'variant name must be provided' if expected.blank?
|
201
199
|
|
202
|
-
@expected_variant = expected
|
200
|
+
@expected_variant = expected
|
203
201
|
end
|
204
202
|
|
205
203
|
failure_message do
|
@@ -239,7 +237,7 @@ module Gitlab
|
|
239
237
|
chain(:for) do |expected|
|
240
238
|
raise ArgumentError, 'variant name must be provided' if expected.blank?
|
241
239
|
|
242
|
-
@expected_variant = expected
|
240
|
+
@expected_variant = expected
|
243
241
|
end
|
244
242
|
|
245
243
|
chain(:with_context) do |expected|
|
@@ -303,17 +301,16 @@ RSpec.configure do |config|
|
|
303
301
|
config.include Gitlab::Experiment::RSpecHelpers
|
304
302
|
config.include Gitlab::Experiment::Dsl
|
305
303
|
|
306
|
-
|
307
|
-
|
304
|
+
config.before(:each) do |example|
|
305
|
+
if example.metadata[:experiment] == true || example.metadata[:type] == :experiment
|
306
|
+
RequestStore.clear!
|
308
307
|
|
309
|
-
|
310
|
-
|
308
|
+
if defined?(Gitlab::Experiment::TestBehaviors::TrackedStructure)
|
309
|
+
Gitlab::Experiment::TestBehaviors::TrackedStructure.reset!
|
310
|
+
end
|
311
311
|
end
|
312
312
|
end
|
313
313
|
|
314
|
-
config.before(:each, :experiment, &clear_cache)
|
315
|
-
config.before(:each, type: :experiment, &clear_cache)
|
316
|
-
|
317
314
|
config.include Gitlab::Experiment::RSpecMatchers, :experiment
|
318
315
|
config.include Gitlab::Experiment::RSpecMatchers, type: :experiment
|
319
316
|
|
data/lib/gitlab/experiment.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'request_store'
|
4
4
|
require 'active_support'
|
5
5
|
require 'active_support/core_ext/module/delegation'
|
6
|
+
require 'active_support/core_ext/object/blank'
|
6
7
|
require 'active_support/core_ext/string/inflections'
|
7
8
|
|
8
9
|
require 'gitlab/experiment/errors'
|
@@ -87,34 +88,15 @@ module Gitlab
|
|
87
88
|
variant(:control, &block)
|
88
89
|
end
|
89
90
|
|
90
|
-
def candidate(
|
91
|
-
|
92
|
-
Configuration.deprecated(<<~MESSAGE, version: '0.7.0')
|
93
|
-
passing name to `candidate` is deprecated and will be removed from {{release}} (instead use `variant(#{name.inspect})`)
|
94
|
-
MESSAGE
|
95
|
-
end
|
96
|
-
|
97
|
-
variant(name || :candidate, &block)
|
91
|
+
def candidate(&block)
|
92
|
+
variant(:candidate, &block)
|
98
93
|
end
|
99
94
|
|
100
|
-
def variant(name
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
return behaviors[name.to_s] = block
|
105
|
-
end
|
106
|
-
|
107
|
-
if name.present?
|
108
|
-
Configuration.deprecated(<<~MESSAGE, version: '0.7.0')
|
109
|
-
setting the variant using `variant` is deprecated and will be removed from {{release}} (instead use `assigned(#{name.inspect})`)
|
110
|
-
MESSAGE
|
111
|
-
else
|
112
|
-
Configuration.deprecated(<<~MESSAGE, version: '0.7.0')
|
113
|
-
getting the assigned variant using `variant` is deprecated and will be removed from {{release}} (instead use `assigned`)
|
114
|
-
MESSAGE
|
115
|
-
end
|
95
|
+
def variant(name, &block)
|
96
|
+
raise ArgumentError, 'name required' if name.blank?
|
97
|
+
raise ArgumentError, 'block required' unless block.present?
|
116
98
|
|
117
|
-
|
99
|
+
behaviors[name] = block
|
118
100
|
end
|
119
101
|
|
120
102
|
def context(value = nil)
|
@@ -126,9 +108,7 @@ module Gitlab
|
|
126
108
|
|
127
109
|
def assigned(value = nil)
|
128
110
|
@_assigned_variant_name = cache_variant(value) if value.present?
|
129
|
-
if @_assigned_variant_name || @_resolving_variant
|
130
|
-
return Variant.new(name: (@_assigned_variant_name || :unresolved).to_s)
|
131
|
-
end
|
111
|
+
return Variant.new(name: @_assigned_variant_name || :unresolved) if @_assigned_variant_name || @_resolving_variant
|
132
112
|
|
133
113
|
if enabled?
|
134
114
|
@_resolving_variant = true
|
@@ -137,7 +117,7 @@ module Gitlab
|
|
137
117
|
|
138
118
|
run_callbacks(segmentation_callback_chain) do
|
139
119
|
@_assigned_variant_name ||= :control
|
140
|
-
Variant.new(name: @_assigned_variant_name
|
120
|
+
Variant.new(name: @_assigned_variant_name)
|
141
121
|
end
|
142
122
|
ensure
|
143
123
|
@_resolving_variant = false
|
@@ -171,13 +151,6 @@ module Gitlab
|
|
171
151
|
instance_exec(action, tracking_context(event_args).try(:compact) || {}, &Configuration.tracking_behavior)
|
172
152
|
end
|
173
153
|
|
174
|
-
def process_redirect_url(url)
|
175
|
-
return unless Configuration.redirect_url_validator&.call(url)
|
176
|
-
|
177
|
-
track('visited', url: url)
|
178
|
-
url # return the url, which allows for mutation
|
179
|
-
end
|
180
|
-
|
181
154
|
def enabled?
|
182
155
|
rollout.enabled?
|
183
156
|
end
|
@@ -193,23 +166,11 @@ module Gitlab
|
|
193
166
|
end
|
194
167
|
|
195
168
|
def signature
|
196
|
-
{ variant: assigned.name, experiment: name }.merge(context.signature)
|
169
|
+
{ variant: assigned.name.to_s, experiment: name }.merge(context.signature)
|
197
170
|
end
|
198
171
|
|
199
|
-
def
|
200
|
-
|
201
|
-
if (block = Configuration.instance_variable_get(:@__context_hash_strategy))
|
202
|
-
return instance_exec(source, seed, &block)
|
203
|
-
end
|
204
|
-
|
205
|
-
return source if source.is_a?(String)
|
206
|
-
|
207
|
-
source = source.keys + source.values if source.is_a?(Hash)
|
208
|
-
|
209
|
-
ingredients = Array(source).map { |v| identify(v) }
|
210
|
-
ingredients.unshift(seed).unshift(Configuration.context_key_secret)
|
211
|
-
|
212
|
-
Digest::SHA2.new(Configuration.context_key_bit_length).hexdigest(ingredients.join('|'))
|
172
|
+
def behaviors
|
173
|
+
@_behaviors ||= registered_behavior_callbacks
|
213
174
|
end
|
214
175
|
|
215
176
|
protected
|
@@ -218,23 +179,15 @@ module Gitlab
|
|
218
179
|
(object.respond_to?(:to_global_id) ? object.to_global_id : object).to_s
|
219
180
|
end
|
220
181
|
|
182
|
+
def cached_variant_resolver(provided_variant)
|
183
|
+
return :control if excluded?
|
184
|
+
|
185
|
+
result = cache_variant(provided_variant) { resolve_variant_name }
|
186
|
+
result.to_sym if result.present?
|
187
|
+
end
|
188
|
+
|
221
189
|
def resolve_variant_name
|
222
|
-
|
223
|
-
# TODO: Remove - deprecated in release 0.7.0
|
224
|
-
Configuration.deprecated(:experiment_group?, <<~MESSAGE, version: '0.7.0')
|
225
|
-
instead put this logic into custom rollout strategies
|
226
|
-
MESSAGE
|
227
|
-
|
228
|
-
rollout.resolve if experiment_group?
|
229
|
-
elsif (block = Configuration.instance_variable_get(:@__inclusion_resolver))
|
230
|
-
# TODO: Remove - deprecated in release 0.7.0
|
231
|
-
rollout.resolve if instance_exec(@_assigned_variant_name, &block)
|
232
|
-
elsif (block = Configuration.instance_variable_get(:@__variant_resolver))
|
233
|
-
# TODO: Remove - deprecated in release 0.6.5
|
234
|
-
instance_exec(@_assigned_variant_name, &block)
|
235
|
-
else
|
236
|
-
rollout.resolve # this is the end result of all deprecations
|
237
|
-
end
|
190
|
+
rollout.resolve
|
238
191
|
end
|
239
192
|
|
240
193
|
def tracking_context(event_args)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-experiment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -38,68 +38,52 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.0'
|
41
|
-
description:
|
41
|
+
description:
|
42
42
|
email:
|
43
43
|
- gitlab_rubygems@gitlab.com
|
44
44
|
executables: []
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
|
-
-
|
49
|
-
-
|
50
|
-
- lib/generators/gitlab/experiment/
|
48
|
+
- LICENSE.txt
|
49
|
+
- README.md
|
50
|
+
- lib/generators/gitlab/experiment/USAGE
|
51
|
+
- lib/generators/gitlab/experiment/experiment_generator.rb
|
51
52
|
- lib/generators/gitlab/experiment/install/install_generator.rb
|
52
|
-
- lib/generators/gitlab/experiment/install/templates
|
53
|
+
- lib/generators/gitlab/experiment/install/templates/POST_INSTALL
|
53
54
|
- lib/generators/gitlab/experiment/install/templates/application_experiment.rb.tt
|
54
55
|
- lib/generators/gitlab/experiment/install/templates/initializer.rb.tt
|
55
|
-
- lib/generators/gitlab/experiment/install/templates/POST_INSTALL
|
56
|
-
- lib/generators/gitlab/experiment/USAGE
|
57
|
-
- lib/generators/gitlab/experiment/experiment_generator.rb
|
58
|
-
- lib/generators/gitlab/experiment/templates
|
59
56
|
- lib/generators/gitlab/experiment/templates/experiment.rb.tt
|
60
|
-
- lib/generators/test_unit
|
61
|
-
- lib/generators/test_unit/experiment
|
62
|
-
- lib/generators/test_unit/experiment/experiment_generator.rb
|
63
|
-
- lib/generators/test_unit/experiment/templates
|
64
|
-
- lib/generators/test_unit/experiment/templates/experiment_test.rb.tt
|
65
|
-
- lib/generators/rspec
|
66
|
-
- lib/generators/rspec/experiment
|
67
57
|
- lib/generators/rspec/experiment/experiment_generator.rb
|
68
|
-
- lib/generators/rspec/experiment/templates
|
69
58
|
- lib/generators/rspec/experiment/templates/experiment_spec.rb.tt
|
59
|
+
- lib/generators/test_unit/experiment/experiment_generator.rb
|
60
|
+
- lib/generators/test_unit/experiment/templates/experiment_test.rb.tt
|
70
61
|
- lib/gitlab/experiment.rb
|
71
|
-
- lib/gitlab/experiment
|
72
|
-
- lib/gitlab/experiment/
|
73
|
-
- lib/gitlab/experiment/middleware.rb
|
74
|
-
- lib/gitlab/experiment/test_behaviors
|
75
|
-
- lib/gitlab/experiment/test_behaviors/trackable.rb
|
76
|
-
- lib/gitlab/experiment/cache
|
62
|
+
- lib/gitlab/experiment/base_interface.rb
|
63
|
+
- lib/gitlab/experiment/cache.rb
|
77
64
|
- lib/gitlab/experiment/cache/redis_hash_store.rb
|
78
|
-
- lib/gitlab/experiment/errors.rb
|
79
65
|
- lib/gitlab/experiment/callbacks.rb
|
80
|
-
- lib/gitlab/experiment/
|
81
|
-
- lib/gitlab/experiment/base_interface.rb
|
82
|
-
- lib/gitlab/experiment/nestable.rb
|
66
|
+
- lib/gitlab/experiment/configuration.rb
|
83
67
|
- lib/gitlab/experiment/context.rb
|
68
|
+
- lib/gitlab/experiment/cookies.rb
|
69
|
+
- lib/gitlab/experiment/dsl.rb
|
84
70
|
- lib/gitlab/experiment/engine.rb
|
85
|
-
- lib/gitlab/experiment/
|
86
|
-
- lib/gitlab/experiment/
|
71
|
+
- lib/gitlab/experiment/errors.rb
|
72
|
+
- lib/gitlab/experiment/middleware.rb
|
73
|
+
- lib/gitlab/experiment/nestable.rb
|
74
|
+
- lib/gitlab/experiment/rollout.rb
|
75
|
+
- lib/gitlab/experiment/rollout/percent.rb
|
87
76
|
- lib/gitlab/experiment/rollout/random.rb
|
88
77
|
- lib/gitlab/experiment/rollout/round_robin.rb
|
89
|
-
- lib/gitlab/experiment/
|
90
|
-
- lib/gitlab/experiment/
|
91
|
-
- lib/gitlab/experiment/
|
78
|
+
- lib/gitlab/experiment/rspec.rb
|
79
|
+
- lib/gitlab/experiment/test_behaviors/trackable.rb
|
80
|
+
- lib/gitlab/experiment/variant.rb
|
92
81
|
- lib/gitlab/experiment/version.rb
|
93
|
-
- lib/gitlab/experiment/cookies.rb
|
94
|
-
- lib/gitlab/experiment/configuration.rb
|
95
|
-
- lib/gitlab/experiment/dsl.rb
|
96
|
-
- LICENSE.txt
|
97
|
-
- README.md
|
98
82
|
homepage: https://gitlab.com/gitlab-org/ruby/gems/gitlab-experiment
|
99
83
|
licenses:
|
100
84
|
- MIT
|
101
85
|
metadata: {}
|
102
|
-
post_install_message:
|
86
|
+
post_install_message:
|
103
87
|
rdoc_options: []
|
104
88
|
require_paths:
|
105
89
|
- lib
|
@@ -114,8 +98,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
98
|
- !ruby/object:Gem::Version
|
115
99
|
version: '0'
|
116
100
|
requirements: []
|
117
|
-
rubygems_version: 3.
|
118
|
-
signing_key:
|
101
|
+
rubygems_version: 3.4.7
|
102
|
+
signing_key:
|
119
103
|
specification_version: 4
|
120
104
|
summary: GitLab experimentation library.
|
121
105
|
test_files: []
|