split 4.0.1 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +6 -3
  3. data/.rubocop.yml +2 -5
  4. data/CHANGELOG.md +23 -0
  5. data/CONTRIBUTING.md +1 -1
  6. data/Gemfile +2 -1
  7. data/README.md +4 -2
  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 +2 -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 +22 -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/index.erb +19 -4
  24. data/lib/split/dashboard.rb +29 -23
  25. data/lib/split/encapsulated_helper.rb +4 -6
  26. data/lib/split/experiment.rb +84 -88
  27. data/lib/split/experiment_catalog.rb +6 -5
  28. data/lib/split/extensions/string.rb +1 -1
  29. data/lib/split/goals_collection.rb +8 -10
  30. data/lib/split/helper.rb +19 -19
  31. data/lib/split/metric.rb +4 -5
  32. data/lib/split/persistence/cookie_adapter.rb +44 -47
  33. data/lib/split/persistence/dual_adapter.rb +7 -8
  34. data/lib/split/persistence/redis_adapter.rb +2 -3
  35. data/lib/split/persistence/session_adapter.rb +0 -2
  36. data/lib/split/persistence.rb +4 -4
  37. data/lib/split/redis_interface.rb +1 -2
  38. data/lib/split/trial.rb +23 -24
  39. data/lib/split/user.rb +12 -13
  40. data/lib/split/version.rb +1 -1
  41. data/lib/split/zscore.rb +1 -3
  42. data/lib/split.rb +26 -25
  43. data/spec/algorithms/block_randomization_spec.rb +6 -5
  44. data/spec/algorithms/weighted_sample_spec.rb +6 -5
  45. data/spec/algorithms/whiplash_spec.rb +4 -5
  46. data/spec/alternative_spec.rb +35 -36
  47. data/spec/cache_spec.rb +15 -19
  48. data/spec/combined_experiments_helper_spec.rb +18 -17
  49. data/spec/configuration_spec.rb +32 -38
  50. data/spec/dashboard/pagination_helpers_spec.rb +69 -67
  51. data/spec/dashboard/paginator_spec.rb +10 -9
  52. data/spec/dashboard_helpers_spec.rb +19 -18
  53. data/spec/dashboard_spec.rb +67 -35
  54. data/spec/encapsulated_helper_spec.rb +12 -14
  55. data/spec/experiment_catalog_spec.rb +14 -13
  56. data/spec/experiment_spec.rb +121 -123
  57. data/spec/goals_collection_spec.rb +17 -15
  58. data/spec/helper_spec.rb +379 -382
  59. data/spec/metric_spec.rb +14 -14
  60. data/spec/persistence/cookie_adapter_spec.rb +23 -8
  61. data/spec/persistence/dual_adapter_spec.rb +71 -71
  62. data/spec/persistence/redis_adapter_spec.rb +25 -26
  63. data/spec/persistence/session_adapter_spec.rb +2 -3
  64. data/spec/persistence_spec.rb +1 -2
  65. data/spec/redis_interface_spec.rb +16 -14
  66. data/spec/spec_helper.rb +15 -13
  67. data/spec/split_spec.rb +11 -11
  68. data/spec/support/cookies_mock.rb +1 -2
  69. data/spec/trial_spec.rb +61 -60
  70. data/spec/user_spec.rb +36 -36
  71. data/split.gemspec +20 -20
  72. metadata +7 -10
  73. data/.rubocop_todo.yml +0 -226
  74. data/Appraisals +0 -19
  75. data/gemfiles/5.0.gemfile +0 -9
  76. data/gemfiles/5.1.gemfile +0 -9
@@ -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;
@@ -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
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rubystats'
4
-
5
3
  module Split
6
4
  class Experiment
7
5
  attr_accessor :name
@@ -13,7 +11,7 @@ module Split
13
11
  attr_reader :resettable
14
12
 
15
13
  DEFAULT_OPTIONS = {
16
- :resettable => true
14
+ resettable: true
17
15
  }
18
16
 
19
17
  def self.find(name)
@@ -52,7 +50,7 @@ module Split
52
50
 
53
51
  if alts.length == 1
54
52
  if alts[0].is_a? Hash
55
- alts = alts[0].map{|k, v| {k => v} }
53
+ alts = alts[0].map { |k, v| { k => v } }
56
54
  end
57
55
  end
58
56
 
@@ -87,7 +85,7 @@ module Split
87
85
  persist_experiment_configuration
88
86
  end
89
87
 
