gitlab-experiment 0.6.4 → 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -1
- data/lib/gitlab/experiment/callbacks.rb +1 -0
- data/lib/gitlab/experiment/context.rb +2 -1
- data/lib/gitlab/experiment/errors.rb +1 -0
- data/lib/gitlab/experiment/nestable.rb +41 -0
- data/lib/gitlab/experiment/rspec.rb +8 -0
- data/lib/gitlab/experiment/test_behaviors/trackable.rb +69 -0
- data/lib/gitlab/experiment/version.rb +1 -1
- data/lib/gitlab/experiment.rb +7 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9651e4cc4941d879e7c006f5865ff71989babd942bfcc09a86eb5be89f17e38c
|
4
|
+
data.tar.gz: d23055b9c808ae680c05762d60a569306069578a8555f87daf816a717cef0c28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af40d50563c3938f089d415ebece34be76a79f8b1c13f7f5a4d6c62f035a7fe07147a4ab9ad475f00372d94be7b5c35f6c54ee2f866c3b0875748774cb04f1bb
|
7
|
+
data.tar.gz: fd4b8c69bc49351a7050d5ab6665c42855cf1d26b18e4a4e1983fbbeba2a8487b1523b6d1f824f0862d0fd16d425565eaf4ce179120a2e55bbbbeef53ae03a2f
|
data/README.md
CHANGED
@@ -47,7 +47,7 @@ We'll name our experiment `notification_toggle`. This name is prefixed based on
|
|
47
47
|
|
48
48
|
When you implement an experiment you'll need to provide a name, and a context. The name can show up in tracking calls, and potentially other aspects. The context determines the variant assigned, and should be consistent between calls. We'll discuss migrating context in later examples.
|
49
49
|
|
50
|
-
A context "key" represents the unique id of a context. It allows us to give the same experience between different calls to the experiment and can be used in caching.
|
50
|
+
A context "key" represents the unique id of a context. It allows us to give the same experience between different calls to the experiment and can be used in caching. This is how an experiment remains "sticky" to a given context.
|
51
51
|
|
52
52
|
Now in our experiment we're going to render one of two views: the control will be our current view, and the candidate will be the new toggle button with a confirmation flow.
|
53
53
|
|
@@ -74,6 +74,12 @@ experiment(:notification_toggle, actor: user) do |e|
|
|
74
74
|
end
|
75
75
|
```
|
76
76
|
|
77
|
+
You can specify what the experiment should be "sticky" to by providing a `:sticky_to` option. By default this will be the entire context provided, but this can be overridden manually if needed.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
experiment(:notification_toggle, actor: user, project: project, sticky_to: project) #...
|
81
|
+
```
|
82
|
+
|
77
83
|
Understanding how an experiment can change behavior is important in evaluating its performance.
|
78
84
|
|
79
85
|
To this end, we track events that are important by calling the same experiment elsewhere in code. By using the same context, you'll have consistent behavior and the ability to track events to it.
|
@@ -27,6 +27,7 @@ module Gitlab
|
|
27
27
|
|
28
28
|
value = value.dup # dup so we don't mutate
|
29
29
|
reinitialize(value.delete(:request))
|
30
|
+
key(value.delete(:sticky_to))
|
30
31
|
|
31
32
|
@value.merge!(process_migrations(value))
|
32
33
|
end
|
@@ -34,7 +35,7 @@ module Gitlab
|
|
34
35
|
def key(key = nil)
|
35
36
|
return @key || @experiment.key_for(value) if key.nil?
|
36
37
|
|
37
|
-
@key = key
|
38
|
+
@key = @experiment.key_for(key)
|
38
39
|
end
|
39
40
|
|
40
41
|
def trackable?
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
class Experiment
|
5
|
+
module Nestable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
set_callback :run, :around, :manage_nested_stack
|
10
|
+
end
|
11
|
+
|
12
|
+
def nest_experiment(other)
|
13
|
+
raise NestingError, "unable to nest the #{other.name} experiment within the #{name} experiment"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def manage_nested_stack
|
19
|
+
Stack.push(self)
|
20
|
+
yield
|
21
|
+
ensure
|
22
|
+
Stack.pop
|
23
|
+
end
|
24
|
+
|
25
|
+
class Stack
|
26
|
+
include Singleton
|
27
|
+
|
28
|
+
@stack = []
|
29
|
+
|
30
|
+
class << self
|
31
|
+
delegate :pop, :length, :size, :[], to: :@stack
|
32
|
+
|
33
|
+
def push(instance)
|
34
|
+
@stack.last&.nest_experiment(instance)
|
35
|
+
@stack.push(instance)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
module Gitlab
|
4
4
|
class Experiment
|
5
|
+
module TestBehaviors
|
6
|
+
autoload :Trackable, 'gitlab/experiment/test_behaviors/trackable.rb'
|
7
|
+
end
|
8
|
+
|
5
9
|
module RSpecHelpers
|
6
10
|
def stub_experiments(experiments, times = nil)
|
7
11
|
experiments.each { |experiment| wrapped_experiment(experiment, times) }
|
@@ -234,6 +238,10 @@ RSpec.configure do |config|
|
|
234
238
|
|
235
239
|
config.before(:each, :experiment) do
|
236
240
|
RequestStore.clear!
|
241
|
+
|
242
|
+
if defined?(Gitlab::Experiment::TestBehaviors::TrackedStructure)
|
243
|
+
Gitlab::Experiment::TestBehaviors::TrackedStructure.reset!
|
244
|
+
end
|
237
245
|
end
|
238
246
|
|
239
247
|
config.include Gitlab::Experiment::RSpecMatchers, :experiment
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
class Experiment
|
5
|
+
module TestBehaviors
|
6
|
+
module Trackable
|
7
|
+
private
|
8
|
+
|
9
|
+
def manage_nested_stack
|
10
|
+
TrackedStructure.push(self)
|
11
|
+
super
|
12
|
+
ensure
|
13
|
+
TrackedStructure.pop
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class TrackedStructure
|
18
|
+
include Singleton
|
19
|
+
|
20
|
+
# dependency tracking
|
21
|
+
@flat = {}
|
22
|
+
@stack = []
|
23
|
+
|
24
|
+
# structure tracking
|
25
|
+
@tree = { name: nil, count: 0, children: {} }
|
26
|
+
@node = @tree
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def reset!
|
30
|
+
# dependency tracking
|
31
|
+
@flat = {}
|
32
|
+
@stack = []
|
33
|
+
|
34
|
+
# structure tracking
|
35
|
+
@tree = { name: nil, count: 0, children: {} }
|
36
|
+
@node = @tree
|
37
|
+
end
|
38
|
+
|
39
|
+
def hierarchy
|
40
|
+
@tree[:children]
|
41
|
+
end
|
42
|
+
|
43
|
+
def dependencies
|
44
|
+
@flat
|
45
|
+
end
|
46
|
+
|
47
|
+
def push(instance)
|
48
|
+
# dependency tracking
|
49
|
+
@flat[instance.name] = ((@flat[instance.name] || []) + @stack.map(&:name)).uniq
|
50
|
+
@stack.push(instance)
|
51
|
+
|
52
|
+
# structure tracking
|
53
|
+
@last = @node
|
54
|
+
@node = @node[:children][instance.name] ||= { name: instance.name, count: 0, children: {} }
|
55
|
+
@node[:count] += 1
|
56
|
+
end
|
57
|
+
|
58
|
+
def pop
|
59
|
+
# dependency tracking
|
60
|
+
@stack.pop
|
61
|
+
|
62
|
+
# structure tracking
|
63
|
+
@node = @last
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/gitlab/experiment.rb
CHANGED
@@ -19,6 +19,7 @@ require 'gitlab/experiment/cookies'
|
|
19
19
|
require 'gitlab/experiment/context'
|
20
20
|
require 'gitlab/experiment/dsl'
|
21
21
|
require 'gitlab/experiment/middleware'
|
22
|
+
require 'gitlab/experiment/nestable'
|
22
23
|
require 'gitlab/experiment/variant'
|
23
24
|
require 'gitlab/experiment/version'
|
24
25
|
require 'gitlab/experiment/engine' if defined?(Rails::Engine)
|
@@ -28,6 +29,7 @@ module Gitlab
|
|
28
29
|
include BaseInterface
|
29
30
|
include Cache
|
30
31
|
include Callbacks
|
32
|
+
include Nestable
|
31
33
|
|
32
34
|
class << self
|
33
35
|
def default_rollout(rollout = nil, options = {})
|
@@ -103,7 +105,9 @@ module Gitlab
|
|
103
105
|
end
|
104
106
|
|
105
107
|
def run(variant_name = nil)
|
106
|
-
@result
|
108
|
+
return @result if context.frozen?
|
109
|
+
|
110
|
+
@result = run_callbacks(:run) { super(variant(variant_name).name) }
|
107
111
|
rescue Scientist::BehaviorMissing => e
|
108
112
|
raise Error, e
|
109
113
|
end
|
@@ -155,6 +159,8 @@ module Gitlab
|
|
155
159
|
return instance_exec(source, seed, &block)
|
156
160
|
end
|
157
161
|
|
162
|
+
return source if source.is_a?(String)
|
163
|
+
|
158
164
|
source = source.keys + source.values if source.is_a?(Hash)
|
159
165
|
|
160
166
|
ingredients = Array(source).map { |v| identify(v) }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab-experiment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitLab
|
@@ -91,12 +91,15 @@ files:
|
|
91
91
|
- lib/gitlab/experiment
|
92
92
|
- lib/gitlab/experiment/variant.rb
|
93
93
|
- lib/gitlab/experiment/middleware.rb
|
94
|
+
- lib/gitlab/experiment/test_behaviors
|
95
|
+
- lib/gitlab/experiment/test_behaviors/trackable.rb
|
94
96
|
- lib/gitlab/experiment/cache
|
95
97
|
- lib/gitlab/experiment/cache/redis_hash_store.rb
|
96
98
|
- lib/gitlab/experiment/errors.rb
|
97
99
|
- lib/gitlab/experiment/callbacks.rb
|
98
100
|
- lib/gitlab/experiment/rollout.rb
|
99
101
|
- lib/gitlab/experiment/base_interface.rb
|
102
|
+
- lib/gitlab/experiment/nestable.rb
|
100
103
|
- lib/gitlab/experiment/context.rb
|
101
104
|
- lib/gitlab/experiment/engine.rb
|
102
105
|
- lib/gitlab/experiment/rspec.rb
|
@@ -130,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
133
|
- !ruby/object:Gem::Version
|
131
134
|
version: '0'
|
132
135
|
requirements: []
|
133
|
-
rubygems_version: 3.1.
|
136
|
+
rubygems_version: 3.1.6
|
134
137
|
signing_key:
|
135
138
|
specification_version: 4
|
136
139
|
summary: GitLab experiment library built on top of scientist.
|