split 4.0.1 → 4.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +8 -3
- data/.rubocop.yml +2 -5
- data/CHANGELOG.md +38 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +1 -1
- data/README.md +11 -3
- data/Rakefile +4 -5
- data/gemfiles/5.2.gemfile +1 -3
- data/gemfiles/6.0.gemfile +1 -3
- data/gemfiles/6.1.gemfile +1 -3
- data/gemfiles/7.0.gemfile +1 -3
- data/lib/split/algorithms/block_randomization.rb +5 -6
- data/lib/split/algorithms/whiplash.rb +16 -18
- data/lib/split/algorithms.rb +14 -0
- data/lib/split/alternative.rb +21 -22
- data/lib/split/cache.rb +0 -1
- data/lib/split/combined_experiments_helper.rb +4 -4
- data/lib/split/configuration.rb +83 -84
- data/lib/split/dashboard/helpers.rb +6 -7
- data/lib/split/dashboard/pagination_helpers.rb +53 -54
- data/lib/split/dashboard/public/style.css +5 -2
- data/lib/split/dashboard/views/_experiment.erb +2 -1
- data/lib/split/dashboard/views/index.erb +19 -4
- data/lib/split/dashboard.rb +29 -23
- data/lib/split/encapsulated_helper.rb +4 -6
- data/lib/split/experiment.rb +93 -88
- data/lib/split/experiment_catalog.rb +6 -5
- data/lib/split/extensions/string.rb +1 -1
- data/lib/split/goals_collection.rb +8 -10
- data/lib/split/helper.rb +20 -20
- data/lib/split/metric.rb +4 -5
- data/lib/split/persistence/cookie_adapter.rb +44 -47
- data/lib/split/persistence/dual_adapter.rb +7 -8
- data/lib/split/persistence/redis_adapter.rb +3 -4
- data/lib/split/persistence/session_adapter.rb +0 -2
- data/lib/split/persistence.rb +4 -4
- data/lib/split/redis_interface.rb +7 -1
- data/lib/split/trial.rb +23 -24
- data/lib/split/user.rb +12 -13
- data/lib/split/version.rb +1 -1
- data/lib/split/zscore.rb +1 -3
- data/lib/split.rb +26 -25
- data/spec/algorithms/block_randomization_spec.rb +6 -5
- data/spec/algorithms/weighted_sample_spec.rb +6 -5
- data/spec/algorithms/whiplash_spec.rb +4 -5
- data/spec/alternative_spec.rb +35 -36
- data/spec/cache_spec.rb +15 -19
- data/spec/combined_experiments_helper_spec.rb +18 -17
- data/spec/configuration_spec.rb +32 -38
- data/spec/dashboard/pagination_helpers_spec.rb +69 -67
- data/spec/dashboard/paginator_spec.rb +10 -9
- data/spec/dashboard_helpers_spec.rb +19 -18
- data/spec/dashboard_spec.rb +79 -35
- data/spec/encapsulated_helper_spec.rb +12 -14
- data/spec/experiment_catalog_spec.rb +14 -13
- data/spec/experiment_spec.rb +132 -123
- data/spec/goals_collection_spec.rb +17 -15
- data/spec/helper_spec.rb +415 -382
- data/spec/metric_spec.rb +14 -14
- data/spec/persistence/cookie_adapter_spec.rb +23 -8
- data/spec/persistence/dual_adapter_spec.rb +71 -71
- data/spec/persistence/redis_adapter_spec.rb +28 -29
- data/spec/persistence/session_adapter_spec.rb +2 -3
- data/spec/persistence_spec.rb +1 -2
- data/spec/redis_interface_spec.rb +26 -14
- data/spec/spec_helper.rb +16 -13
- data/spec/split_spec.rb +11 -11
- data/spec/support/cookies_mock.rb +1 -2
- data/spec/trial_spec.rb +61 -60
- data/spec/user_spec.rb +36 -36
- data/split.gemspec +21 -20
- metadata +25 -14
- data/.rubocop_todo.yml +0 -226
- data/Appraisals +0 -19
- data/gemfiles/5.0.gemfile +0 -9
- data/gemfiles/5.1.gemfile +0 -9
data/lib/split/configuration.rb
CHANGED
@@ -39,83 +39,83 @@ module Split
|
|
39
39
|
def bots
|
40
40
|
@bots ||= {
|
41
41
|
# Indexers
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
42
|
+
"AdsBot-Google" => "Google Adwords",
|
43
|
+
"Baidu" => "Chinese search engine",
|
44
|
+
"Baiduspider" => "Chinese search engine",
|
45
|
+
"bingbot" => "Microsoft bing bot",
|
46
|
+
"Butterfly" => "Topsy Labs",
|
47
|
+
"Gigabot" => "Gigabot spider",
|
48
|
+
"Googlebot" => "Google spider",
|
49
|
+
"MJ12bot" => "Majestic-12 spider",
|
50
|
+
"msnbot" => "Microsoft bot",
|
51
|
+
"rogerbot" => "SeoMoz spider",
|
52
|
+
"PaperLiBot" => "PaperLi is another content curation service",
|
53
|
+
"Slurp" => "Yahoo spider",
|
54
|
+
"Sogou" => "Chinese search engine",
|
55
|
+
"spider" => "generic web spider",
|
56
|
+
"UnwindFetchor" => "Gnip crawler",
|
57
|
+
"WordPress" => "WordPress spider",
|
58
|
+
"YandexAccessibilityBot" => "Yandex accessibility spider",
|
59
|
+
"YandexBot" => "Yandex spider",
|
60
|
+
"YandexMobileBot" => "Yandex mobile spider",
|
61
|
+
"ZIBB" => "ZIBB spider",
|
62
62
|
|
63
63
|
# HTTP libraries
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
64
|
+
"Apache-HttpClient" => "Java http library",
|
65
|
+
"AppEngine-Google" => "Google App Engine",
|
66
|
+
"curl" => "curl unix CLI http client",
|
67
|
+
"ColdFusion" => "ColdFusion http library",
|
68
|
+
"EventMachine HttpClient" => "Ruby http library",
|
69
|
+
"Go http package" => "Go http library",
|
70
|
+
"Go-http-client" => "Go http library",
|
71
|
+
"Java" => "Generic Java http library",
|
72
|
+
"libwww-perl" => "Perl client-server library loved by script kids",
|
73
|
+
"lwp-trivial" => "Another Perl library loved by script kids",
|
74
|
+
"Python-urllib" => "Python http library",
|
75
|
+
"PycURL" => "Python http library",
|
76
|
+
"Test Certificate Info" => "C http library?",
|
77
|
+
"Typhoeus" => "Ruby http library",
|
78
|
+
"Wget" => "wget unix CLI http client",
|
79
79
|
|
80
80
|
# URL expanders / previewers
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
81
|
+
"awe.sm" => "Awe.sm URL expander",
|
82
|
+
"bitlybot" => "bit.ly bot",
|
83
|
+
"bot@linkfluence.net" => "Linkfluence bot",
|
84
|
+
"facebookexternalhit" => "facebook bot",
|
85
|
+
"Facebot" => "Facebook crawler",
|
86
|
+
"Feedfetcher-Google" => "Google Feedfetcher",
|
87
|
+
"https://developers.google.com/+/web/snippet" => "Google+ Snippet Fetcher",
|
88
|
+
"LinkedInBot" => "LinkedIn bot",
|
89
|
+
"LongURL" => "URL expander service",
|
90
|
+
"NING" => "NING - Yet Another Twitter Swarmer",
|
91
|
+
"Pinterestbot" => "Pinterest Bot",
|
92
|
+
"redditbot" => "Reddit Bot",
|
93
|
+
"ShortLinkTranslate" => "Link shortener",
|
94
|
+
"Slackbot" => "Slackbot link expander",
|
95
|
+
"TweetmemeBot" => "TweetMeMe Crawler",
|
96
|
+
"Twitterbot" => "Twitter URL expander",
|
97
|
+
"UnwindFetch" => "Gnip URL expander",
|
98
|
+
"vkShare" => "VKontake Sharer",
|
99
99
|
|
100
100
|
# Uptime monitoring
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
101
|
+
"check_http" => "Nagios monitor",
|
102
|
+
"GoogleStackdriverMonitoring" => "Google Cloud monitor",
|
103
|
+
"NewRelicPinger" => "NewRelic monitor",
|
104
|
+
"Panopta" => "Monitoring service",
|
105
|
+
"Pingdom" => "Pingdom monitoring",
|
106
|
+
"SiteUptime" => "Site monitoring services",
|
107
|
+
"UptimeRobot" => "Monitoring service",
|
108
108
|
|
109
109
|
# ???
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
110
|
+
"DigitalPersona Fingerprint Software" => "HP Fingerprint scanner",
|
111
|
+
"ShowyouBot" => "Showyou iOS app spider",
|
112
|
+
"ZyBorg" => "Zyborg? Hmmm....",
|
113
|
+
"ELB-HealthChecker" => "ELB Health Check"
|
114
114
|
}
|
115
115
|
end
|
116
116
|
|
117
|
-
def experiments=
|
118
|
-
raise InvalidExperimentsFormatError.new(
|
117
|
+
def experiments=(experiments)
|
118
|
+
raise InvalidExperimentsFormatError.new("Experiments must be a Hash") unless experiments.respond_to?(:keys)
|
119
119
|
@experiments = experiments
|
120
120
|
end
|
121
121
|
|
@@ -157,8 +157,8 @@ module Split
|
|
157
157
|
|
158
158
|
@experiments.each do |experiment_name, settings|
|
159
159
|
alternatives = if (alts = value_for(settings, :alternatives))
|
160
|
-
|
161
|
-
|
160
|
+
normalize_alternatives(alts)
|
161
|
+
end
|
162
162
|
|
163
163
|
experiment_data = {
|
164
164
|
alternatives: alternatives,
|
@@ -213,14 +213,14 @@ module Split
|
|
213
213
|
|
214
214
|
def initialize
|
215
215
|
@ignore_ip_addresses = []
|
216
|
-
@ignore_filter = proc{ |request| is_robot? || is_ignored_ip_address? }
|
216
|
+
@ignore_filter = proc { |request| is_robot? || is_ignored_ip_address? }
|
217
217
|
@db_failover = false
|
218
|
-
@db_failover_on_db_error = proc{|error|} # e.g. use Rails logger here
|
219
|
-
@on_experiment_reset = proc{|experiment|}
|
220
|
-
@on_experiment_delete = proc{|experiment|}
|
221
|
-
@on_before_experiment_reset = proc{|experiment|}
|
222
|
-
@on_before_experiment_delete = proc{|experiment|}
|
223
|
-
@on_experiment_winner_choose = proc{|experiment|}
|
218
|
+
@db_failover_on_db_error = proc { |error| } # e.g. use Rails logger here
|
219
|
+
@on_experiment_reset = proc { |experiment| }
|
220
|
+
@on_experiment_delete = proc { |experiment| }
|
221
|
+
@on_before_experiment_reset = proc { |experiment| }
|
222
|
+
@on_before_experiment_delete = proc { |experiment| }
|
223
|
+
@on_experiment_winner_choose = proc { |experiment| }
|
224
224
|
@db_failover_allow_parameter_override = false
|
225
225
|
@allow_multiple_experiments = false
|
226
226
|
@enabled = true
|
@@ -232,20 +232,19 @@ module Split
|
|
232
232
|
@include_rails_helper = true
|
233
233
|
@beta_probability_simulations = 10000
|
234
234
|
@winning_alternative_recalculation_interval = 60 * 60 * 24 # 1 day
|
235
|
-
@redis = ENV.fetch(ENV.fetch(
|
235
|
+
@redis = ENV.fetch(ENV.fetch("REDIS_PROVIDER", "REDIS_URL"), "redis://localhost:6379")
|
236
236
|
@dashboard_pagination_default_per_page = 10
|
237
237
|
end
|
238
238
|
|
239
239
|
private
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
240
|
+
def value_for(hash, key)
|
241
|
+
if hash.kind_of?(Hash)
|
242
|
+
hash.has_key?(key.to_s) ? hash[key.to_s] : hash[key.to_sym]
|
243
|
+
end
|
244
244
|
end
|
245
|
-
end
|
246
245
|
|
247
|
-
|
248
|
-
|
249
|
-
|
246
|
+
def escaped_bots
|
247
|
+
bots.map { |key, _| Regexp.escape(key) }
|
248
|
+
end
|
250
249
|
end
|
251
250
|
end
|
@@ -7,11 +7,11 @@ module Split
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def url(*path_parts)
|
10
|
-
[ path_prefix, path_parts ].join("/").squeeze(
|
10
|
+
[ path_prefix, path_parts ].join("/").squeeze("/")
|
11
11
|
end
|
12
12
|
|
13
13
|
def path_prefix
|
14
|
-
request.env[
|
14
|
+
request.env["SCRIPT_NAME"]
|
15
15
|
end
|
16
16
|
|
17
17
|
def number_to_percentage(number, precision = 2)
|
@@ -32,15 +32,14 @@ module Split
|
|
32
32
|
z = round(z_score.to_s.to_f, 3).abs
|
33
33
|
|
34
34
|
if z >= 2.58
|
35
|
-
|
35
|
+
"99% confidence"
|
36
36
|
elsif z >= 1.96
|
37
|
-
|
37
|
+
"95% confidence"
|
38
38
|
elsif z >= 1.65
|
39
|
-
|
39
|
+
"90% confidence"
|
40
40
|
else
|
41
|
-
|
41
|
+
"Insufficient confidence"
|
42
42
|
end
|
43
|
-
|
44
43
|
end
|
45
44
|
end
|
46
45
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "split/dashboard/paginator"
|
4
4
|
|
5
5
|
module Split
|
6
6
|
module DashboardPaginationHelpers
|
@@ -30,58 +30,57 @@ module Split
|
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
33
|
+
def show_first_page_tag?
|
34
|
+
page_number > 2
|
35
|
+
end
|
36
|
+
|
37
|
+
def first_page_tag
|
38
|
+
%Q(<a href="#{url.chop}?page=1&per=#{pagination_per}">1</a>)
|
39
|
+
end
|
40
|
+
|
41
|
+
def show_first_ellipsis_tag?
|
42
|
+
page_number >= 4
|
43
|
+
end
|
44
|
+
|
45
|
+
def ellipsis_tag
|
46
|
+
"<span>...</span>"
|
47
|
+
end
|
48
|
+
|
49
|
+
def show_prev_page_tag?
|
50
|
+
page_number > 1
|
51
|
+
end
|
52
|
+
|
53
|
+
def prev_page_tag
|
54
|
+
%Q(<a href="#{url.chop}?page=#{page_number - 1}&per=#{pagination_per}">#{page_number - 1}</a>)
|
55
|
+
end
|
56
|
+
|
57
|
+
def current_page_tag
|
58
|
+
"<span><b>#{page_number}</b></span>"
|
59
|
+
end
|
60
|
+
|
61
|
+
def show_next_page_tag?(collection)
|
62
|
+
(page_number * pagination_per) < collection.count
|
63
|
+
end
|
64
|
+
|
65
|
+
def next_page_tag
|
66
|
+
%Q(<a href="#{url.chop}?page=#{page_number + 1}&per=#{pagination_per}">#{page_number + 1}</a>)
|
67
|
+
end
|
68
|
+
|
69
|
+
def show_last_ellipsis_tag?(collection)
|
70
|
+
(total_pages(collection) - page_number) >= 3
|
71
|
+
end
|
72
|
+
|
73
|
+
def total_pages(collection)
|
74
|
+
collection.count / pagination_per + ((collection.count % pagination_per).zero? ? 0 : 1)
|
75
|
+
end
|
76
|
+
|
77
|
+
def show_last_page_tag?(collection)
|
78
|
+
page_number < (total_pages(collection) - 1)
|
79
|
+
end
|
80
|
+
|
81
|
+
def last_page_tag(collection)
|
82
|
+
total = total_pages(collection)
|
83
|
+
%Q(<a href="#{url.chop}?page=#{total}&per=#{pagination_per}">#{total}</a>)
|
84
|
+
end
|
86
85
|
end
|
87
86
|
end
|
@@ -258,7 +258,7 @@ body {
|
|
258
258
|
color: #408C48;
|
259
259
|
}
|
260
260
|
|
261
|
-
a.button, button, input[type="submit"] {
|
261
|
+
.experiment a.button, .experiment button, .experiment input[type="submit"] {
|
262
262
|
padding: 4px 10px;
|
263
263
|
overflow: hidden;
|
264
264
|
background: #d8dae0;
|
@@ -312,10 +312,13 @@ a.button.green:focus, button.green:focus, input[type="submit"].green:focus {
|
|
312
312
|
background:#768E7A;
|
313
313
|
}
|
314
314
|
|
315
|
-
|
315
|
+
.dashboard-controls input, .dashboard-controls select {
|
316
316
|
padding: 10px;
|
317
317
|
}
|
318
318
|
|
319
|
+
.dashboard-controls-bottom {
|
320
|
+
margin-top: 10px;
|
321
|
+
}
|
319
322
|
|
320
323
|
.pagination {
|
321
324
|
text-align: center;
|
@@ -16,7 +16,8 @@
|
|
16
16
|
summary_texts = {}
|
17
17
|
extra_columns.each do |column|
|
18
18
|
extra_infos = experiment.alternatives.map(&:extra_info).select{|extra_info| extra_info && extra_info[column] }
|
19
|
-
|
19
|
+
|
20
|
+
if extra_infos.length > 0 && extra_infos.all? { |extra_info| extra_info[column].kind_of?(Numeric) }
|
20
21
|
summary_texts[column] = extra_infos.inject(0){|sum, extra_info| sum += extra_info[column]}
|
21
22
|
else
|
22
23
|
summary_texts[column] = "N/A"
|
@@ -1,10 +1,12 @@
|
|
1
1
|
<% if @experiments.any? %>
|
2
2
|
<p class="intro">The list below contains all the registered experiments along with the number of test participants, completed and conversion rate currently in the system.</p>
|
3
3
|
|
4
|
-
<
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
<div class="dashboard-controls">
|
5
|
+
<input type="text" placeholder="Begin typing to filter" id="filter" />
|
6
|
+
<input type="button" id="toggle-completed" value="Hide completed" />
|
7
|
+
<input type="button" id="toggle-active" value="Hide active" />
|
8
|
+
<input type="button" id="clear-filter" value="Clear filters" />
|
9
|
+
</div>
|
8
10
|
|
9
11
|
<% paginated(@experiments).each do |experiment| %>
|
10
12
|
<% if experiment.goals.empty? %>
|
@@ -24,3 +26,16 @@
|
|
24
26
|
<p class="intro">No experiments have started yet, you need to define them in your code and introduce them to your users.</p>
|
25
27
|
<p class="intro">Check out the <a href='https://github.com/splitrb/split#readme'>Readme</a> for more help getting started.</p>
|
26
28
|
<% end %>
|
29
|
+
|
30
|
+
<div class="dashboard-controls dashboard-controls-bottom">
|
31
|
+
<form action="<%= url "/initialize_experiment" %>" method='post'>
|
32
|
+
<label>Add unregistered experiment: </label>
|
33
|
+
<select name="experiment" id="experiment-select">
|
34
|
+
<option selected disabled>experiment</option>
|
35
|
+
<% @unintialized_experiments.sort.each do |experiment_name| %>
|
36
|
+
<option value="<%= experiment_name %>"><%= experiment_name %></option>
|
37
|
+
<% end %>
|
38
|
+
</select>
|
39
|
+
<input type="submit" id="register-experiment-btn" value="register experiment"/>
|
40
|
+
</form>
|
41
|
+
<div>
|
data/lib/split/dashboard.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
3
|
+
require "sinatra/base"
|
4
|
+
require "split"
|
5
|
+
require "bigdecimal"
|
6
|
+
require "split/dashboard/helpers"
|
7
|
+
require "split/dashboard/pagination_helpers"
|
8
8
|
|
9
9
|
module Split
|
10
10
|
class Dashboard < Sinatra::Base
|
@@ -18,14 +18,15 @@ module Split
|
|
18
18
|
helpers Split::DashboardHelpers
|
19
19
|
helpers Split::DashboardPaginationHelpers
|
20
20
|
|
21
|
-
get
|
21
|
+
get "/" do
|
22
22
|
# Display experiments without a winner at the top of the dashboard
|
23
23
|
@experiments = Split::ExperimentCatalog.all_active_first
|
24
|
+
@unintialized_experiments = Split.configuration.experiments.keys - @experiments.map(&:name)
|
24
25
|
|
25
26
|
@metrics = Split::Metric.all
|
26
27
|
|
27
28
|
# Display Rails Environment mode (or Rack version if not using Rails)
|
28
|
-
if Object.const_defined?(
|
29
|
+
if Object.const_defined?("Rails") && Rails.respond_to?(:env)
|
29
30
|
@current_env = Rails.env.titlecase
|
30
31
|
else
|
31
32
|
@current_env = "Rack: #{Rack.version}"
|
@@ -33,43 +34,48 @@ module Split
|
|
33
34
|
erb :index
|
34
35
|
end
|
35
36
|
|
36
|
-
post
|
37
|
+
post "/initialize_experiment" do
|
38
|
+
Split::ExperimentCatalog.find_or_create(params[:experiment]) unless params[:experiment].nil? || params[:experiment].empty?
|
39
|
+
redirect url("/")
|
40
|
+
end
|
41
|
+
|
42
|
+
post "/force_alternative" do
|
37
43
|
experiment = Split::ExperimentCatalog.find(params[:experiment])
|
38
44
|
alternative = Split::Alternative.new(params[:alternative], experiment.name)
|
39
45
|
|
40
|
-
cookies = JSON.parse(request.cookies[
|
46
|
+
cookies = JSON.parse(request.cookies["split_override"]) rescue {}
|
41
47
|
cookies[experiment.name] = alternative.name
|
42
|
-
response.set_cookie(
|
48
|
+
response.set_cookie("split_override", { value: cookies.to_json, path: "/" })
|
43
49
|
|
44
|
-
redirect url(
|
50
|
+
redirect url("/")
|
45
51
|
end
|
46
52
|
|
47
|
-
post
|
53
|
+
post "/experiment" do
|
48
54
|
@experiment = Split::ExperimentCatalog.find(params[:experiment])
|
49
55
|
@alternative = Split::Alternative.new(params[:alternative], params[:experiment])
|
50
56
|
@experiment.winner = @alternative.name
|
51
|
-
redirect url(
|
57
|
+
redirect url("/")
|
52
58
|
end
|
53
59
|
|
54
|
-
post
|
60
|
+
post "/start" do
|
55
61
|
@experiment = Split::ExperimentCatalog.find(params[:experiment])
|
56
62
|
@experiment.start
|
57
|
-
redirect url(
|
63
|
+
redirect url("/")
|
58
64
|
end
|
59
65
|
|
60
|
-
post
|
66
|
+
post "/reset" do
|
61
67
|
@experiment = Split::ExperimentCatalog.find(params[:experiment])
|
62
68
|
@experiment.reset
|
63
|
-
redirect url(
|
69
|
+
redirect url("/")
|
64
70
|
end
|
65
71
|
|
66
|
-
post
|
72
|
+
post "/reopen" do
|
67
73
|
@experiment = Split::ExperimentCatalog.find(params[:experiment])
|
68
74
|
@experiment.reset_winner
|
69
|
-
redirect url(
|
75
|
+
redirect url("/")
|
70
76
|
end
|
71
77
|
|
72
|
-
post
|
78
|
+
post "/update_cohorting" do
|
73
79
|
@experiment = Split::ExperimentCatalog.find(params[:experiment])
|
74
80
|
case params[:cohorting_action].downcase
|
75
81
|
when "enable"
|
@@ -77,13 +83,13 @@ module Split
|
|
77
83
|
when "disable"
|
78
84
|
@experiment.disable_cohorting
|
79
85
|
end
|
80
|
-
redirect url(
|
86
|
+
redirect url("/")
|
81
87
|
end
|
82
88
|
|
83
|
-
delete
|
89
|
+
delete "/experiment" do
|
84
90
|
@experiment = Split::ExperimentCatalog.find(params[:experiment])
|
85
91
|
@experiment.delete
|
86
|
-
redirect url(
|
92
|
+
redirect url("/")
|
87
93
|
end
|
88
94
|
end
|
89
95
|
end
|
@@ -15,7 +15,6 @@ require "split/helper"
|
|
15
15
|
#
|
16
16
|
module Split
|
17
17
|
module EncapsulatedHelper
|
18
|
-
|
19
18
|
class ContextShim
|
20
19
|
include Split::Helper
|
21
20
|
public :ab_test, :ab_finished
|
@@ -34,10 +33,9 @@ module Split
|
|
34
33
|
end
|
35
34
|
|
36
35
|
private
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
36
|
+
# instantiate and memoize a context shim in case of multiple ab_test* calls
|
37
|
+
def split_context_shim
|
38
|
+
@split_context_shim ||= ContextShim.new(self)
|
39
|
+
end
|
42
40
|
end
|
43
41
|
end
|