split 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -22
- data/Appraisals +5 -9
- data/{CHANGELOG.mdown → CHANGELOG.md} +17 -0
- data/Gemfile +1 -1
- data/LICENSE +2 -2
- data/{README.mdown → README.md} +3 -14
- data/gemfiles/4.0.gemfile +1 -1
- data/gemfiles/{3.1.gemfile → 4.1.gemfile} +1 -1
- data/lib/split/alternative.rb +1 -1
- data/lib/split/dashboard.rb +6 -6
- data/lib/split/experiment.rb +2 -19
- data/lib/split/experiment_catalog.rb +20 -11
- data/lib/split/helper.rb +36 -99
- data/lib/split/metric.rb +2 -2
- data/lib/split/persistence/redis_adapter.rb +4 -2
- data/lib/split/trial.rb +84 -22
- data/lib/split/version.rb +1 -1
- data/spec/algorithms/weighted_sample_spec.rb +3 -3
- data/spec/algorithms/whiplash_spec.rb +2 -2
- data/spec/alternative_spec.rb +1 -1
- data/spec/dashboard_spec.rb +3 -3
- data/spec/encapsulated_helper_spec.rb +2 -6
- data/spec/experiment_catalog_spec.rb +37 -0
- data/spec/experiment_spec.rb +26 -26
- data/spec/helper_spec.rb +55 -20
- data/spec/metric_spec.rb +5 -5
- data/spec/persistence/redis_adapter_spec.rb +9 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/trial_spec.rb +126 -29
- data/split.gemspec +3 -3
- metadata +28 -30
- data/changes.rtf +0 -14
- data/gemfiles/3.0.gemfile +0 -8
data/lib/split/metric.rb
CHANGED
@@ -17,7 +17,7 @@ module Split
|
|
17
17
|
experiment_names = metric.split(',')
|
18
18
|
|
19
19
|
experiments = experiment_names.collect do |experiment_name|
|
20
|
-
Split::
|
20
|
+
Split::ExperimentCatalog.find(experiment_name)
|
21
21
|
end
|
22
22
|
|
23
23
|
Split::Metric.new(:name => name, :experiments => experiments)
|
@@ -67,7 +67,7 @@ module Split
|
|
67
67
|
if metric
|
68
68
|
experiments << metric.experiments
|
69
69
|
end
|
70
|
-
experiment = Split::
|
70
|
+
experiment = Split::ExperimentCatalog.find(metric_name)
|
71
71
|
if experiment
|
72
72
|
experiments << experiment
|
73
73
|
end
|
@@ -5,8 +5,10 @@ module Split
|
|
5
5
|
|
6
6
|
attr_reader :redis_key
|
7
7
|
|
8
|
-
def initialize(context)
|
9
|
-
if
|
8
|
+
def initialize(context, key = nil)
|
9
|
+
if key
|
10
|
+
@redis_key = "#{self.class.config[:namespace]}:#{key}"
|
11
|
+
elsif lookup_by = self.class.config[:lookup_by]
|
10
12
|
if lookup_by.respond_to?(:call)
|
11
13
|
key_frag = lookup_by.call(context)
|
12
14
|
else
|
data/lib/split/trial.rb
CHANGED
@@ -1,49 +1,111 @@
|
|
1
1
|
module Split
|
2
2
|
class Trial
|
3
3
|
attr_accessor :experiment
|
4
|
-
attr_accessor :goals
|
5
4
|
|
6
5
|
def initialize(attrs = {})
|
7
|
-
self.experiment
|
8
|
-
self.alternative
|
9
|
-
|
6
|
+
self.experiment = attrs.delete(:experiment)
|
7
|
+
self.alternative = attrs.delete(:alternative)
|
8
|
+
|
9
|
+
@user = attrs.delete(:user)
|
10
|
+
@options = attrs
|
11
|
+
|
12
|
+
@alternative_choosen = false
|
10
13
|
end
|
11
14
|
|
12
15
|
def alternative
|
13
|
-
@alternative ||= if experiment.has_winner?
|
14
|
-
experiment.winner
|
16
|
+
@alternative ||= if @experiment.has_winner?
|
17
|
+
@experiment.winner
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
21
|
+
def alternative=(alternative)
|
22
|
+
@alternative = if alternative.kind_of?(Split::Alternative)
|
23
|
+
alternative
|
24
|
+
else
|
25
|
+
@experiment.alternatives.find{|a| a.name == alternative }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def complete!(goals, context = nil)
|
30
|
+
goals = goals || []
|
31
|
+
|
19
32
|
if alternative
|
20
|
-
if
|
33
|
+
if goals.empty?
|
21
34
|
alternative.increment_completion
|
22
35
|
else
|
23
|
-
|
36
|
+
goals.each {|g| alternative.increment_completion(g) }
|
24
37
|
end
|
38
|
+
|
39
|
+
context.send(Split.configuration.on_trial_complete, self) \
|
40
|
+
if Split.configuration.on_trial_complete && context
|
25
41
|
end
|
26
42
|
end
|
27
43
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
44
|
+
# Choose an alternative, add a participant, and save the alternative choice on the user. This
|
45
|
+
# method is guaranteed to only run once, and will skip the alternative choosing process if run
|
46
|
+
# a second time.
|
47
|
+
def choose!(context = nil)
|
48
|
+
# Only run the process once
|
49
|
+
return alternative if @alternative_choosen
|
32
50
|
|
33
|
-
|
34
|
-
|
35
|
-
|
51
|
+
if @options[:override]
|
52
|
+
self.alternative = @options[:override]
|
53
|
+
elsif @options[:disabled] || !Split.configuration.enabled
|
54
|
+
self.alternative = @experiment.control
|
55
|
+
elsif @experiment.has_winner?
|
56
|
+
self.alternative = @experiment.winner
|
57
|
+
else
|
58
|
+
cleanup_old_versions
|
59
|
+
|
60
|
+
if exclude_user?
|
61
|
+
self.alternative = @experiment.control
|
62
|
+
elsif @user[@experiment.key]
|
63
|
+
self.alternative = @user[@experiment.key]
|
64
|
+
else
|
65
|
+
self.alternative = @experiment.next_alternative
|
66
|
+
|
67
|
+
# Increment the number of participants since we are actually choosing a new alternative
|
68
|
+
self.alternative.increment_participation
|
36
69
|
|
37
|
-
|
38
|
-
|
70
|
+
# Run the post-choosing hook on the context
|
71
|
+
context.send(Split.configuration.on_trial_choose, self) \
|
72
|
+
if Split.configuration.on_trial_choose && context
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
@user[@experiment.key] = alternative.name if should_store_alternative?
|
77
|
+
@alternative_choosen = true
|
78
|
+
alternative
|
39
79
|
end
|
40
80
|
|
41
|
-
|
42
|
-
|
43
|
-
|
81
|
+
private
|
82
|
+
|
83
|
+
def should_store_alternative?
|
84
|
+
if @options[:override] || @options[:disabled]
|
85
|
+
Split.configuration.store_override
|
44
86
|
else
|
45
|
-
|
87
|
+
!exclude_user?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def cleanup_old_versions
|
92
|
+
if @experiment.version > 0
|
93
|
+
keys = @user.keys.select { |k| k.match(Regexp.new(@experiment.name)) }
|
94
|
+
keys_without_experiment(keys).each { |key| @user.delete(key) }
|
46
95
|
end
|
47
96
|
end
|
97
|
+
|
98
|
+
def exclude_user?
|
99
|
+
@options[:exclude] || @experiment.start_time.nil? || max_experiments_reached?
|
100
|
+
end
|
101
|
+
|
102
|
+
def max_experiments_reached?
|
103
|
+
!Split.configuration.allow_multiple_experiments &&
|
104
|
+
keys_without_experiment(@user.keys).length > 0
|
105
|
+
end
|
106
|
+
|
107
|
+
def keys_without_experiment(keys)
|
108
|
+
keys.reject { |k| k.match(Regexp.new("^#{@experiment.key}(:finished)?$")) }
|
109
|
+
end
|
48
110
|
end
|
49
111
|
end
|
data/lib/split/version.rb
CHANGED
@@ -2,17 +2,17 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Split::Algorithms::WeightedSample do
|
4
4
|
it "should return an alternative" do
|
5
|
-
experiment = Split::
|
5
|
+
experiment = Split::ExperimentCatalog.find_or_create('link_color', {'blue' => 100}, {'red' => 0 })
|
6
6
|
expect(Split::Algorithms::WeightedSample.choose_alternative(experiment).class).to eq(Split::Alternative)
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should always return a heavily weighted option" do
|
10
|
-
experiment = Split::
|
10
|
+
experiment = Split::ExperimentCatalog.find_or_create('link_color', {'blue' => 100}, {'red' => 0 })
|
11
11
|
expect(Split::Algorithms::WeightedSample.choose_alternative(experiment).name).to eq('blue')
|
12
12
|
end
|
13
13
|
|
14
14
|
it "should return one of the results" do
|
15
|
-
experiment = Split::
|
15
|
+
experiment = Split::ExperimentCatalog.find_or_create('link_color', {'blue' => 1}, {'red' => 1 })
|
16
16
|
expect(['red', 'blue']).to include Split::Algorithms::WeightedSample.choose_alternative(experiment).name
|
17
17
|
end
|
18
18
|
end
|
@@ -3,12 +3,12 @@ require "spec_helper"
|
|
3
3
|
describe Split::Algorithms::Whiplash do
|
4
4
|
|
5
5
|
it "should return an algorithm" do
|
6
|
-
experiment = Split::
|
6
|
+
experiment = Split::ExperimentCatalog.find_or_create('link_color', {'blue' => 1}, {'red' => 1 })
|
7
7
|
expect(Split::Algorithms::Whiplash.choose_alternative(experiment).class).to eq(Split::Alternative)
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should return one of the results" do
|
11
|
-
experiment = Split::
|
11
|
+
experiment = Split::ExperimentCatalog.find_or_create('link_color', {'blue' => 1}, {'red' => 1 })
|
12
12
|
expect(['red', 'blue']).to include Split::Algorithms::Whiplash.choose_alternative(experiment).name
|
13
13
|
end
|
14
14
|
|
data/spec/alternative_spec.rb
CHANGED
@@ -12,7 +12,7 @@ describe Split::Alternative do
|
|
12
12
|
}
|
13
13
|
|
14
14
|
let!(:experiment) {
|
15
|
-
Split::
|
15
|
+
Split::ExperimentCatalog.find_or_create({"basket_text" => ["purchase", "refund"]}, "Basket", "Cart")
|
16
16
|
}
|
17
17
|
|
18
18
|
let(:goal1) { "purchase" }
|
data/spec/dashboard_spec.rb
CHANGED
@@ -14,11 +14,11 @@ describe Split::Dashboard do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
let(:experiment) {
|
17
|
-
Split::
|
17
|
+
Split::ExperimentCatalog.find_or_create("link_color", "blue", "red")
|
18
18
|
}
|
19
19
|
|
20
20
|
let(:experiment_with_goals) {
|
21
|
-
Split::
|
21
|
+
Split::ExperimentCatalog.find_or_create({"link_color" => ["goal_1", "goal_2"]}, "blue", "red")
|
22
22
|
}
|
23
23
|
|
24
24
|
let(:metric) {
|
@@ -139,7 +139,7 @@ describe Split::Dashboard do
|
|
139
139
|
it "should delete an experiment" do
|
140
140
|
delete "/#{experiment.name}"
|
141
141
|
expect(last_response).to be_redirect
|
142
|
-
expect(Split::
|
142
|
+
expect(Split::ExperimentCatalog.find(experiment.name)).to be_nil
|
143
143
|
end
|
144
144
|
|
145
145
|
it "should mark an alternative as the winner" do
|
@@ -4,12 +4,8 @@ describe Split::EncapsulatedHelper do
|
|
4
4
|
include Split::EncapsulatedHelper
|
5
5
|
|
6
6
|
before do
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
after do
|
12
|
-
Split.configuration.persistence = @persistence_adapter
|
7
|
+
allow_any_instance_of(Split::EncapsulatedHelper::ContextShim).to receive(:ab_user)
|
8
|
+
.and_return({})
|
13
9
|
end
|
14
10
|
|
15
11
|
def params
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Split::ExperimentCatalog do
|
4
|
+
subject { Split::ExperimentCatalog }
|
5
|
+
|
6
|
+
describe ".find_or_create" do
|
7
|
+
it "should not raise an error when passed strings for alternatives" do
|
8
|
+
expect { subject.find_or_create('xyz', '1', '2', '3') }.not_to raise_error
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should not raise an error when passed an array for alternatives" do
|
12
|
+
expect { subject.find_or_create('xyz', ['1', '2', '3']) }.not_to raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should raise the appropriate error when passed integers for alternatives" do
|
16
|
+
expect { subject.find_or_create('xyz', 1, 2, 3) }.to raise_error
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should raise the appropriate error when passed symbols for alternatives" do
|
20
|
+
expect { subject.find_or_create('xyz', :a, :b, :c) }.to raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should not raise error when passed an array for goals" do
|
24
|
+
expect { subject.find_or_create({'link_color' => ["purchase", "refund"]}, 'blue', 'red') }
|
25
|
+
.not_to raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should not raise error when passed just one goal" do
|
29
|
+
expect { subject.find_or_create({'link_color' => "purchase"}, 'blue', 'red') }
|
30
|
+
.not_to raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
it "constructs a new experiment" do
|
34
|
+
expect(subject.find_or_create('my_exp', 'control me').control.to_s).to eq('control me')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/experiment_spec.rb
CHANGED
@@ -48,14 +48,14 @@ describe Split::Experiment do
|
|
48
48
|
expect(Time).to receive(:now).and_return(experiment_start_time)
|
49
49
|
experiment.save
|
50
50
|
|
51
|
-
expect(Split::
|
51
|
+
expect(Split::ExperimentCatalog.find('basket_text').start_time).to eq(experiment_start_time)
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should not save the start time to redis when start_manually is enabled" do
|
55
55
|
expect(Split.configuration).to receive(:start_manually).and_return(true)
|
56
56
|
experiment.save
|
57
57
|
|
58
|
-
expect(Split::
|
58
|
+
expect(Split::ExperimentCatalog.find('basket_text').start_time).to be_nil
|
59
59
|
end
|
60
60
|
|
61
61
|
it "should save the selected algorithm to redis" do
|
@@ -63,7 +63,7 @@ describe Split::Experiment do
|
|
63
63
|
experiment.algorithm = experiment_algorithm
|
64
64
|
experiment.save
|
65
65
|
|
66
|
-
expect(Split::
|
66
|
+
expect(Split::ExperimentCatalog.find('basket_text').algorithm).to eq(experiment_algorithm)
|
67
67
|
end
|
68
68
|
|
69
69
|
it "should handle having a start time stored as a string" do
|
@@ -72,7 +72,7 @@ describe Split::Experiment do
|
|
72
72
|
experiment.save
|
73
73
|
Split.redis.hset(:experiment_start_times, experiment.name, experiment_start_time)
|
74
74
|
|
75
|
-
expect(Split::
|
75
|
+
expect(Split::ExperimentCatalog.find('basket_text').start_time).to eq(experiment_start_time)
|
76
76
|
end
|
77
77
|
|
78
78
|
it "should handle not having a start time" do
|
@@ -82,7 +82,7 @@ describe Split::Experiment do
|
|
82
82
|
|
83
83
|
Split.redis.hdel(:experiment_start_times, experiment.name)
|
84
84
|
|
85
|
-
expect(Split::
|
85
|
+
expect(Split::ExperimentCatalog.find('basket_text').start_time).to be_nil
|
86
86
|
end
|
87
87
|
|
88
88
|
it "should not create duplicates when saving multiple times" do
|
@@ -106,12 +106,12 @@ describe Split::Experiment do
|
|
106
106
|
describe 'find' do
|
107
107
|
it "should return an existing experiment" do
|
108
108
|
experiment.save
|
109
|
-
experiment = Split::
|
109
|
+
experiment = Split::ExperimentCatalog.find('basket_text')
|
110
110
|
expect(experiment.name).to eq('basket_text')
|
111
111
|
end
|
112
112
|
|
113
113
|
it "should return an existing experiment" do
|
114
|
-
expect(Split::
|
114
|
+
expect(Split::ExperimentCatalog.find('non_existent_experiment')).to be_nil
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
@@ -141,7 +141,7 @@ describe Split::Experiment do
|
|
141
141
|
experiment = Split::Experiment.new('basket_text', :alternatives => ['Basket', "Cart"], :resettable => false)
|
142
142
|
experiment.save
|
143
143
|
|
144
|
-
e = Split::
|
144
|
+
e = Split::ExperimentCatalog.find('basket_text')
|
145
145
|
expect(e).to eq(experiment)
|
146
146
|
expect(e.resettable).to be_falsey
|
147
147
|
|
@@ -151,7 +151,7 @@ describe Split::Experiment do
|
|
151
151
|
experiment = Split::Experiment.new('basket_text', :alternatives => ['Basket', "Cart"], :algorithm => Split::Algorithms::Whiplash)
|
152
152
|
experiment.save
|
153
153
|
|
154
|
-
e = Split::
|
154
|
+
e = Split::ExperimentCatalog.find('basket_text')
|
155
155
|
expect(e).to eq(experiment)
|
156
156
|
expect(e.algorithm).to eq(Split::Algorithms::Whiplash)
|
157
157
|
end
|
@@ -160,7 +160,7 @@ describe Split::Experiment do
|
|
160
160
|
experiment = Split::Experiment.new('foobar', :alternatives => ['tra', 'la'], :algorithm => Split::Algorithms::Whiplash)
|
161
161
|
experiment.save
|
162
162
|
|
163
|
-
e = Split::
|
163
|
+
e = Split::ExperimentCatalog.find('foobar')
|
164
164
|
expect(e).to eq(experiment)
|
165
165
|
expect(e.alternatives.collect{|a| a.name}).to eq(['tra', 'la'])
|
166
166
|
end
|
@@ -173,7 +173,7 @@ describe Split::Experiment do
|
|
173
173
|
|
174
174
|
experiment.delete
|
175
175
|
expect(Split.redis.exists('link_color')).to be false
|
176
|
-
expect(Split::
|
176
|
+
expect(Split::ExperimentCatalog.find('link_color')).to be_nil
|
177
177
|
end
|
178
178
|
|
179
179
|
it "should increment the version" do
|
@@ -255,7 +255,7 @@ describe Split::Experiment do
|
|
255
255
|
end
|
256
256
|
|
257
257
|
describe 'algorithm' do
|
258
|
-
let(:experiment) { Split::
|
258
|
+
let(:experiment) { Split::ExperimentCatalog.find_or_create('link_color', 'blue', 'red', 'green') }
|
259
259
|
|
260
260
|
it 'should use the default algorithm if none is specified' do
|
261
261
|
expect(experiment.algorithm).to eq(Split.configuration.algorithm)
|
@@ -268,7 +268,7 @@ describe Split::Experiment do
|
|
268
268
|
end
|
269
269
|
|
270
270
|
describe 'next_alternative' do
|
271
|
-
let(:experiment) { Split::
|
271
|
+
let(:experiment) { Split::ExperimentCatalog.find_or_create('link_color', 'blue', 'red', 'green') }
|
272
272
|
|
273
273
|
it "should always return the winner if one exists" do
|
274
274
|
green = Split::Alternative.new('green', 'link_color')
|
@@ -288,7 +288,7 @@ describe Split::Experiment do
|
|
288
288
|
end
|
289
289
|
|
290
290
|
describe 'single alternative' do
|
291
|
-
let(:experiment) { Split::
|
291
|
+
let(:experiment) { Split::ExperimentCatalog.find_or_create('link_color', 'blue') }
|
292
292
|
|
293
293
|
it "should always return the color blue" do
|
294
294
|
expect(experiment.next_alternative.name).to eq('blue')
|
@@ -297,7 +297,7 @@ describe Split::Experiment do
|
|
297
297
|
|
298
298
|
describe 'changing an existing experiment' do
|
299
299
|
def same_but_different_alternative
|
300
|
-
Split::
|
300
|
+
Split::ExperimentCatalog.find_or_create('link_color', 'blue', 'yellow', 'orange')
|
301
301
|
end
|
302
302
|
|
303
303
|
it "should reset an experiment if it is loaded with different alternatives" do
|
@@ -320,14 +320,14 @@ describe Split::Experiment do
|
|
320
320
|
|
321
321
|
describe 'alternatives passed as non-strings' do
|
322
322
|
it "should throw an exception if an alternative is passed that is not a string" do
|
323
|
-
expect(lambda { Split::
|
324
|
-
expect(lambda { Split::
|
323
|
+
expect(lambda { Split::ExperimentCatalog.find_or_create('link_color', :blue, :red) }).to raise_error
|
324
|
+
expect(lambda { Split::ExperimentCatalog.find_or_create('link_enabled', true, false) }).to raise_error
|
325
325
|
end
|
326
326
|
end
|
327
327
|
|
328
328
|
describe 'specifying weights' do
|
329
329
|
let(:experiment_with_weight) {
|
330
|
-
Split::
|
330
|
+
Split::ExperimentCatalog.find_or_create('link_color', {'blue' => 1}, {'red' => 2 })
|
331
331
|
}
|
332
332
|
|
333
333
|
it "should work for a new experiment" do
|
@@ -347,18 +347,18 @@ describe Split::Experiment do
|
|
347
347
|
|
348
348
|
context "saving experiment" do
|
349
349
|
def same_but_different_goals
|
350
|
-
Split::
|
350
|
+
Split::ExperimentCatalog.find_or_create({'link_color' => ["purchase", "refund"]}, 'blue', 'red', 'green')
|
351
351
|
end
|
352
352
|
|
353
353
|
before { experiment.save }
|
354
354
|
|
355
355
|
it "can find existing experiment" do
|
356
|
-
expect(Split::
|
356
|
+
expect(Split::ExperimentCatalog.find("link_color").name).to eq("link_color")
|
357
357
|
end
|
358
358
|
|
359
359
|
it "should reset an experiment if it is loaded with different goals" do
|
360
360
|
same_experiment = same_but_different_goals
|
361
|
-
expect(Split::
|
361
|
+
expect(Split::ExperimentCatalog.find("link_color").goals).to eq(["purchase", "refund"])
|
362
362
|
end
|
363
363
|
|
364
364
|
end
|
@@ -369,9 +369,9 @@ describe Split::Experiment do
|
|
369
369
|
|
370
370
|
context "find or create experiment" do
|
371
371
|
it "should have correct goals" do
|
372
|
-
experiment = Split::
|
372
|
+
experiment = Split::ExperimentCatalog.find_or_create({'link_color3' => ["purchase", "refund"]}, 'blue', 'red', 'green')
|
373
373
|
expect(experiment.goals).to eq(["purchase", "refund"])
|
374
|
-
experiment = Split::
|
374
|
+
experiment = Split::ExperimentCatalog.find_or_create('link_color3', 'blue', 'red', 'green')
|
375
375
|
expect(experiment.goals).to eq([])
|
376
376
|
end
|
377
377
|
end
|
@@ -379,19 +379,19 @@ describe Split::Experiment do
|
|
379
379
|
|
380
380
|
describe "beta probability calculation" do
|
381
381
|
it "should return a hash with the probability of each alternative being the best" do
|
382
|
-
experiment = Split::
|
382
|
+
experiment = Split::ExperimentCatalog.find_or_create('mathematicians', 'bernoulli', 'poisson', 'lagrange')
|
383
383
|
experiment.calc_winning_alternatives
|
384
384
|
expect(experiment.alternative_probabilities).not_to be_nil
|
385
385
|
end
|
386
386
|
|
387
387
|
it "should return between 46% and 54% probability for an experiment with 2 alternatives and no data" do
|
388
|
-
experiment = Split::
|
388
|
+
experiment = Split::ExperimentCatalog.find_or_create('scientists', 'einstein', 'bohr')
|
389
389
|
experiment.calc_winning_alternatives
|
390
390
|
expect(experiment.alternatives[0].p_winner).to be_within(0.04).of(0.50)
|
391
391
|
end
|
392
392
|
|
393
393
|
it "should calculate the probability of being the winning alternative separately for each goal" do
|
394
|
-
experiment = Split::
|
394
|
+
experiment = Split::ExperimentCatalog.find_or_create({'link_color3' => ["purchase", "refund"]}, 'blue', 'red', 'green')
|
395
395
|
goal1 = experiment.goals[0]
|
396
396
|
goal2 = experiment.goals[1]
|
397
397
|
experiment.alternatives.each do |alternative|
|