Class: Gitlab::Experiment
- Inherits:
-
Object
- Object
- Gitlab::Experiment
show all
- Includes:
- Cache, Callbacks, Scientist::Experiment
- Defined in:
- lib/gitlab/experiment.rb,
lib/gitlab/experiment/dsl.rb,
lib/gitlab/experiment/cache.rb,
lib/gitlab/experiment/rspec.rb,
lib/gitlab/experiment/engine.rb,
lib/gitlab/experiment/context.rb,
lib/gitlab/experiment/cookies.rb,
lib/gitlab/experiment/variant.rb,
lib/gitlab/experiment/version.rb,
lib/gitlab/experiment/callbacks.rb,
lib/gitlab/experiment/configuration.rb,
lib/gitlab/experiment/cache/redis_hash_store.rb
Defined Under Namespace
Modules: Cache, Callbacks, Cookies, Dsl, RSpecHelpers, RSpecMatchers
Classes: Configuration, Context, Engine, Variant
Constant Summary
collapse
- VERSION =
'0.4.12'
Class Method Summary
collapse
Instance Method Summary
collapse
Methods included from Cache
#cache, #cache_key, #cache_variant
Constructor Details
#initialize(name = nil, variant_name = nil, **context) {|_self| ... } ⇒ Experiment
Returns a new instance of Experiment.
58
59
60
61
62
63
64
65
66
67
68
|
# File 'lib/gitlab/experiment.rb', line 58
def initialize(name = nil, variant_name = nil, **context)
raise ArgumentError, 'name is required' if name.blank? && self.class.base?
@name = self.class.experiment_name(name, suffix: false)
@context = Context.new(self, **context)
@variant_name = cache_variant(variant_name) { nil } if variant_name.present?
compare { false }
yield self if block_given?
end
|
Class Method Details
26
27
28
|
# File 'lib/gitlab/experiment.rb', line 26
def configure
yield Configuration
end
|
.experiment_name(name = nil, suffix: true, suffix_word: 'experiment') ⇒ Object
39
40
41
42
43
|
# File 'lib/gitlab/experiment.rb', line 39
def experiment_name(name = nil, suffix: true, suffix_word: 'experiment')
name = (name.presence || self.name).to_s.underscore.sub(%r{(?<char>[_/]|)#{suffix_word}$}, '')
name = "#{name}#{Regexp.last_match(:char) || '_'}#{suffix_word}"
suffix ? name : name.sub(/_#{suffix_word}$/, '')
end
|
.run(name = nil, variant_name = nil, **context, &block) ⇒ Object
30
31
32
33
34
35
36
37
|
# File 'lib/gitlab/experiment.rb', line 30
def run(name = nil, variant_name = nil, **context, &block)
raise ArgumentError, 'name is required' if name.nil? && base?
instance = constantize(name).new(name, variant_name, **context, &block)
return instance unless block
instance.context.frozen? ? instance.run : instance.tap(&:run)
end
|
Instance Method Details
#behaviors ⇒ Object
124
125
126
127
128
129
130
131
|
# File 'lib/gitlab/experiment.rb', line 124
def behaviors
@behaviors ||= public_methods.each_with_object(super) do |name, behaviors|
next unless name.end_with?('_behavior')
behavior_name = name.to_s.sub(/_behavior$/, '')
behaviors[behavior_name] ||= -> { send(name) } end
end
|
#context(value = nil) ⇒ Object
74
75
76
77
78
79
|
# File 'lib/gitlab/experiment.rb', line 74
def context(value = nil)
return @context if value.blank?
@context.value(value)
@context
end
|
#enabled? ⇒ Boolean
151
152
153
|
# File 'lib/gitlab/experiment.rb', line 151
def enabled?
true
end
|
#excluded? ⇒ Boolean
155
156
157
158
159
160
|
# File 'lib/gitlab/experiment.rb', line 155
def excluded?
return @excluded if defined?(@excluded)
@excluded = !@context.trackable? || !run_callbacks(:exclusion_check) { :not_excluded } end
|
#flipper_id ⇒ Object
147
148
149
|
# File 'lib/gitlab/experiment.rb', line 147
def flipper_id
"Experiment;#{id}"
end
|
#id ⇒ Object
Also known as:
session_id
142
143
144
|
# File 'lib/gitlab/experiment.rb', line 142
def id
"#{name}:#{key_for(context.value)}"
end
|
#inspect ⇒ Object
70
71
72
|
# File 'lib/gitlab/experiment.rb', line 70
def inspect
"#<#{self.class.name || 'AnonymousClass'}:#{format('0x%016X', __id__)} @name=#{name} @context=#{context.value}>"
end
|
#name ⇒ Object
116
117
118
|
# File 'lib/gitlab/experiment.rb', line 116
def name
[Configuration.name_prefix, @name].compact.join('_')
end
|
#publish(result) ⇒ Object
#run(variant_name = nil) ⇒ Object
102
103
104
|
# File 'lib/gitlab/experiment.rb', line 102
def run(variant_name = nil)
@result ||= super(variant(variant_name).name)
end
|
#should_track? ⇒ Boolean
162
163
164
|
# File 'lib/gitlab/experiment.rb', line 162
def should_track?
enabled? && !excluded?
end
|
#signature ⇒ Object
138
139
140
|
# File 'lib/gitlab/experiment.rb', line 138
def signature
{ variant: variant.name, experiment: name }.merge(context.signature)
end
|
#track(action, **event_args) ⇒ Object
110
111
112
113
114
|
# File 'lib/gitlab/experiment.rb', line 110
def track(action, **event_args)
return unless should_track?
instance_exec(action, event_args, &Configuration.tracking_behavior)
end
|
#try(name = nil, &block) ⇒ Object
133
134
135
136
|
# File 'lib/gitlab/experiment.rb', line 133
def try(name = nil, &block)
name = (name || 'candidate').to_s
behaviors[name] = block
end
|
#variant(value = nil) ⇒ Object
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
# File 'lib/gitlab/experiment.rb', line 81
def variant(value = nil)
@variant_name = cache_variant(value) if value.present?
return Variant.new(name: (@variant_name || :unresolved).to_s) if @variant_name || @resolving_variant
if enabled?
@variant_name ||= :control if excluded?
@resolving_variant = true
if (result = cache_variant(@variant_name) { resolve_variant_name }).present?
@variant_name = result.to_sym
end
end
run_callbacks(segmentation_callback_chain) do
@variant_name ||= :control
Variant.new(name: @variant_name.to_s)
end
ensure
@resolving_variant = false
end
|
#variant_names ⇒ Object
120
121
122
|
# File 'lib/gitlab/experiment.rb', line 120
def variant_names
@variant_names ||= behaviors.keys.map(&:to_sym) - [:control]
end
|