split 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +9 -0
- data/CHANGELOG.mdown +17 -0
- data/README.mdown +16 -1
- data/lib/split/configuration.rb +2 -0
- data/lib/split/dashboard.rb +2 -44
- data/lib/split/dashboard/helpers.rb +48 -0
- data/lib/split/dashboard/public/style.css +5 -0
- data/lib/split/dashboard/views/_experiment.erb +6 -1
- data/lib/split/experiment.rb +6 -1
- data/lib/split/helper.rb +32 -7
- data/lib/split/version.rb +1 -1
- data/spec/configuration_spec.rb +1 -0
- data/spec/dashboard_helpers_spec.rb +12 -0
- data/spec/dashboard_spec.rb +22 -0
- data/spec/experiment_spec.rb +20 -0
- data/spec/helper_spec.rb +52 -11
- metadata +23 -19
data/.travis.yml
ADDED
data/CHANGELOG.mdown
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.4.0 (March 7, 2012)
|
2
|
+
|
3
|
+
**IMPORTANT**
|
4
|
+
|
5
|
+
If using ruby 1.8.x and weighted alternatives you should always pass the control alternative through as the second argument with any other alternatives as a third argument because the order of the hash is not preserved in ruby 1.8, ruby 1.9 users are not affected by this bug.
|
6
|
+
|
7
|
+
Features:
|
8
|
+
|
9
|
+
- Experiments now record when they were started (@vrish88, #35)
|
10
|
+
- Old versions of experiments in sessions are now cleaned up
|
11
|
+
- Avoid users participating in multiple experiments at once (#21)
|
12
|
+
|
13
|
+
Bugfixes:
|
14
|
+
|
15
|
+
- Overriding alternatives doesn't work for weighted alternatives (@layflags, #34)
|
16
|
+
- confidence_level helper should handle tiny z-scores (#23)
|
17
|
+
|
1
18
|
## 0.3.3 (February 16, 2012)
|
2
19
|
|
3
20
|
Bugfixes:
|
data/README.mdown
CHANGED
@@ -105,12 +105,14 @@ Perhaps you only want to show an alternative to 10% of your visitors because it
|
|
105
105
|
|
106
106
|
To do this you can pass a weight with each alternative in the following ways:
|
107
107
|
|
108
|
-
ab_test('homepage design', 'Old' => 20, 'New' => 2)
|
108
|
+
ab_test('homepage design', {'Old' => 20}, {'New' => 2})
|
109
109
|
|
110
110
|
ab_test('homepage design', 'Old', {'New' => 0.1})
|
111
111
|
|
112
112
|
ab_test('homepage design', {'Old' => 10}, 'New')
|
113
113
|
|
114
|
+
Note: If using ruby 1.8.x and weighted alternatives you should always pass the control alternative through as the second argument with any other alternatives as a third argument because the order of the hash is not preserved in ruby 1.8, ruby 1.9.1+ users are not affected by this bug.
|
115
|
+
|
114
116
|
This will only show the new alternative to visitors 1 in 10 times, the default weight for an alternative is 1.
|
115
117
|
|
116
118
|
### Overriding alternatives
|
@@ -134,6 +136,16 @@ To stop this behaviour you can pass the following option to the `finished` metho
|
|
134
136
|
|
135
137
|
The user will then always see the alternative they started with.
|
136
138
|
|
139
|
+
### Multiple experiments at once
|
140
|
+
|
141
|
+
By default Split will avoid users participating in multiple experiments at once. This means you are less likely to skew results by adding in more variation to your tests.
|
142
|
+
|
143
|
+
To stop this behaviour and allow users to participate in multiple experiments at once enable the `allow_multiple_experiments` config option like so:
|
144
|
+
|
145
|
+
Split.configure do |config|
|
146
|
+
config.allow_multiple_experiments = true
|
147
|
+
end
|
148
|
+
|
137
149
|
## Web Interface
|
138
150
|
|
139
151
|
Split comes with a Sinatra-based front end to get an overview of how your experiments are doing.
|
@@ -169,6 +181,7 @@ You can override the default configuration options of Split like so:
|
|
169
181
|
config.ignore_ip_addresses << '81.19.48.130'
|
170
182
|
config.db_failover = true # handle redis errors gracefully
|
171
183
|
config.db_failover_on_db_error = proc{|error| Rails.logger.error(error.message) }
|
184
|
+
config.allow_multiple_experiments = true
|
172
185
|
end
|
173
186
|
|
174
187
|
### DB failover solution
|
@@ -251,6 +264,8 @@ Report Issues/Feature requests on [GitHub Issues](http://github.com/andrew/split
|
|
251
264
|
|
252
265
|
Tests can be ran with `rake spec`
|
253
266
|
|
267
|
+
[![Build Status](https://secure.travis-ci.org/andrew/split.png?branch=master)](http://travis-ci.org/andrew/split)
|
268
|
+
|
254
269
|
### Note on Patches/Pull Requests
|
255
270
|
|
256
271
|
* Fork the project.
|
data/lib/split/configuration.rb
CHANGED
@@ -4,12 +4,14 @@ module Split
|
|
4
4
|
attr_accessor :ignore_ip_addresses
|
5
5
|
attr_accessor :db_failover
|
6
6
|
attr_accessor :db_failover_on_db_error
|
7
|
+
attr_accessor :allow_multiple_experiments
|
7
8
|
|
8
9
|
def initialize
|
9
10
|
@robot_regex = /\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i
|
10
11
|
@ignore_ip_addresses = []
|
11
12
|
@db_failover = false
|
12
13
|
@db_failover_on_db_error = proc{|error|} # e.g. use Rails logger here
|
14
|
+
@allow_multiple_experiments = false
|
13
15
|
end
|
14
16
|
end
|
15
17
|
end
|
data/lib/split/dashboard.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'split'
|
3
3
|
require 'bigdecimal'
|
4
|
+
require 'split/dashboard/helpers'
|
4
5
|
|
5
6
|
module Split
|
6
7
|
class Dashboard < Sinatra::Base
|
@@ -11,50 +12,7 @@ module Split
|
|
11
12
|
set :static, true
|
12
13
|
set :method_override, true
|
13
14
|
|
14
|
-
helpers
|
15
|
-
def url(*path_parts)
|
16
|
-
[ path_prefix, path_parts ].join("/").squeeze('/')
|
17
|
-
end
|
18
|
-
|
19
|
-
def path_prefix
|
20
|
-
request.env['SCRIPT_NAME']
|
21
|
-
end
|
22
|
-
|
23
|
-
def number_to_percentage(number, precision = 2)
|
24
|
-
round(number * 100)
|
25
|
-
end
|
26
|
-
|
27
|
-
def round(number, precision = 2)
|
28
|
-
BigDecimal.new(number.to_s).round(precision).to_f
|
29
|
-
end
|
30
|
-
|
31
|
-
def confidence_level(z_score)
|
32
|
-
z = z_score.to_f
|
33
|
-
if z > 0.0
|
34
|
-
if z < 1.96
|
35
|
-
'no confidence'
|
36
|
-
elsif z < 2.57
|
37
|
-
'95% confidence'
|
38
|
-
elsif z < 3.29
|
39
|
-
'99% confidence'
|
40
|
-
else
|
41
|
-
'99.9% confidence'
|
42
|
-
end
|
43
|
-
elsif z < 0.0
|
44
|
-
if z > -1.96
|
45
|
-
'no confidence'
|
46
|
-
elsif z > -2.57
|
47
|
-
'95% confidence'
|
48
|
-
elsif z > -3.29
|
49
|
-
'99% confidence'
|
50
|
-
else
|
51
|
-
'99.9% confidence'
|
52
|
-
end
|
53
|
-
else
|
54
|
-
"No Change"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
15
|
+
helpers Split::DashboardHelpers
|
58
16
|
|
59
17
|
get '/' do
|
60
18
|
@experiments = Split::Experiment.all
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Split
|
2
|
+
module DashboardHelpers
|
3
|
+
def url(*path_parts)
|
4
|
+
[ path_prefix, path_parts ].join("/").squeeze('/')
|
5
|
+
end
|
6
|
+
|
7
|
+
def path_prefix
|
8
|
+
request.env['SCRIPT_NAME']
|
9
|
+
end
|
10
|
+
|
11
|
+
def number_to_percentage(number, precision = 2)
|
12
|
+
round(number * 100)
|
13
|
+
end
|
14
|
+
|
15
|
+
def round(number, precision = 2)
|
16
|
+
BigDecimal.new(number.to_s).round(precision).to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
def confidence_level(z_score)
|
20
|
+
return z_score if z_score.is_a? String
|
21
|
+
|
22
|
+
z = round(z_score.to_f, 3)
|
23
|
+
if z > 0.0
|
24
|
+
if z < 1.96
|
25
|
+
'no confidence'
|
26
|
+
elsif z < 2.57
|
27
|
+
'95% confidence'
|
28
|
+
elsif z < 3.29
|
29
|
+
'99% confidence'
|
30
|
+
else
|
31
|
+
'99.9% confidence'
|
32
|
+
end
|
33
|
+
elsif z < 0.0
|
34
|
+
if z > -1.96
|
35
|
+
'no confidence'
|
36
|
+
elsif z > -2.57
|
37
|
+
'95% confidence'
|
38
|
+
elsif z > -3.29
|
39
|
+
'99% confidence'
|
40
|
+
else
|
41
|
+
'99.9% confidence'
|
42
|
+
end
|
43
|
+
else
|
44
|
+
"No Change"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,7 +1,12 @@
|
|
1
1
|
<div class="experiment">
|
2
2
|
<div class="experiment-header">
|
3
|
-
<h2>
|
3
|
+
<h2>
|
4
|
+
Experiment: <%= experiment.name %>
|
5
|
+
<% if experiment.version > 1 %><span class='version'>v<%= experiment.version %></span><% end %>
|
6
|
+
</h2>
|
7
|
+
|
4
8
|
<div class='inline-controls'>
|
9
|
+
<small><%= experiment.start_time ? experiment.start_time.strftime('%Y-%m-%d') : 'Unknown' %></small>
|
5
10
|
<form action="<%= url "/reset/#{experiment.name}" %>" method='post' onclick="return confirmReset()">
|
6
11
|
<input type="submit" value="Reset Data">
|
7
12
|
</form>
|
data/lib/split/experiment.rb
CHANGED
@@ -26,11 +26,15 @@ module Split
|
|
26
26
|
Split.redis.hdel(:experiment_winner, name)
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
29
|
def winner=(winner_name)
|
31
30
|
Split.redis.hset(:experiment_winner, name, winner_name.to_s)
|
32
31
|
end
|
33
32
|
|
33
|
+
def start_time
|
34
|
+
t = Split.redis.hget(:experiment_start_times, @name)
|
35
|
+
Time.parse(t) if t
|
36
|
+
end
|
37
|
+
|
34
38
|
def alternatives
|
35
39
|
@alternatives.dup
|
36
40
|
end
|
@@ -92,6 +96,7 @@ module Split
|
|
92
96
|
def save
|
93
97
|
if new_record?
|
94
98
|
Split.redis.sadd(:experiments, name)
|
99
|
+
Split.redis.hset(:experiment_start_times, @name, Time.now)
|
95
100
|
@alternatives.reverse.each {|a| Split.redis.lpush(name, a.name) }
|
96
101
|
end
|
97
102
|
end
|
data/lib/split/helper.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
module Split
|
2
2
|
module Helper
|
3
|
-
def ab_test(experiment_name, *alternatives)
|
3
|
+
def ab_test(experiment_name, control, *alternatives)
|
4
|
+
puts 'WARNING: You should always pass the control alternative through as the second argument with any other alternatives as the third because the order of the hash is not preserved in ruby 1.8' if RUBY_VERSION.match(/1\.8/) && alternatives.length.zero?
|
4
5
|
begin
|
5
|
-
experiment = Split::Experiment.find_or_create(experiment_name, *alternatives)
|
6
|
+
experiment = Split::Experiment.find_or_create(experiment_name, *([control] + alternatives))
|
6
7
|
if experiment.winner
|
7
8
|
ret = experiment.winner.name
|
8
9
|
else
|
9
|
-
if forced_alternative = override(experiment.name,
|
10
|
+
if forced_alternative = override(experiment.name, experiment.alternative_names)
|
10
11
|
ret = forced_alternative
|
11
12
|
else
|
12
|
-
|
13
|
+
clean_old_versions(experiment)
|
14
|
+
begin_experiment(experiment) if exclude_visitor? or not_allowed_to_test?(experiment.key)
|
13
15
|
|
14
|
-
if ab_user[experiment.key]
|
16
|
+
if ab_user[experiment.key]
|
15
17
|
ret = ab_user[experiment.key]
|
16
18
|
else
|
17
19
|
alternative = experiment.next_alternative
|
@@ -24,7 +26,7 @@ module Split
|
|
24
26
|
rescue Errno::ECONNREFUSED => e
|
25
27
|
raise unless Split.configuration.db_failover
|
26
28
|
Split.configuration.db_failover_on_db_error.call(e)
|
27
|
-
ret = Hash ===
|
29
|
+
ret = Hash === control ? control.keys.first : control
|
28
30
|
end
|
29
31
|
if block_given?
|
30
32
|
if defined?(capture) # a block in a rails view
|
@@ -56,7 +58,8 @@ module Split
|
|
56
58
|
params[experiment_name] if defined?(params) && alternatives.include?(params[experiment_name])
|
57
59
|
end
|
58
60
|
|
59
|
-
def begin_experiment(experiment, alternative_name)
|
61
|
+
def begin_experiment(experiment, alternative_name = nil)
|
62
|
+
alternative_name ||= experiment.control.name
|
60
63
|
ab_user[experiment.key] = alternative_name
|
61
64
|
end
|
62
65
|
|
@@ -68,6 +71,28 @@ module Split
|
|
68
71
|
is_robot? or is_ignored_ip_address?
|
69
72
|
end
|
70
73
|
|
74
|
+
def not_allowed_to_test?(experiment_key)
|
75
|
+
!Split.configuration.allow_multiple_experiments && doing_other_tests?(experiment_key)
|
76
|
+
end
|
77
|
+
|
78
|
+
def doing_other_tests?(experiment_key)
|
79
|
+
ab_user.keys.reject{|k| k == experiment_key}.length > 0
|
80
|
+
end
|
81
|
+
|
82
|
+
def clean_old_versions(experiment)
|
83
|
+
old_versions(experiment).each do |old_key|
|
84
|
+
ab_user.delete old_key
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def old_versions(experiment)
|
89
|
+
if experiment.version > 0
|
90
|
+
ab_user.keys.select{|k| k.match(Regexp.new(experiment.name))}.reject{|k| k == experiment.key}
|
91
|
+
else
|
92
|
+
[]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
71
96
|
def is_robot?
|
72
97
|
request.user_agent =~ Split.configuration.robot_regex
|
73
98
|
end
|
data/lib/split/version.rb
CHANGED
data/spec/configuration_spec.rb
CHANGED
@@ -8,5 +8,6 @@ describe Split::Configuration do
|
|
8
8
|
config.robot_regex.should eql(/\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i)
|
9
9
|
config.db_failover.should be_false
|
10
10
|
config.db_failover_on_db_error.should be_a Proc
|
11
|
+
config.allow_multiple_experiments.should be_false
|
11
12
|
end
|
12
13
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'split/dashboard/helpers'
|
3
|
+
|
4
|
+
include Split::DashboardHelpers
|
5
|
+
|
6
|
+
describe Split::DashboardHelpers do
|
7
|
+
describe 'confidence_level' do
|
8
|
+
it 'should handle very small numbers' do
|
9
|
+
confidence_level(0.000000000000006).should eql('No Change')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/spec/dashboard_spec.rb
CHANGED
@@ -51,4 +51,26 @@ describe Split::Dashboard do
|
|
51
51
|
last_response.should be_redirect
|
52
52
|
experiment.winner.name.should eql('red')
|
53
53
|
end
|
54
|
+
|
55
|
+
it "should display the start date" do
|
56
|
+
experiment_start_time = Time.parse('2011-07-07')
|
57
|
+
Time.stub(:now => experiment_start_time)
|
58
|
+
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
59
|
+
|
60
|
+
get '/'
|
61
|
+
|
62
|
+
last_response.body.should include('<small>2011-07-07</small>')
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should handle experiments without a start date" do
|
66
|
+
experiment_start_time = Time.parse('2011-07-07')
|
67
|
+
Time.stub(:now => experiment_start_time)
|
68
|
+
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
69
|
+
|
70
|
+
Split.redis.hdel(:experiment_start_times, experiment.name)
|
71
|
+
|
72
|
+
get '/'
|
73
|
+
|
74
|
+
last_response.body.should include('<small>Unknown</small>')
|
75
|
+
end
|
54
76
|
end
|
data/spec/experiment_spec.rb
CHANGED
@@ -20,6 +20,26 @@ describe Split::Experiment do
|
|
20
20
|
Split.redis.exists('basket_text').should be true
|
21
21
|
end
|
22
22
|
|
23
|
+
it "should save the start time to redis" do
|
24
|
+
experiment_start_time = Time.parse("Sat Mar 03 14:01:03")
|
25
|
+
Time.stub(:now => experiment_start_time)
|
26
|
+
experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
|
27
|
+
experiment.save
|
28
|
+
|
29
|
+
Split::Experiment.find('basket_text').start_time.should == experiment_start_time
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should handle not having a start time" do
|
33
|
+
experiment_start_time = Time.parse("Sat Mar 03 14:01:03")
|
34
|
+
Time.stub(:now => experiment_start_time)
|
35
|
+
experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
|
36
|
+
experiment.save
|
37
|
+
|
38
|
+
Split.redis.hdel(:experiment_start_times, experiment.name)
|
39
|
+
|
40
|
+
Split::Experiment.find('basket_text').start_time.should == nil
|
41
|
+
end
|
42
|
+
|
23
43
|
it "should not create duplicates when saving multiple times" do
|
24
44
|
experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
|
25
45
|
experiment.save
|
data/spec/helper_spec.rb
CHANGED
@@ -50,6 +50,13 @@ describe Split::Helper do
|
|
50
50
|
@params = {'link_color' => 'blue'}
|
51
51
|
alternative = ab_test('link_color', 'blue', 'red')
|
52
52
|
alternative.should eql('blue')
|
53
|
+
alternative = ab_test('link_color', {'blue' => 1}, 'red' => 5)
|
54
|
+
alternative.should eql('blue')
|
55
|
+
@params = {'link_color' => 'red'}
|
56
|
+
alternative = ab_test('link_color', 'blue', 'red')
|
57
|
+
alternative.should eql('red')
|
58
|
+
alternative = ab_test('link_color', {'blue' => 5}, 'red' => 1)
|
59
|
+
alternative.should eql('red')
|
53
60
|
end
|
54
61
|
|
55
62
|
it "should allow passing a block" do
|
@@ -58,17 +65,37 @@ describe Split::Helper do
|
|
58
65
|
ret.should eql("shared/#{alt}")
|
59
66
|
end
|
60
67
|
|
61
|
-
it
|
68
|
+
it "should allow the share of visitors see an alternative to be specificed" do
|
62
69
|
ab_test('link_color', {'blue' => 0.8}, {'red' => 20})
|
63
70
|
['red', 'blue'].should include(ab_user['link_color'])
|
64
71
|
end
|
65
72
|
|
66
|
-
it
|
67
|
-
ab_test('link_color', 'blue' => 0.01, 'red' => 0.2)
|
73
|
+
it "should allow alternative weighting interface as a single hash" do
|
74
|
+
ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2)
|
68
75
|
experiment = Split::Experiment.find('link_color')
|
69
76
|
experiment.alternative_names.should eql(['blue', 'red'])
|
70
77
|
end
|
71
78
|
|
79
|
+
it "should only let a user participate in one experiment at a time" do
|
80
|
+
ab_test('link_color', 'blue', 'red')
|
81
|
+
ab_test('button_size', 'small', 'big')
|
82
|
+
ab_user['button_size'].should eql('small')
|
83
|
+
big = Split::Alternative.new('big', 'button_size')
|
84
|
+
big.participant_count.should eql(0)
|
85
|
+
small = Split::Alternative.new('small', 'button_size')
|
86
|
+
small.participant_count.should eql(0)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should let a user participate in many experiment with allow_multiple_experiments option" do
|
90
|
+
Split.configure do |config|
|
91
|
+
config.allow_multiple_experiments = true
|
92
|
+
end
|
93
|
+
link_color = ab_test('link_color', 'blue', 'red')
|
94
|
+
button_size = ab_test('button_size', 'small', 'big')
|
95
|
+
ab_user['button_size'].should eql(button_size)
|
96
|
+
button_size_alt = Split::Alternative.new(button_size, 'button_size')
|
97
|
+
button_size_alt.participant_count.should eql(1)
|
98
|
+
end
|
72
99
|
end
|
73
100
|
|
74
101
|
describe 'finished' do
|
@@ -258,6 +285,22 @@ describe Split::Helper do
|
|
258
285
|
new_alternative.participant_count.should eql(1)
|
259
286
|
end
|
260
287
|
|
288
|
+
it "should cleanup old versions of experiments from the session" do
|
289
|
+
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
290
|
+
alternative_name = ab_test('link_color', 'blue', 'red')
|
291
|
+
session[:split].should eql({'link_color' => alternative_name})
|
292
|
+
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
293
|
+
alternative.participant_count.should eql(1)
|
294
|
+
|
295
|
+
experiment.reset
|
296
|
+
experiment.version.should eql(1)
|
297
|
+
alternative = Split::Alternative.new(alternative_name, 'link_color')
|
298
|
+
alternative.participant_count.should eql(0)
|
299
|
+
|
300
|
+
new_alternative_name = ab_test('link_color', 'blue', 'red')
|
301
|
+
session[:split].should eql({'link_color:1' => new_alternative_name})
|
302
|
+
end
|
303
|
+
|
261
304
|
it "should only count completion of users on the current version" do
|
262
305
|
experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
|
263
306
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
@@ -329,14 +372,12 @@ describe Split::Helper do
|
|
329
372
|
ab_test('link_color', 'blue', 'red')
|
330
373
|
end
|
331
374
|
it 'should always use first alternative' do
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
end.should eq('shared/blue')
|
339
|
-
end
|
375
|
+
ab_test('link_color', 'blue', 'red').should eq('blue')
|
376
|
+
ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2).should eq('blue')
|
377
|
+
ab_test('link_color', {'blue' => 0.8}, {'red' => 20}).should eq('blue')
|
378
|
+
ab_test('link_color', 'blue', 'red') do |alternative|
|
379
|
+
"shared/#{alternative}"
|
380
|
+
end.should eq('shared/blue')
|
340
381
|
end
|
341
382
|
end
|
342
383
|
|
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.
|
4
|
+
version: 0.4.0
|
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: 2012-
|
12
|
+
date: 2012-03-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
16
|
-
requirement: &
|
16
|
+
requirement: &70291376942100 !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: *70291376942100
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: redis-namespace
|
27
|
-
requirement: &
|
27
|
+
requirement: &70291376938960 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 1.0.3
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70291376938960
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: sinatra
|
38
|
-
requirement: &
|
38
|
+
requirement: &70291376938260 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 1.2.6
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70291376938260
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rake
|
49
|
-
requirement: &
|
49
|
+
requirement: &70291376937280 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70291376937280
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: bundler
|
60
|
-
requirement: &
|
60
|
+
requirement: &70291376930680 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '1.0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70291376930680
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
|
-
requirement: &
|
71
|
+
requirement: &70291376929580 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: '2.6'
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70291376929580
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: rack-test
|
82
|
-
requirement: &
|
82
|
+
requirement: &70291376928420 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: '0.6'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70291376928420
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: guard-rspec
|
93
|
-
requirement: &
|
93
|
+
requirement: &70291376927240 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ~>
|
@@ -98,7 +98,7 @@ dependencies:
|
|
98
98
|
version: '0.4'
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70291376927240
|
102
102
|
description:
|
103
103
|
email:
|
104
104
|
- andrewnez@gmail.com
|
@@ -107,6 +107,7 @@ extensions: []
|
|
107
107
|
extra_rdoc_files: []
|
108
108
|
files:
|
109
109
|
- .gitignore
|
110
|
+
- .travis.yml
|
110
111
|
- CHANGELOG.mdown
|
111
112
|
- Gemfile
|
112
113
|
- Guardfile
|
@@ -117,6 +118,7 @@ files:
|
|
117
118
|
- lib/split/alternative.rb
|
118
119
|
- lib/split/configuration.rb
|
119
120
|
- lib/split/dashboard.rb
|
121
|
+
- lib/split/dashboard/helpers.rb
|
120
122
|
- lib/split/dashboard/public/dashboard.js
|
121
123
|
- lib/split/dashboard/public/reset.css
|
122
124
|
- lib/split/dashboard/public/style.css
|
@@ -128,6 +130,7 @@ files:
|
|
128
130
|
- lib/split/version.rb
|
129
131
|
- spec/alternative_spec.rb
|
130
132
|
- spec/configuration_spec.rb
|
133
|
+
- spec/dashboard_helpers_spec.rb
|
131
134
|
- spec/dashboard_spec.rb
|
132
135
|
- spec/experiment_spec.rb
|
133
136
|
- spec/helper_spec.rb
|
@@ -153,13 +156,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
156
|
version: '0'
|
154
157
|
requirements: []
|
155
158
|
rubyforge_project: split
|
156
|
-
rubygems_version: 1.8.
|
159
|
+
rubygems_version: 1.8.11
|
157
160
|
signing_key:
|
158
161
|
specification_version: 3
|
159
162
|
summary: Rack based split testing framework
|
160
163
|
test_files:
|
161
164
|
- spec/alternative_spec.rb
|
162
165
|
- spec/configuration_spec.rb
|
166
|
+
- spec/dashboard_helpers_spec.rb
|
163
167
|
- spec/dashboard_spec.rb
|
164
168
|
- spec/experiment_spec.rb
|
165
169
|
- spec/helper_spec.rb
|