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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +8 -3
  3. data/.rubocop.yml +2 -5
  4. data/CHANGELOG.md +38 -0
  5. data/CONTRIBUTING.md +1 -1
  6. data/Gemfile +1 -1
  7. data/README.md +11 -3
  8. data/Rakefile +4 -5
  9. data/gemfiles/5.2.gemfile +1 -3
  10. data/gemfiles/6.0.gemfile +1 -3
  11. data/gemfiles/6.1.gemfile +1 -3
  12. data/gemfiles/7.0.gemfile +1 -3
  13. data/lib/split/algorithms/block_randomization.rb +5 -6
  14. data/lib/split/algorithms/whiplash.rb +16 -18
  15. data/lib/split/algorithms.rb +14 -0
  16. data/lib/split/alternative.rb +21 -22
  17. data/lib/split/cache.rb +0 -1
  18. data/lib/split/combined_experiments_helper.rb +4 -4
  19. data/lib/split/configuration.rb +83 -84
  20. data/lib/split/dashboard/helpers.rb +6 -7
  21. data/lib/split/dashboard/pagination_helpers.rb +53 -54
  22. data/lib/split/dashboard/public/style.css +5 -2
  23. data/lib/split/dashboard/views/_experiment.erb +2 -1
  24. data/lib/split/dashboard/views/index.erb +19 -4
  25. data/lib/split/dashboard.rb +29 -23
  26. data/lib/split/encapsulated_helper.rb +4 -6
  27. data/lib/split/experiment.rb +93 -88
  28. data/lib/split/experiment_catalog.rb +6 -5
  29. data/lib/split/extensions/string.rb +1 -1
  30. data/lib/split/goals_collection.rb +8 -10
  31. data/lib/split/helper.rb +20 -20
  32. data/lib/split/metric.rb +4 -5
  33. data/lib/split/persistence/cookie_adapter.rb +44 -47
  34. data/lib/split/persistence/dual_adapter.rb +7 -8
  35. data/lib/split/persistence/redis_adapter.rb +3 -4
  36. data/lib/split/persistence/session_adapter.rb +0 -2
  37. data/lib/split/persistence.rb +4 -4
  38. data/lib/split/redis_interface.rb +7 -1
  39. data/lib/split/trial.rb +23 -24
  40. data/lib/split/user.rb +12 -13
  41. data/lib/split/version.rb +1 -1
  42. data/lib/split/zscore.rb +1 -3
  43. data/lib/split.rb +26 -25
  44. data/spec/algorithms/block_randomization_spec.rb +6 -5
  45. data/spec/algorithms/weighted_sample_spec.rb +6 -5
  46. data/spec/algorithms/whiplash_spec.rb +4 -5
  47. data/spec/alternative_spec.rb +35 -36
  48. data/spec/cache_spec.rb +15 -19
  49. data/spec/combined_experiments_helper_spec.rb +18 -17
  50. data/spec/configuration_spec.rb +32 -38
  51. data/spec/dashboard/pagination_helpers_spec.rb +69 -67
  52. data/spec/dashboard/paginator_spec.rb +10 -9
  53. data/spec/dashboard_helpers_spec.rb +19 -18
  54. data/spec/dashboard_spec.rb +79 -35
  55. data/spec/encapsulated_helper_spec.rb +12 -14
  56. data/spec/experiment_catalog_spec.rb +14 -13
  57. data/spec/experiment_spec.rb +132 -123
  58. data/spec/goals_collection_spec.rb +17 -15
  59. data/spec/helper_spec.rb +415 -382
  60. data/spec/metric_spec.rb +14 -14
  61. data/spec/persistence/cookie_adapter_spec.rb +23 -8
  62. data/spec/persistence/dual_adapter_spec.rb +71 -71
  63. data/spec/persistence/redis_adapter_spec.rb +28 -29
  64. data/spec/persistence/session_adapter_spec.rb +2 -3
  65. data/spec/persistence_spec.rb +1 -2
  66. data/spec/redis_interface_spec.rb +26 -14
  67. data/spec/spec_helper.rb +16 -13
  68. data/spec/split_spec.rb +11 -11
  69. data/spec/support/cookies_mock.rb +1 -2
  70. data/spec/trial_spec.rb +61 -60
  71. data/spec/user_spec.rb +36 -36
  72. data/split.gemspec +21 -20
  73. metadata +25 -14
  74. data/.rubocop_todo.yml +0 -226
  75. data/Appraisals +0 -19
  76. data/gemfiles/5.0.gemfile +0 -9
  77. data/gemfiles/5.1.gemfile +0 -9