90
- redis.hmset(experiment_config_key, :resettable, resettable,
88
+ redis.hmset(experiment_config_key, :resettable, resettable.to_s,
91
89
  :algorithm, algorithm.to_s)
92
90
  self
93
91
  end
@@ -96,7 +94,7 @@ module Split
96
94
  if @alternatives.empty? && Split.configuration.experiment_for(@name).nil?
97
95
  raise ExperimentNotFound.new("Experiment #{@name} not found")
98
96
  end
99
- @alternatives.each {|a| a.validate! }
97
+ @alternatives.each { |a| a.validate! }
100
98
  goals_collection.validate!
101
99
  end
102
100
 
@@ -109,7 +107,7 @@ module Split
109
107
  end
110
108
 
111
109
  def [](name)
112
- alternatives.find{|a| a.name == name}
110
+ alternatives.find { |a| a.name == name }
113
111
  end
114
112
 
115
113
  def algorithm
@@ -121,7 +119,7 @@ module Split
121
119
  end
122
120
 
123
121
  def resettable=(resettable)
124
- @resettable = resettable.is_a?(String) ? resettable == 'true' : resettable
122
+ @resettable = resettable.is_a?(String) ? resettable == "true" : resettable
125
123
  end
126
124
 
127
125
  def alternatives=(alts)
@@ -157,7 +155,7 @@ module Split
157
155
  end
158
156
 
159
157
  def participant_count
160
- alternatives.inject(0){|sum, a| sum + a.participant_count}
158
+ alternatives.inject(0) { |sum, a| sum + a.participant_count }
161
159
  end
162
160
 
163
161
  def control
@@ -262,8 +260,8 @@ module Split
262
260
  exp_config = redis.hgetall(experiment_config_key)
263
261
 
264
262
  options = {
265
- resettable: exp_config['resettable'],
266
- algorithm: exp_config['algorithm'],
263
+ resettable: exp_config["resettable"],
264
+ algorithm: exp_config["algorithm"],
267
265
  alternatives: load_alternatives_from_redis,
268
266
  goals: Split::GoalsCollection.new(@name).load_from_redis,
269
267
  metadata: load_metadata_from_redis
@@ -328,11 +326,11 @@ module Split
328
326
  winning_counts.each do |alternative, wins|
329
327
  alternative_probabilities[alternative] = wins / number_of_simulations.to_f
330
328
  end
331
- return alternative_probabilities
329
+ alternative_probabilities
332
330
  end
333
331
 
334
332
  def count_simulated_wins(winning_alternatives)
335
- # initialize a hash to keep track of winning alternative in simulations
333
+ # initialize a hash to keep track of winning alternative in simulations
336
334
  winning_counts = {}
337
335
  alternatives.each do |alternative|
338
336
  winning_counts[alternative] = 0
@@ -341,7 +339,7 @@ module Split
341
339
  winning_alternatives.each do |alternative|
342
340
  winning_counts[alternative] += 1
343
341
  end
344
- return winning_counts
342
+ winning_counts
345
343
  end
346
344
 
347
345
  def find_simulated_winner(simulated_cr_hash)
@@ -353,7 +351,7 @@ module Split
353
351
  end
354
352
  end
355
353
  winner = winning_pair[0]
356
- return winner
354
+ winner
357
355
  end
358
356
 
359
357
  def calc_simulated_conversion_rates(beta_params)
@@ -363,11 +361,11 @@ module Split
363
361
  beta_params.each do |alternative, params|
364
362
  alpha = params[0]
365
363
  beta = params[1]
366
- simulated_conversion_rate = Rubystats::BetaDistribution.new(alpha, beta).rng
364
+ simulated_conversion_rate = Split::Algorithms.beta_distribution_rng(alpha, beta)
367
365
  simulated_cr_hash[alternative] = simulated_conversion_rate
368
366
  end
369
367
 
370
- return simulated_cr_hash
368
+ simulated_cr_hash
371
369
  end
372
370
 
373
371
  def calc_beta_params(goal = nil)
@@ -381,7 +379,7 @@ module Split
381
379
 
382
380
  beta_params[alternative] = params
383
381
  end
384
- return beta_params
382
+ beta_params
385
383
  end
386
384
 
387
385
  def calc_time=(time)
@@ -394,11 +392,11 @@ module Split
394
392
 
395
393
  def jstring(goal = nil)
396
394
  js_id = if goal.nil?
397
- name
398
- else
399
- name + "-" + goal
400
- end
401
- js_id.gsub('/', '--')
395
+ name
396
+ else
397
+ name + "-" + goal
398
+ end
399
+ js_id.gsub("/", "--")
402
400
  end
403
401
 
404
402
  def cohorting_disabled?
@@ -410,95 +408,93 @@ module Split
410
408
 
411
409
  def disable_cohorting
412
410
  @cohorting_disabled = true
413
- redis.hset(experiment_config_key, :cohorting, true)
411
+ redis.hset(experiment_config_key, :cohorting, true.to_s)
414
412
  end
415
413
 
416
414
  def enable_cohorting
417
415
  @cohorting_disabled = false
418
- redis.hset(experiment_config_key, :cohorting, false)
416
+ redis.hset(experiment_config_key, :cohorting, false.to_s)
419
417
  end
420
418
 
421
419
  protected
420
+ def experiment_config_key
421
+ "experiment_configurations/#{@name}"
422
+ end
422
423
 
423
- def experiment_config_key
424
- "experiment_configurations/#{@name}"
425
- end
426
-
427
- def load_metadata_from_configuration
428
- Split.configuration.experiment_for(@name)[:metadata]
429
- end
424
+ def load_metadata_from_configuration
425
+ Split.configuration.experiment_for(@name)[:metadata]
426
+ end
430
427
 
431
- def load_metadata_from_redis
432
- meta = redis.get(metadata_key)
433
- JSON.parse(meta) unless meta.nil?
434
- end
428
+ def load_metadata_from_redis
429
+ meta = redis.get(metadata_key)
430
+ JSON.parse(meta) unless meta.nil?
431
+ end
435
432
 
436
- def load_alternatives_from_configuration
437
- alts = Split.configuration.experiment_for(@name)[:alternatives]
438
- raise ArgumentError, "Experiment configuration is missing :alternatives array" unless alts
439
- if alts.is_a?(Hash)
440
- alts.keys
441
- else
442
- alts.flatten
433
+ def load_alternatives_from_configuration
434
+ alts = Split.configuration.experiment_for(@name)[:alternatives]
435
+ raise ArgumentError, "Experiment configuration is missing :alternatives array" unless alts
436
+ if alts.is_a?(Hash)
437
+ alts.keys
438
+ else
439
+ alts.flatten
440
+ end
443
441
  end
444
- end
445
442
 
446
- def load_alternatives_from_redis
447
- alternatives = redis.lrange(@name, 0, -1)
448
- alternatives.map do |alt|
449
- alt = begin
450
- JSON.parse(alt)
451
- rescue
452
- alt
453
- end
454
- Split::Alternative.new(alt, @name)
443
+ def load_alternatives_from_redis
444
+ alternatives = redis.lrange(@name, 0, -1)
445
+ alternatives.map do |alt|
446
+ alt = begin
447
+ JSON.parse(alt)
448
+ rescue
449
+ alt
450
+ end
451
+ Split::Alternative.new(alt, @name)
452
+ end
455
453
  end
456
- end
457
454
 
458
455
  private
456
+ def redis
457
+ Split.redis
458
+ end
459
459
 
460
- def redis
461
- Split.redis
462
- end
460
+ def redis_interface
461
+ RedisInterface.new
462
+ end
463
463
 
464
- def redis_interface
465
- RedisInterface.new
466
- end
464
+ def persist_experiment_configuration
465
+ redis_interface.add_to_set(:experiments, name)
466
+ redis_interface.persist_list(name, @alternatives.map { |alt| { alt.name => alt.weight }.to_json })
467
+ goals_collection.save
467
468
 
468
- def persist_experiment_configuration
469
- redis_interface.add_to_set(:experiments, name)
470
- redis_interface.persist_list(name, @alternatives.map{|alt| {alt.name => alt.weight}.to_json})
471
- goals_collection.save
469
+ if @metadata
470
+ redis.set(metadata_key, @metadata.to_json)
471
+ else
472
+ delete_metadata
473
+ end
474
+ end
472
475
 
473
- if @metadata
474
- redis.set(metadata_key, @metadata.to_json)
475
- else
476
+ def remove_experiment_configuration
477
+ @alternatives.each(&:delete)
478
+ goals_collection.delete
476
479
  delete_metadata
480
+ redis.del(@name)
477
481
  end
478
- end
479
482
 
480
- def remove_experiment_configuration
481
- @alternatives.each(&:delete)
482
- goals_collection.delete
483
- delete_metadata
484
- redis.del(@name)
485
- end
483
+ def experiment_configuration_has_changed?
484
+ existing_experiment = Experiment.find(@name)
486
485
 
487
- def experiment_configuration_has_changed?
488
- existing_experiment = Experiment.find(@name)
489
-
490
- existing_experiment.alternatives.map(&:to_s) != @alternatives.map(&:to_s) ||
491
- existing_experiment.goals != @goals ||
492
- existing_experiment.metadata != @metadata
493
- end
486
+ existing_experiment.alternatives.map(&:to_s) != @alternatives.map(&:to_s) ||
487
+ existing_experiment.goals != @goals ||
488
+ existing_experiment.metadata != @metadata
489
+ end
494
490
 
495
- def goals_collection
496
- Split::GoalsCollection.new(@name, @goals)
497
- end
491
+ def goals_collection
492
+ Split::GoalsCollection.new(@name, @goals)
493
+ end
498
494
 
499
- def remove_experiment_cohorting
500
- @cohorting_disabled = false
501
- redis.hdel(experiment_config_key, :cohorting)
502
- end
495
+ def remove_experiment_cohorting
496
+ @cohorting_disabled = false
497
+ redis.hdel(experiment_config_key, :cohorting)
498
+ end
503
499
  end
504
500
  end