split 0.3.0 → 0.3.1
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.
- data/CHANGELOG.mdown +13 -0
- data/lib/split/alternative.rb +31 -40
- data/lib/split/configuration.rb +1 -1
- data/lib/split/dashboard.rb +2 -2
- data/lib/split/experiment.rb +16 -18
- data/lib/split/helper.rb +4 -4
- data/lib/split/version.rb +1 -1
- data/spec/alternative_spec.rb +15 -17
- data/spec/dashboard_spec.rb +5 -8
- data/spec/experiment_spec.rb +17 -11
- data/spec/helper_spec.rb +34 -29
- data/split.gemspec +2 -1
- metadata +34 -17
data/CHANGELOG.mdown
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## 0.3.1 (November 19, 2011)
|
2
|
+
|
3
|
+
Features:
|
4
|
+
|
5
|
+
- General code tidy up (@ryanlecompte, #22, @mocoso, #28)
|
6
|
+
- Lazy loading data from Redis (@lautis, #25)
|
7
|
+
|
8
|
+
Bugfixes:
|
9
|
+
|
10
|
+
- Handle unstarted experiments (@mocoso, #27)
|
11
|
+
- Relaxed Sinatra version requirement (@martinclu, #24)
|
12
|
+
|
13
|
+
|
1
14
|
## 0.3.0 (October 9, 2011)
|
2
15
|
|
3
16
|
Features:
|
data/lib/split/alternative.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
module Split
|
2
2
|
class Alternative
|
3
3
|
attr_accessor :name
|
4
|
-
attr_accessor :participant_count
|
5
|
-
attr_accessor :completed_count
|
6
4
|
attr_accessor :experiment_name
|
7
5
|
attr_accessor :weight
|
8
6
|
|
9
|
-
def initialize(name, experiment_name
|
7
|
+
def initialize(name, experiment_name)
|
10
8
|
@experiment_name = experiment_name
|
11
|
-
|
12
|
-
@completed_count = counters['completed_count'].to_i
|
13
|
-
if name.class == Hash
|
9
|
+
if Hash === name
|
14
10
|
@name = name.keys.first
|
15
11
|
@weight = name.values.first
|
16
12
|
else
|
@@ -23,14 +19,28 @@ module Split
|
|
23
19
|
name
|
24
20
|
end
|
25
21
|
|
22
|
+
def participant_count
|
23
|
+
Split.redis.hget(key, 'participant_count').to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def participant_count=(count)
|
27
|
+
Split.redis.hset(key, 'participant_count', count.to_i)
|
28
|
+
end
|
29
|
+
|
30
|
+
def completed_count
|
31
|
+
Split.redis.hget(key, 'completed_count').to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
def completed_count=(count)
|
35
|
+
Split.redis.hset(key, 'completed_count', count.to_i)
|
36
|
+
end
|
37
|
+
|
26
38
|
def increment_participation
|
27
|
-
|
28
|
-
Split.redis.hincrby "#{experiment_name}:#{name}", 'participant_count', 1
|
39
|
+
Split.redis.hincrby key, 'participant_count', 1
|
29
40
|
end
|
30
41
|
|
31
42
|
def increment_completion
|
32
|
-
|
33
|
-
Split.redis.hincrby "#{experiment_name}:#{name}", 'completed_count', 1
|
43
|
+
Split.redis.hincrby key, 'completed_count', 1
|
34
44
|
end
|
35
45
|
|
36
46
|
def control?
|
@@ -72,49 +82,30 @@ module Split
|
|
72
82
|
end
|
73
83
|
|
74
84
|
def save
|
75
|
-
|
76
|
-
|
77
|
-
Split.redis.hset "#{experiment_name}:#{name}", 'completed_count', @completed_count
|
78
|
-
else
|
79
|
-
Split.redis.hmset "#{experiment_name}:#{name}", 'participant_count', 'completed_count', @participant_count, @completed_count
|
80
|
-
end
|
85
|
+
Split.redis.hsetnx key, 'participant_count', 0
|
86
|
+
Split.redis.hsetnx key, 'completed_count', 0
|
81
87
|
end
|
82
88
|
|
83
89
|
def reset
|
84
|
-
|
85
|
-
@completed_count = 0
|
86
|
-
save
|
90
|
+
Split.redis.hmset key, 'participant_count', 0, 'completed_count', 0
|
87
91
|
end
|
88
92
|
|
89
93
|
def delete
|
90
|
-
Split.redis.del(
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.find(name, experiment_name)
|
94
|
-
counters = Split.redis.hgetall "#{experiment_name}:#{name}"
|
95
|
-
self.new(name, experiment_name, counters)
|
96
|
-
end
|
97
|
-
|
98
|
-
def self.find_or_create(name, experiment_name)
|
99
|
-
self.find(name, experiment_name) || self.create(name, experiment_name)
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.create(name, experiment_name)
|
103
|
-
alt = self.new(name, experiment_name)
|
104
|
-
alt.save
|
105
|
-
alt
|
94
|
+
Split.redis.del(key)
|
106
95
|
end
|
107
96
|
|
108
97
|
def self.valid?(name)
|
109
|
-
|
98
|
+
String === name || hash_with_correct_values?(name)
|
110
99
|
end
|
111
100
|
|
112
|
-
def self.
|
113
|
-
name.
|
101
|
+
def self.hash_with_correct_values?(name)
|
102
|
+
Hash === name && String === name.keys.first && Float(name.values.first) rescue false
|
114
103
|
end
|
115
104
|
|
116
|
-
|
117
|
-
|
105
|
+
private
|
106
|
+
|
107
|
+
def key
|
108
|
+
"#{experiment_name}:#{name}"
|
118
109
|
end
|
119
110
|
end
|
120
111
|
end
|
data/lib/split/configuration.rb
CHANGED
@@ -3,7 +3,7 @@ module Split
|
|
3
3
|
attr_accessor :robot_regex
|
4
4
|
attr_accessor :ignore_ip_addresses
|
5
5
|
|
6
|
-
def initialize
|
6
|
+
def initialize
|
7
7
|
@robot_regex = /\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i
|
8
8
|
@ignore_ip_addresses = []
|
9
9
|
end
|
data/lib/split/dashboard.rb
CHANGED
@@ -7,7 +7,7 @@ module Split
|
|
7
7
|
dir = File.dirname(File.expand_path(__FILE__))
|
8
8
|
|
9
9
|
set :views, "#{dir}/dashboard/views"
|
10
|
-
set :
|
10
|
+
set :public_folder, "#{dir}/dashboard/public"
|
11
11
|
set :static, true
|
12
12
|
set :method_override, true
|
13
13
|
|
@@ -63,7 +63,7 @@ module Split
|
|
63
63
|
|
64
64
|
post '/:experiment' do
|
65
65
|
@experiment = Split::Experiment.find(params[:experiment])
|
66
|
-
@alternative = Split::Alternative.
|
66
|
+
@alternative = Split::Alternative.new(params[:alternative], params[:experiment])
|
67
67
|
@experiment.winner = @alternative.name
|
68
68
|
redirect url('/')
|
69
69
|
end
|
data/lib/split/experiment.rb
CHANGED
@@ -1,22 +1,18 @@
|
|
1
1
|
module Split
|
2
2
|
class Experiment
|
3
3
|
attr_accessor :name
|
4
|
-
attr_accessor :alternative_names
|
5
4
|
attr_accessor :winner
|
6
|
-
attr_accessor :version
|
7
5
|
|
8
6
|
def initialize(name, *alternative_names)
|
9
7
|
@name = name.to_s
|
10
|
-
@
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@version = (Split.redis.get("#{name.to_s}:version").to_i || 0)
|
8
|
+
@alternatives = alternative_names.map do |alternative|
|
9
|
+
Split::Alternative.new(alternative, name)
|
10
|
+
end
|
15
11
|
end
|
16
12
|
|
17
13
|
def winner
|
18
14
|
if w = Split.redis.hget(:experiment_winner, name)
|
19
|
-
|
15
|
+
Split::Alternative.new(w, name)
|
20
16
|
else
|
21
17
|
nil
|
22
18
|
end
|
@@ -30,12 +26,17 @@ module Split
|
|
30
26
|
Split.redis.hdel(:experiment_winner, name)
|
31
27
|
end
|
32
28
|
|
29
|
+
|
33
30
|
def winner=(winner_name)
|
34
31
|
Split.redis.hset(:experiment_winner, name, winner_name.to_s)
|
35
32
|
end
|
36
33
|
|
37
34
|
def alternatives
|
38
|
-
@
|
35
|
+
@alternatives.dup
|
36
|
+
end
|
37
|
+
|
38
|
+
def alternative_names
|
39
|
+
@alternatives.map(&:name)
|
39
40
|
end
|
40
41
|
|
41
42
|
def next_alternative
|
@@ -45,7 +46,7 @@ module Split
|
|
45
46
|
def random_alternative
|
46
47
|
weights = alternatives.map(&:weight)
|
47
48
|
|
48
|
-
total = weights.inject(
|
49
|
+
total = weights.inject(:+)
|
49
50
|
point = rand * total
|
50
51
|
|
51
52
|
alternatives.zip(weights).each do |n,w|
|
@@ -55,12 +56,11 @@ module Split
|
|
55
56
|
end
|
56
57
|
|
57
58
|
def version
|
58
|
-
@version ||= 0
|
59
|
+
@version ||= (Split.redis.get("#{name.to_s}:version").to_i || 0)
|
59
60
|
end
|
60
61
|
|
61
62
|
def increment_version
|
62
|
-
@version
|
63
|
-
Split.redis.set("#{name}:version", @version)
|
63
|
+
@version = Split.redis.incr("#{name}:version")
|
64
64
|
end
|
65
65
|
|
66
66
|
def key
|
@@ -92,7 +92,7 @@ module Split
|
|
92
92
|
def save
|
93
93
|
if new_record?
|
94
94
|
Split.redis.sadd(:experiments, name)
|
95
|
-
@
|
95
|
+
@alternatives.reverse.each {|a| Split.redis.lpush(name, a.name) }
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
@@ -115,8 +115,6 @@ module Split
|
|
115
115
|
def self.find(name)
|
116
116
|
if Split.redis.exists(name)
|
117
117
|
self.new(name, *load_alternatives_for(name))
|
118
|
-
else
|
119
|
-
raise 'Experiment not found'
|
120
118
|
end
|
121
119
|
end
|
122
120
|
|
@@ -148,12 +146,12 @@ module Split
|
|
148
146
|
experiment.save
|
149
147
|
end
|
150
148
|
return experiment
|
151
|
-
|
149
|
+
|
152
150
|
end
|
153
151
|
|
154
152
|
def self.initialize_alternatives(alternatives, name)
|
155
153
|
|
156
|
-
|
154
|
+
unless alternatives.all? { |a| Split::Alternative.valid?(a) }
|
157
155
|
raise InvalidArgument, 'Alternatives must be strings'
|
158
156
|
end
|
159
157
|
|
data/lib/split/helper.rb
CHANGED
@@ -27,7 +27,7 @@ module Split
|
|
27
27
|
concat(capture(ret, &block))
|
28
28
|
false
|
29
29
|
else
|
30
|
-
|
30
|
+
yield(ret)
|
31
31
|
end
|
32
32
|
else
|
33
33
|
ret
|
@@ -36,16 +36,16 @@ module Split
|
|
36
36
|
|
37
37
|
def finished(experiment_name, options = {:reset => true})
|
38
38
|
return if exclude_visitor?
|
39
|
-
experiment = Split::Experiment.find(experiment_name)
|
39
|
+
return unless (experiment = Split::Experiment.find(experiment_name))
|
40
40
|
if alternative_name = ab_user[experiment.key]
|
41
|
-
alternative = Split::Alternative.
|
41
|
+
alternative = Split::Alternative.new(alternative_name, experiment_name)
|
42
42
|
alternative.increment_completion
|
43
43
|
session[:split].delete(experiment_name) if options[:reset]
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
47
|
def override(experiment_name, alternatives)
|
48
|
-
|
48
|
+
params[experiment_name] if defined?(params) && alternatives.include?(params[experiment_name])
|
49
49
|
end
|
50
50
|
|
51
51
|
def begin_experiment(experiment, alternative_name)
|
data/lib/split/version.rb
CHANGED
data/spec/alternative_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe Split::Alternative do
|
|
9
9
|
alternative = Split::Alternative.new('Basket', 'basket_text')
|
10
10
|
alternative.name.should eql('Basket')
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
it "return only the name" do
|
14
14
|
experiment = Split::Experiment.new('basket_text', {'Basket' => 0.6}, {"Cart" => 0.4})
|
15
15
|
alternative = Split::Alternative.new('Basket', 'basket_text')
|
@@ -29,7 +29,7 @@ describe Split::Alternative do
|
|
29
29
|
it "should belong to an experiment" do
|
30
30
|
experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
|
31
31
|
experiment.save
|
32
|
-
alternative = Split::Alternative.
|
32
|
+
alternative = Split::Alternative.new('Basket', 'basket_text')
|
33
33
|
alternative.experiment.name.should eql(experiment.name)
|
34
34
|
end
|
35
35
|
|
@@ -42,28 +42,29 @@ describe Split::Alternative do
|
|
42
42
|
it "should increment participation count" do
|
43
43
|
experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
|
44
44
|
experiment.save
|
45
|
-
alternative = Split::Alternative.
|
45
|
+
alternative = Split::Alternative.new('Basket', 'basket_text')
|
46
46
|
old_participant_count = alternative.participant_count
|
47
47
|
alternative.increment_participation
|
48
48
|
alternative.participant_count.should eql(old_participant_count+1)
|
49
49
|
|
50
|
-
Split::Alternative.
|
50
|
+
Split::Alternative.new('Basket', 'basket_text').participant_count.should eql(old_participant_count+1)
|
51
51
|
end
|
52
52
|
|
53
53
|
it "should increment completed count" do
|
54
54
|
experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
|
55
55
|
experiment.save
|
56
|
-
alternative = Split::Alternative.
|
56
|
+
alternative = Split::Alternative.new('Basket', 'basket_text')
|
57
57
|
old_completed_count = alternative.participant_count
|
58
58
|
alternative.increment_completion
|
59
59
|
alternative.completed_count.should eql(old_completed_count+1)
|
60
60
|
|
61
|
-
Split::Alternative.
|
61
|
+
Split::Alternative.new('Basket', 'basket_text').completed_count.should eql(old_completed_count+1)
|
62
62
|
end
|
63
63
|
|
64
64
|
it "can be reset" do
|
65
|
-
alternative = Split::Alternative.new('Basket', 'basket_text'
|
66
|
-
alternative.
|
65
|
+
alternative = Split::Alternative.new('Basket', 'basket_text')
|
66
|
+
alternative.participant_count = 10
|
67
|
+
alternative.completed_count = 4
|
67
68
|
alternative.reset
|
68
69
|
alternative.participant_count.should eql(0)
|
69
70
|
alternative.completed_count.should eql(0)
|
@@ -72,9 +73,9 @@ describe Split::Alternative do
|
|
72
73
|
it "should know if it is the control of an experiment" do
|
73
74
|
experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
|
74
75
|
experiment.save
|
75
|
-
alternative = Split::Alternative.
|
76
|
+
alternative = Split::Alternative.new('Basket', 'basket_text')
|
76
77
|
alternative.control?.should be_true
|
77
|
-
alternative = Split::Alternative.
|
78
|
+
alternative = Split::Alternative.new('Cart', 'basket_text')
|
78
79
|
alternative.control?.should be_false
|
79
80
|
end
|
80
81
|
|
@@ -86,21 +87,18 @@ describe Split::Alternative do
|
|
86
87
|
end
|
87
88
|
|
88
89
|
it "does something" do
|
89
|
-
alternative = Split::Alternative.new('Basket', 'basket_text'
|
90
|
+
alternative = Split::Alternative.new('Basket', 'basket_text')
|
91
|
+
alternative.stub(:participant_count).and_return(10)
|
92
|
+
alternative.stub(:completed_count).and_return(4)
|
90
93
|
alternative.conversion_rate.should eql(0.4)
|
91
94
|
end
|
92
95
|
end
|
93
96
|
|
94
|
-
it "should return an existing alternative" do
|
95
|
-
alternative = Split::Alternative.create('Basket', 'basket_text')
|
96
|
-
Split::Alternative.find('Basket', 'basket_text').name.should eql('Basket')
|
97
|
-
end
|
98
|
-
|
99
97
|
describe 'z score' do
|
100
98
|
it 'should be zero when the control has no conversions' do
|
101
99
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
102
100
|
|
103
|
-
alternative = Split::Alternative.
|
101
|
+
alternative = Split::Alternative.new('red', 'link_color')
|
104
102
|
alternative.z_score.should eql(0)
|
105
103
|
end
|
106
104
|
|
data/spec/dashboard_spec.rb
CHANGED
@@ -18,21 +18,18 @@ describe Split::Dashboard do
|
|
18
18
|
|
19
19
|
it "should reset an experiment" do
|
20
20
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
21
|
-
red = Split::Alternative.find('red', 'link_color').participant_count
|
22
21
|
|
23
|
-
red = Split::Alternative.
|
24
|
-
blue = Split::Alternative.
|
22
|
+
red = Split::Alternative.new('red', 'link_color')
|
23
|
+
blue = Split::Alternative.new('blue', 'link_color')
|
25
24
|
red.participant_count = 5
|
26
|
-
red.save
|
27
25
|
blue.participant_count = 6
|
28
|
-
blue.save
|
29
26
|
|
30
27
|
post '/reset/link_color'
|
31
28
|
|
32
29
|
last_response.should be_redirect
|
33
30
|
|
34
|
-
new_red_count = Split::Alternative.
|
35
|
-
new_blue_count = Split::Alternative.
|
31
|
+
new_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
32
|
+
new_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
36
33
|
|
37
34
|
new_blue_count.should eql(0)
|
38
35
|
new_red_count.should eql(0)
|
@@ -42,7 +39,7 @@ describe Split::Dashboard do
|
|
42
39
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
43
40
|
delete '/link_color'
|
44
41
|
last_response.should be_redirect
|
45
|
-
|
42
|
+
Split::Experiment.find('link_color').should be_nil
|
46
43
|
end
|
47
44
|
|
48
45
|
it "should mark an alternative as the winner" do
|
data/spec/experiment_spec.rb
CHANGED
@@ -35,7 +35,7 @@ describe Split::Experiment do
|
|
35
35
|
|
36
36
|
experiment.delete
|
37
37
|
Split.redis.exists('basket_text').should be false
|
38
|
-
|
38
|
+
Split::Experiment.find('link_color').should be_nil
|
39
39
|
end
|
40
40
|
|
41
41
|
it "should increment the version" do
|
@@ -59,10 +59,16 @@ describe Split::Experiment do
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
describe 'find' do
|
63
|
+
it "should return an existing experiment" do
|
64
|
+
experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
|
65
|
+
experiment.save
|
66
|
+
Split::Experiment.find('basket_text').name.should eql('basket_text')
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should return an existing experiment" do
|
70
|
+
Split::Experiment.find('non_existent_experiment').should be_nil
|
71
|
+
end
|
66
72
|
end
|
67
73
|
|
68
74
|
describe 'control' do
|
@@ -91,7 +97,7 @@ describe Split::Experiment do
|
|
91
97
|
describe 'reset' do
|
92
98
|
it 'should reset all alternatives' do
|
93
99
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
|
94
|
-
green = Split::Alternative.
|
100
|
+
green = Split::Alternative.new('green', 'link_color')
|
95
101
|
experiment.winner = 'green'
|
96
102
|
|
97
103
|
experiment.next_alternative.name.should eql('green')
|
@@ -99,14 +105,14 @@ describe Split::Experiment do
|
|
99
105
|
|
100
106
|
experiment.reset
|
101
107
|
|
102
|
-
reset_green = Split::Alternative.
|
108
|
+
reset_green = Split::Alternative.new('green', 'link_color')
|
103
109
|
reset_green.participant_count.should eql(0)
|
104
110
|
reset_green.completed_count.should eql(0)
|
105
111
|
end
|
106
112
|
|
107
113
|
it 'should reset the winner' do
|
108
114
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
|
109
|
-
green = Split::Alternative.
|
115
|
+
green = Split::Alternative.new('green', 'link_color')
|
110
116
|
experiment.winner = 'green'
|
111
117
|
|
112
118
|
experiment.next_alternative.name.should eql('green')
|
@@ -128,7 +134,7 @@ describe Split::Experiment do
|
|
128
134
|
describe 'next_alternative' do
|
129
135
|
it "should always return the winner if one exists" do
|
130
136
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
|
131
|
-
green = Split::Alternative.
|
137
|
+
green = Split::Alternative.new('green', 'link_color')
|
132
138
|
experiment.winner = 'green'
|
133
139
|
|
134
140
|
experiment.next_alternative.name.should eql('green')
|
@@ -142,12 +148,12 @@ describe Split::Experiment do
|
|
142
148
|
describe 'changing an existing experiment' do
|
143
149
|
it "should reset an experiment if it is loaded with different alternatives" do
|
144
150
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
|
145
|
-
blue = Split::Alternative.
|
151
|
+
blue = Split::Alternative.new('blue', 'link_color')
|
146
152
|
blue.participant_count = 5
|
147
153
|
blue.save
|
148
154
|
same_experiment = Split::Experiment.find_or_create('link_color', 'blue', 'yellow', 'orange')
|
149
155
|
same_experiment.alternatives.map(&:name).should eql(['blue', 'yellow', 'orange'])
|
150
|
-
new_blue = Split::Alternative.
|
156
|
+
new_blue = Split::Alternative.new('blue', 'link_color')
|
151
157
|
new_blue.participant_count.should eql(0)
|
152
158
|
end
|
153
159
|
end
|
data/spec/helper_spec.rb
CHANGED
@@ -20,13 +20,13 @@ describe Split::Helper do
|
|
20
20
|
it "should increment the participation counter after assignment to a new user" do
|
21
21
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
22
22
|
|
23
|
-
previous_red_count = Split::Alternative.
|
24
|
-
previous_blue_count = Split::Alternative.
|
23
|
+
previous_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
24
|
+
previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
25
25
|
|
26
26
|
ab_test('link_color', 'blue', 'red')
|
27
27
|
|
28
|
-
new_red_count = Split::Alternative.
|
29
|
-
new_blue_count = Split::Alternative.
|
28
|
+
new_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
29
|
+
new_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
30
30
|
|
31
31
|
(new_red_count + new_blue_count).should eql(previous_red_count + previous_blue_count + 1)
|
32
32
|
end
|
@@ -57,12 +57,12 @@ describe Split::Helper do
|
|
57
57
|
ret = ab_test('link_color', 'blue', 'red') { |alternative| "shared/#{alternative}" }
|
58
58
|
ret.should eql("shared/#{alt}")
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
it "should allow the share of visitors see an alternative to be specificed" do
|
62
62
|
ab_test('link_color', {'blue' => 0.8}, {'red' => 20})
|
63
63
|
['red', 'blue'].should include(ab_user['link_color'])
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
it "should allow alternative weighting interface as a single hash" do
|
67
67
|
ab_test('link_color', 'blue' => 0.01, 'red' => 0.2)
|
68
68
|
experiment = Split::Experiment.find('link_color')
|
@@ -75,11 +75,11 @@ describe Split::Helper do
|
|
75
75
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
76
76
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
77
77
|
|
78
|
-
previous_completion_count = Split::Alternative.
|
78
|
+
previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
79
79
|
|
80
80
|
finished('link_color')
|
81
81
|
|
82
|
-
new_completion_count = Split::Alternative.
|
82
|
+
new_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
83
83
|
|
84
84
|
new_completion_count.should eql(previous_completion_count + 1)
|
85
85
|
end
|
@@ -88,7 +88,7 @@ describe Split::Helper do
|
|
88
88
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
89
89
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
90
90
|
|
91
|
-
previous_completion_count = Split::Alternative.
|
91
|
+
previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
92
92
|
|
93
93
|
session[:split].should eql("link_color" => alternative_name)
|
94
94
|
finished('link_color')
|
@@ -99,12 +99,17 @@ describe Split::Helper do
|
|
99
99
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
100
100
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
101
101
|
|
102
|
-
previous_completion_count = Split::Alternative.
|
102
|
+
previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
103
103
|
|
104
104
|
session[:split].should eql("link_color" => alternative_name)
|
105
105
|
finished('link_color', :reset => false)
|
106
106
|
session[:split].should eql("link_color" => alternative_name)
|
107
107
|
end
|
108
|
+
|
109
|
+
it "should do nothing where the experiment was not started by this user" do
|
110
|
+
session[:split] = nil
|
111
|
+
lambda { finished('some_experiment_not_started_by_the_user') }.should_not raise_exception
|
112
|
+
end
|
108
113
|
end
|
109
114
|
|
110
115
|
describe 'conversions' do
|
@@ -112,12 +117,12 @@ describe Split::Helper do
|
|
112
117
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
113
118
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
114
119
|
|
115
|
-
previous_convertion_rate = Split::Alternative.
|
120
|
+
previous_convertion_rate = Split::Alternative.new(alternative_name, 'link_color').conversion_rate
|
116
121
|
previous_convertion_rate.should eql(0.0)
|
117
122
|
|
118
123
|
finished('link_color')
|
119
124
|
|
120
|
-
new_convertion_rate = Split::Alternative.
|
125
|
+
new_convertion_rate = Split::Alternative.new(alternative_name, 'link_color').conversion_rate
|
121
126
|
new_convertion_rate.should eql(1.0)
|
122
127
|
end
|
123
128
|
end
|
@@ -137,13 +142,13 @@ describe Split::Helper do
|
|
137
142
|
it "should not increment the participation count" do
|
138
143
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
139
144
|
|
140
|
-
previous_red_count = Split::Alternative.
|
141
|
-
previous_blue_count = Split::Alternative.
|
145
|
+
previous_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
146
|
+
previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
142
147
|
|
143
148
|
ab_test('link_color', 'blue', 'red')
|
144
149
|
|
145
|
-
new_red_count = Split::Alternative.
|
146
|
-
new_blue_count = Split::Alternative.
|
150
|
+
new_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
151
|
+
new_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
147
152
|
|
148
153
|
(new_red_count + new_blue_count).should eql(previous_red_count + previous_blue_count)
|
149
154
|
end
|
@@ -153,11 +158,11 @@ describe Split::Helper do
|
|
153
158
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
154
159
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
155
160
|
|
156
|
-
previous_completion_count = Split::Alternative.
|
161
|
+
previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
157
162
|
|
158
163
|
finished('link_color')
|
159
164
|
|
160
|
-
new_completion_count = Split::Alternative.
|
165
|
+
new_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
161
166
|
|
162
167
|
new_completion_count.should eql(previous_completion_count)
|
163
168
|
end
|
@@ -181,13 +186,13 @@ describe Split::Helper do
|
|
181
186
|
it "should not increment the participation count" do
|
182
187
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
183
188
|
|
184
|
-
previous_red_count = Split::Alternative.
|
185
|
-
previous_blue_count = Split::Alternative.
|
189
|
+
previous_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
190
|
+
previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
186
191
|
|
187
192
|
ab_test('link_color', 'blue', 'red')
|
188
193
|
|
189
|
-
new_red_count = Split::Alternative.
|
190
|
-
new_blue_count = Split::Alternative.
|
194
|
+
new_red_count = Split::Alternative.new('red', 'link_color').participant_count
|
195
|
+
new_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
|
191
196
|
|
192
197
|
(new_red_count + new_blue_count).should eql(previous_red_count + previous_blue_count)
|
193
198
|
end
|
@@ -197,11 +202,11 @@ describe Split::Helper do
|
|
197
202
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
198
203
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
199
204
|
|
200
|
-
previous_completion_count = Split::Alternative.
|
205
|
+
previous_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
201
206
|
|
202
207
|
finished('link_color')
|
203
208
|
|
204
|
-
new_completion_count = Split::Alternative.
|
209
|
+
new_completion_count = Split::Alternative.new(alternative_name, 'link_color').completed_count
|
205
210
|
|
206
211
|
new_completion_count.should eql(previous_completion_count)
|
207
212
|
end
|
@@ -238,17 +243,17 @@ describe Split::Helper do
|
|
238
243
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
239
244
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
240
245
|
session[:split].should eql({'link_color' => alternative_name})
|
241
|
-
alternative = Split::Alternative.
|
246
|
+
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
242
247
|
alternative.participant_count.should eql(1)
|
243
248
|
|
244
249
|
experiment.reset
|
245
250
|
experiment.version.should eql(1)
|
246
|
-
alternative = Split::Alternative.
|
251
|
+
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
247
252
|
alternative.participant_count.should eql(0)
|
248
253
|
|
249
254
|
new_alternative_name = ab_test('link_color', 'blue', 'red')
|
250
255
|
session[:split]['link_color:1'].should eql(new_alternative_name)
|
251
|
-
new_alternative = Split::Alternative.
|
256
|
+
new_alternative = Split::Alternative.new(new_alternative_name, 'link_color')
|
252
257
|
new_alternative.participant_count.should eql(1)
|
253
258
|
end
|
254
259
|
|
@@ -256,13 +261,13 @@ describe Split::Helper do
|
|
256
261
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
257
262
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
258
263
|
session[:split].should eql({'link_color' => alternative_name})
|
259
|
-
alternative = Split::Alternative.
|
264
|
+
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
260
265
|
|
261
266
|
experiment.reset
|
262
267
|
experiment.version.should eql(1)
|
263
268
|
|
264
269
|
finished('link_color')
|
265
|
-
alternative = Split::Alternative.
|
270
|
+
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
266
271
|
alternative.completed_count.should eql(0)
|
267
272
|
end
|
268
273
|
end
|
data/split.gemspec
CHANGED
@@ -20,8 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_dependency 'redis', '~> 2.1'
|
22
22
|
s.add_dependency 'redis-namespace', '~> 1.0.3'
|
23
|
-
s.add_dependency 'sinatra', '
|
23
|
+
s.add_dependency 'sinatra', '>= 1.2.6'
|
24
24
|
|
25
|
+
s.add_development_dependency 'rake'
|
25
26
|
s.add_development_dependency 'bundler', '~> 1.0'
|
26
27
|
s.add_development_dependency 'rspec', '~> 2.6'
|
27
28
|
s.add_development_dependency 'rack-test', '~> 0.6'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: split
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-19 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
16
|
-
requirement: &
|
16
|
+
requirement: &70308912822720 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '2.1'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70308912822720
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: redis-namespace
|
27
|
-
requirement: &
|
27
|
+
requirement: &70308912821980 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,21 +32,32 @@ dependencies:
|
|
32
32
|
version: 1.0.3
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70308912821980
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: sinatra
|
38
|
-
requirement: &
|
38
|
+
requirement: &70308912821300 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
|
-
- -
|
41
|
+
- - ! '>='
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: 1.2.6
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70308912821300
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &70308912820460 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70308912820460
|
47
58
|
- !ruby/object:Gem::Dependency
|
48
59
|
name: bundler
|
49
|
-
requirement: &
|
60
|
+
requirement: &70308912819780 !ruby/object:Gem::Requirement
|
50
61
|
none: false
|
51
62
|
requirements:
|
52
63
|
- - ~>
|
@@ -54,10 +65,10 @@ dependencies:
|
|
54
65
|
version: '1.0'
|
55
66
|
type: :development
|
56
67
|
prerelease: false
|
57
|
-
version_requirements: *
|
68
|
+
version_requirements: *70308912819780
|
58
69
|
- !ruby/object:Gem::Dependency
|
59
70
|
name: rspec
|
60
|
-
requirement: &
|
71
|
+
requirement: &70308912819060 !ruby/object:Gem::Requirement
|
61
72
|
none: false
|
62
73
|
requirements:
|
63
74
|
- - ~>
|
@@ -65,10 +76,10 @@ dependencies:
|
|
65
76
|
version: '2.6'
|
66
77
|
type: :development
|
67
78
|
prerelease: false
|
68
|
-
version_requirements: *
|
79
|
+
version_requirements: *70308912819060
|
69
80
|
- !ruby/object:Gem::Dependency
|
70
81
|
name: rack-test
|
71
|
-
requirement: &
|
82
|
+
requirement: &70308912818400 !ruby/object:Gem::Requirement
|
72
83
|
none: false
|
73
84
|
requirements:
|
74
85
|
- - ~>
|
@@ -76,10 +87,10 @@ dependencies:
|
|
76
87
|
version: '0.6'
|
77
88
|
type: :development
|
78
89
|
prerelease: false
|
79
|
-
version_requirements: *
|
90
|
+
version_requirements: *70308912818400
|
80
91
|
- !ruby/object:Gem::Dependency
|
81
92
|
name: guard-rspec
|
82
|
-
requirement: &
|
93
|
+
requirement: &70308912817760 !ruby/object:Gem::Requirement
|
83
94
|
none: false
|
84
95
|
requirements:
|
85
96
|
- - ~>
|
@@ -87,7 +98,7 @@ dependencies:
|
|
87
98
|
version: '0.4'
|
88
99
|
type: :development
|
89
100
|
prerelease: false
|
90
|
-
version_requirements: *
|
101
|
+
version_requirements: *70308912817760
|
91
102
|
description:
|
92
103
|
email:
|
93
104
|
- andrewnez@gmail.com
|
@@ -134,12 +145,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
134
145
|
- - ! '>='
|
135
146
|
- !ruby/object:Gem::Version
|
136
147
|
version: '0'
|
148
|
+
segments:
|
149
|
+
- 0
|
150
|
+
hash: -3943651241702022185
|
137
151
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
152
|
none: false
|
139
153
|
requirements:
|
140
154
|
- - ! '>='
|
141
155
|
- !ruby/object:Gem::Version
|
142
156
|
version: '0'
|
157
|
+
segments:
|
158
|
+
- 0
|
159
|
+
hash: -3943651241702022185
|
143
160
|
requirements: []
|
144
161
|
rubyforge_project: split
|
145
162
|
rubygems_version: 1.8.6
|