split 4.0.1 → 4.0.3
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.
- 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
|