@@ -39,83 +39,83 @@ module Split
39
39
  def bots
40
40
  @bots ||= {
41
41
  # Indexers
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',
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
- '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',
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
- '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',
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
- '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',
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
- 'DigitalPersona Fingerprint Software' => 'HP Fingerprint scanner',
111
- 'ShowyouBot' => 'Showyou iOS app spider',
112
- 'ZyBorg' => 'Zyborg? Hmmm....',
113
- 'ELB-HealthChecker' => 'ELB Health Check'
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= experiments
118
- raise InvalidExperimentsFormatError.new('Experiments must be a Hash') unless experiments.respond_to?(:keys)
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
- normalize_alternatives(alts)
161
- end
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('REDIS_PROVIDER', 'REDIS_URL'), 'redis://localhost:6379')
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
- def value_for(hash, key)
242
- if hash.kind_of?(Hash)
243
- hash.has_key?(key.to_s) ? hash[key.to_s] : hash[key.to_sym]
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
- def escaped_bots
248
- bots.map { |key, _| Regexp.escape(key) }
249
- end
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['SCRIPT_NAME']
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
- '99% confidence'
35
+ "99% confidence"
36
36
  elsif z >= 1.96
37
- '95% confidence'
37
+ "95% confidence"
38
38
  elsif z >= 1.65
39
- '90% confidence'
39
+ "90% confidence"
40
40
  else
41
- 'Insufficient confidence'
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 'split/dashboard/paginator'
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
- def show_first_page_tag?
35
- page_number > 2
36
- end
37
-
38
- def first_page_tag
39
- %Q(<a href="#{url.chop}?page=1&per=#{pagination_per}">1</a>)
40
- end
41
-
42
- def show_first_ellipsis_tag?
43
- page_number >= 4
44
- end
45
-
46
- def ellipsis_tag
47
- '<span>...</span>'
48
- end
49
-
50
- def show_prev_page_tag?
51
- page_number > 1
52
- end
53
-
54
- def prev_page_tag
55
- %Q(<a href="#{url.chop}?page=#{page_number - 1}&per=#{pagination_per}">#{page_number - 1}</a>)
56
- end
57
-
58
- def current_page_tag
59
- "<span><b>#{page_number}</b></span>"
60
- end
61
-
62
- def show_next_page_tag?(collection)
63
- (page_number * pagination_per) < collection.count
64
- end
65
-
66
- def next_page_tag
67
- %Q(<a href="#{url.chop}?page=#{page_number + 1}&per=#{pagination_per}">#{page_number + 1}</a>)
68
- end
69
-
70
- def show_last_ellipsis_tag?(collection)
71
- (total_pages(collection) - page_number) >= 3
72
- end
73
-
74
- def total_pages(collection)
75
- collection.count / pagination_per + ((collection.count % pagination_per).zero? ? 0 : 1)
76
- end
77
-
78
- def show_last_page_tag?(collection)
79
- page_number < (total_pages(collection) - 1)
80
- end
81
-
82
- def last_page_tag(collection)
83
- total = total_pages(collection)
84
- %Q(<a href="#{url.chop}?page=#{total}&per=#{pagination_per}">#{total}</a>)
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
- #filter, #clear-filter {
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
- if extra_infos[0][column].kind_of?(Numeric)
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
- <input type="text" placeholder="Begin typing to filter" id="filter" />
5
- <input type="button" id="toggle-completed" value="Hide completed" />
6
- <input type="button" id="toggle-active" value="Hide active" />
7
- <input type="button" id="clear-filter" value="Clear filters" />
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>
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'sinatra/base'
4
- require 'split'
5
- require 'bigdecimal'
6
- require 'split/dashboard/helpers'
7
- require 'split/dashboard/pagination_helpers'
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 '/' do
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?('Rails')
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 '/force_alternative' do
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['split_override']) rescue {}
46
+ cookies = JSON.parse(request.cookies["split_override"]) rescue {}
41
47
  cookies[experiment.name] = alternative.name
42
- response.set_cookie('split_override', { value: cookies.to_json, path: '/' })
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 '/experiment' do
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 '/start' do
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 '/reset' do
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 '/reopen' do
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 '/update_cohorting' do
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 '/experiment' do
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
- # instantiate and memoize a context shim in case of multiple ab_test* calls
39
- def split_context_shim
40
- @split_context_shim ||= ContextShim.new(self)
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