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
@@ -5,7 +5,6 @@ require "json"
|
|
5
5
|
module Split
|
6
6
|
module Persistence
|
7
7
|
class CookieAdapter
|
8
|
-
|
9
8
|
def initialize(context)
|
10
9
|
@context = context
|
11
10
|
@request, @response = context.request, context.response
|
@@ -30,50 +29,49 @@ module Split
|
|
30
29
|
end
|
31
30
|
|
32
31
|
private
|
32
|
+
def set_cookie(value = {})
|
33
|
+
cookie_key = :split.to_s
|
34
|
+
cookie_value = default_options.merge(value: JSON.generate(value))
|
35
|
+
if action_dispatch?
|
36
|
+
# The "send" is necessary when we call ab_test from the controller
|
37
|
+
# and thus @context is a rails controller, because then "cookies" is
|
38
|
+
# a private method.
|
39
|
+
@context.send(:cookies)[cookie_key] = cookie_value
|
40
|
+
else
|
41
|
+
set_cookie_via_rack(cookie_key, cookie_value)
|
42
|
+
end
|
43
|
+
end
|
33
44
|
|
34
|
-
|
35
|
-
|
36
|
-
cookie_value = default_options.merge(value: JSON.generate(value))
|
37
|
-
if action_dispatch?
|
38
|
-
# The "send" is necessary when we call ab_test from the controller
|
39
|
-
# and thus @context is a rails controller, because then "cookies" is
|
40
|
-
# a private method.
|
41
|
-
@context.send(:cookies)[cookie_key] = cookie_value
|
42
|
-
else
|
43
|
-
set_cookie_via_rack(cookie_key, cookie_value)
|
45
|
+
def default_options
|
46
|
+
{ expires: @expires, path: "/", domain: cookie_domain_config }.compact
|
44
47
|
end
|
45
|
-
end
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
def set_cookie_via_rack(key, value)
|
50
|
+
delete_cookie_header!(@response.header, key, value)
|
51
|
+
Rack::Utils.set_cookie_header!(@response.header, key, value)
|
52
|
+
end
|
50
53
|
|
51
|
-
|
52
|
-
delete_cookie_header!(
|
53
|
-
|
54
|
-
|
54
|
+
# Use Rack::Utils#make_delete_cookie_header after Rack 2.0.0
|
55
|
+
def delete_cookie_header!(header, key, value)
|
56
|
+
cookie_header = header["Set-Cookie"]
|
57
|
+
case cookie_header
|
58
|
+
when nil, ""
|
59
|
+
cookies = []
|
60
|
+
when String
|
61
|
+
cookies = cookie_header.split("\n")
|
62
|
+
when Array
|
63
|
+
cookies = cookie_header
|
64
|
+
end
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
cookie_header = header['Set-Cookie']
|
59
|
-
case cookie_header
|
60
|
-
when nil, ''
|
61
|
-
cookies = []
|
62
|
-
when String
|
63
|
-
cookies = cookie_header.split("\n")
|
64
|
-
when Array
|
65
|
-
cookies = cookie_header
|
66
|
+
cookies.reject! { |cookie| cookie =~ /\A#{Rack::Utils.escape(key)}=/ }
|
67
|
+
header["Set-Cookie"] = cookies.join("\n")
|
66
68
|
end
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
def hash
|
73
|
-
@hash ||= begin
|
74
|
-
if cookies = @cookies[:split.to_s]
|
70
|
+
def hash
|
71
|
+
@hash ||= if cookies = @cookies[:split.to_s]
|
75
72
|
begin
|
76
|
-
JSON.parse(cookies)
|
73
|
+
parsed = JSON.parse(cookies)
|
74
|
+
parsed.is_a?(Hash) ? parsed : {}
|
77
75
|
rescue JSON::ParserError
|
78
76
|
{}
|
79
77
|
end
|
@@ -81,19 +79,18 @@ module Split
|
|
81
79
|
{}
|
82
80
|
end
|
83
81
|
end
|
84
|
-
end
|
85
82
|
|
86
|
-
|
87
|
-
|
88
|
-
|
83
|
+
def cookie_length_config
|
84
|
+
Split.configuration.persistence_cookie_length
|
85
|
+
end
|
89
86
|
|
90
|
-
|
91
|
-
|
92
|
-
|
87
|
+
def cookie_domain_config
|
88
|
+
Split.configuration.persistence_cookie_domain
|
89
|
+
end
|
93
90
|
|
94
|
-
|
95
|
-
|
96
|
-
|
91
|
+
def action_dispatch?
|
92
|
+
defined?(Rails) && @response.is_a?(ActionDispatch::Response)
|
93
|
+
end
|
97
94
|
end
|
98
95
|
end
|
99
96
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Split
|
4
4
|
module Persistence
|
5
5
|
class DualAdapter
|
6
|
-
def self.with_config(options={})
|
6
|
+
def self.with_config(options = {})
|
7
7
|
self.config.merge!(options)
|
8
8
|
self
|
9
9
|
end
|
@@ -72,14 +72,13 @@ module Split
|
|
72
72
|
end
|
73
73
|
|
74
74
|
private
|
75
|
+
def decrement_participation?(old_value, value)
|
76
|
+
!old_value.nil? && !value.nil? && old_value != value
|
77
|
+
end
|
75
78
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
def decrement_participation(key, value)
|
81
|
-
Split.redis.hincrby("#{key}:#{value}", 'participant_count', -1)
|
82
|
-
end
|
79
|
+
def decrement_participation(key, value)
|
80
|
+
Split.redis.hincrby("#{key}:#{value}", "participant_count", -1)
|
81
|
+
end
|
83
82
|
end
|
84
83
|
end
|
85
84
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Split
|
4
4
|
module Persistence
|
5
5
|
class RedisAdapter
|
6
|
-
DEFAULT_CONFIG = {:
|
6
|
+
DEFAULT_CONFIG = { namespace: "persistence" }.freeze
|
7
7
|
|
8
8
|
attr_reader :redis_key
|
9
9
|
|
@@ -27,7 +27,7 @@ module Split
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def []=(field, value)
|
30
|
-
Split.redis.hset(redis_key, field, value)
|
30
|
+
Split.redis.hset(redis_key, field, value.to_s)
|
31
31
|
expire_seconds = self.class.config[:expire_seconds]
|
32
32
|
Split.redis.expire(redis_key, expire_seconds) if expire_seconds
|
33
33
|
end
|
@@ -44,7 +44,7 @@ module Split
|
|
44
44
|
new(nil, user_id)
|
45
45
|
end
|
46
46
|
|
47
|
-
def self.with_config(options={})
|
47
|
+
def self.with_config(options = {})
|
48
48
|
self.config.merge!(options)
|
49
49
|
self
|
50
50
|
end
|
@@ -56,7 +56,6 @@ module Split
|
|
56
56
|
def self.reset_config!
|
57
57
|
@config = DEFAULT_CONFIG.dup
|
58
58
|
end
|
59
|
-
|
60
59
|
end
|
61
60
|
end
|
62
61
|
end
|
data/lib/split/persistence.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module Split
|
4
4
|
module Persistence
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
5
|
+
require "split/persistence/cookie_adapter"
|
6
|
+
require "split/persistence/dual_adapter"
|
7
|
+
require "split/persistence/redis_adapter"
|
8
|
+
require "split/persistence/session_adapter"
|
9
9
|
|
10
10
|
ADAPTERS = {
|
11
11
|
cookie: Split::Persistence::CookieAdapter,
|
@@ -11,6 +11,7 @@ module Split
|
|
11
11
|
if list_values.length > 0
|
12
12
|
redis.multi do |multi|
|
13
13
|
tmp_list = "#{list_name}_tmp"
|
14
|
+
tmp_list += redis_namespace_used? ? "{#{Split.redis.namespace}:#{list_name}}" : "{#{list_name}}"
|
14
15
|
multi.rpush(tmp_list, list_values)
|
15
16
|
multi.rename(tmp_list, list_name)
|
16
17
|
end
|
@@ -20,11 +21,16 @@ module Split
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def add_to_set(set_name, value)
|
24
|
+
return redis.sadd?(set_name, value) if redis.respond_to?(:sadd?)
|
25
|
+
|
23
26
|
redis.sadd(set_name, value)
|
24
27
|
end
|
25
28
|
|
26
29
|
private
|
30
|
+
attr_accessor :redis
|
27
31
|
|
28
|
-
|
32
|
+
def redis_namespace_used?
|
33
|
+
Redis.const_defined?("Namespace") && Split.redis.is_a?(Redis::Namespace)
|
34
|
+
end
|
29
35
|
end
|
30
36
|
end
|
data/lib/split/trial.rb
CHANGED
@@ -24,15 +24,15 @@ module Split
|
|
24
24
|
|
25
25
|
def alternative
|
26
26
|
@alternative ||= if @experiment.has_winner?
|
27
|
-
|
28
|
-
|
27
|
+
@experiment.winner
|
28
|
+
end
|
29
29
|
end
|
30
30
|
|
31
31
|
def alternative=(alternative)
|
32
32
|
@alternative = if alternative.kind_of?(Split::Alternative)
|
33
33
|
alternative
|
34
34
|
else
|
35
|
-
@experiment.alternatives.find{|a| a.name == alternative }
|
35
|
+
@experiment.alternatives.find { |a| a.name == alternative }
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -41,7 +41,7 @@ module Split
|
|
41
41
|
if Array(goals).empty?
|
42
42
|
alternative.increment_completion
|
43
43
|
else
|
44
|
-
Array(goals).each {|g| alternative.increment_completion(g) }
|
44
|
+
Array(goals).each { |g| alternative.increment_completion(g) }
|
45
45
|
end
|
46
46
|
|
47
47
|
run_callback context, Split.configuration.on_trial_complete
|
@@ -97,31 +97,30 @@ module Split
|
|
97
97
|
end
|
98
98
|
|
99
99
|
private
|
100
|
+
def run_callback(context, callback_name)
|
101
|
+
context.send(callback_name, self) if callback_name && context.respond_to?(callback_name, true)
|
102
|
+
end
|
100
103
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
def override_is_alternative?
|
106
|
-
@experiment.alternatives.map(&:name).include?(@options[:override])
|
107
|
-
end
|
104
|
+
def override_is_alternative?
|
105
|
+
@experiment.alternatives.map(&:name).include?(@options[:override])
|
106
|
+
end
|
108
107
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
108
|
+
def should_store_alternative?
|
109
|
+
if @options[:override] || @options[:disabled]
|
110
|
+
Split.configuration.store_override
|
111
|
+
else
|
112
|
+
!exclude_user?
|
113
|
+
end
|
114
114
|
end
|
115
|
-
end
|
116
115
|
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
def cleanup_old_versions
|
117
|
+
if @experiment.version > 0
|
118
|
+
@user.cleanup_old_versions!(@experiment)
|
119
|
+
end
|
120
120
|
end
|
121
|
-
end
|
122
121
|
|
123
|
-
|
124
|
-
|
125
|
-
|
122
|
+
def exclude_user?
|
123
|
+
@options[:exclude] || @experiment.start_time.nil? || @user.max_experiments_reached?(@experiment.key)
|
124
|
+
end
|
126
125
|
end
|
127
126
|
end
|
data/lib/split/user.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "forwardable"
|
4
4
|
|
5
5
|
module Split
|
6
6
|
class User
|
@@ -26,10 +26,10 @@ module Split
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def max_experiments_reached?(experiment_key)
|
29
|
-
if Split.configuration.allow_multiple_experiments ==
|
29
|
+
if Split.configuration.allow_multiple_experiments == "control"
|
30
30
|
experiments = active_experiments
|
31
31
|
experiment_key_without_version = key_without_version(experiment_key)
|
32
|
-
count_control = experiments.count {|k, v| k == experiment_key_without_version || v ==
|
32
|
+
count_control = experiments.count { |k, v| k == experiment_key_without_version || v == "control" }
|
33
33
|
experiments.size > count_control
|
34
34
|
else
|
35
35
|
!Split.configuration.allow_multiple_experiments &&
|
@@ -65,17 +65,16 @@ module Split
|
|
65
65
|
end
|
66
66
|
|
67
67
|
private
|
68
|
+
def keys_without_experiment(keys, experiment_key)
|
69
|
+
keys.reject { |k| k.match(Regexp.new("^#{experiment_key}(:finished)?$")) }
|
70
|
+
end
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def keys_without_finished(keys)
|
74
|
-
keys.reject { |k| k.include?(":finished") }
|
75
|
-
end
|
72
|
+
def keys_without_finished(keys)
|
73
|
+
keys.reject { |k| k.include?(":finished") }
|
74
|
+
end
|
76
75
|
|
77
|
-
|
78
|
-
|
79
|
-
|
76
|
+
def key_without_version(key)
|
77
|
+
key.split(/\:\d(?!\:)/)[0]
|
78
|
+
end
|
80
79
|
end
|
81
80
|
end
|
data/lib/split/version.rb
CHANGED
data/lib/split/zscore.rb
CHANGED
data/lib/split.rb
CHANGED
@@ -1,29 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "redis"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
24
|
-
require
|
25
|
-
require
|
26
|
-
require
|
5
|
+
require "split/algorithms"
|
6
|
+
require "split/algorithms/block_randomization"
|
7
|
+
require "split/algorithms/weighted_sample"
|
8
|
+
require "split/algorithms/whiplash"
|
9
|
+
require "split/alternative"
|
10
|
+
require "split/cache"
|
11
|
+
require "split/configuration"
|
12
|
+
require "split/encapsulated_helper"
|
13
|
+
require "split/exceptions"
|
14
|
+
require "split/experiment"
|
15
|
+
require "split/experiment_catalog"
|
16
|
+
require "split/extensions/string"
|
17
|
+
require "split/goals_collection"
|
18
|
+
require "split/helper"
|
19
|
+
require "split/combined_experiments_helper"
|
20
|
+
require "split/metric"
|
21
|
+
require "split/persistence"
|
22
|
+
require "split/redis_interface"
|
23
|
+
require "split/trial"
|
24
|
+
require "split/user"
|
25
|
+
require "split/version"
|
26
|
+
require "split/zscore"
|
27
|
+
require "split/engine" if defined?(Rails)
|
27
28
|
|
28
29
|
module Split
|
29
30
|
extend self
|
@@ -75,8 +76,8 @@ end
|
|
75
76
|
# Check to see if being run in a Rails application. If so, wait until before_initialize to run configuration so Gems that create ENV variables have the chance to initialize first.
|
76
77
|
if defined?(::Rails)
|
77
78
|
class Split::Railtie < Rails::Railtie
|
78
|
-
config.before_initialize { Split.configure {} }
|
79
|
+
config.before_initialize { Split.configure { } }
|
79
80
|
end
|
80
81
|
else
|
81
|
-
Split.configure {}
|
82
|
+
Split.configure { }
|
82
83
|
end
|
@@ -1,11 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "spec_helper"
|
2
4
|
|
3
5
|
describe Split::Algorithms::BlockRandomization do
|
4
|
-
|
5
|
-
let(:
|
6
|
-
let(:
|
7
|
-
let(:
|
8
|
-
let(:alternative_C) { Split::Alternative.new 'C', 'experiment' }
|
6
|
+
let(:experiment) { Split::Experiment.new "experiment" }
|
7
|
+
let(:alternative_A) { Split::Alternative.new "A", "experiment" }
|
8
|
+
let(:alternative_B) { Split::Alternative.new "B", "experiment" }
|
9
|
+
let(:alternative_C) { Split::Alternative.new "C", "experiment" }
|
9
10
|
|
10
11
|
before :each do
|
11
12
|
allow(experiment).to receive(:alternatives) { [alternative_A, alternative_B, alternative_C] }
|
@@ -1,19 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "spec_helper"
|
3
4
|
|
4
5
|
describe Split::Algorithms::WeightedSample do
|
5
6
|
it "should return an alternative" do
|
6
|
-
experiment = Split::ExperimentCatalog.find_or_create(
|
7
|
+
experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 100 }, { "red" => 0 })
|
7
8
|
expect(Split::Algorithms::WeightedSample.choose_alternative(experiment).class).to eq(Split::Alternative)
|
8
9
|
end
|
9
10
|
|
10
11
|
it "should always return a heavily weighted option" do
|
11
|
-
experiment = Split::ExperimentCatalog.find_or_create(
|
12
|
-
expect(Split::Algorithms::WeightedSample.choose_alternative(experiment).name).to eq(
|
12
|
+
experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 100 }, { "red" => 0 })
|
13
|
+
expect(Split::Algorithms::WeightedSample.choose_alternative(experiment).name).to eq("blue")
|
13
14
|
end
|
14
15
|
|
15
16
|
it "should return one of the results" do
|
16
|
-
experiment = Split::ExperimentCatalog.find_or_create(
|
17
|
-
expect([
|
17
|
+
experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 1 }, { "red" => 1 })
|
18
|
+
expect(["red", "blue"]).to include Split::Algorithms::WeightedSample.choose_alternative(experiment).name
|
18
19
|
end
|
19
20
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "spec_helper"
|
3
4
|
|
4
5
|
describe Split::Algorithms::Whiplash do
|
5
|
-
|
6
6
|
it "should return an algorithm" do
|
7
|
-
experiment = Split::ExperimentCatalog.find_or_create(
|
7
|
+
experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 1 }, { "red" => 1 })
|
8
8
|
expect(Split::Algorithms::Whiplash.choose_alternative(experiment).class).to eq(Split::Alternative)
|
9
9
|
end
|
10
10
|
|
11
11
|
it "should return one of the results" do
|
12
|
-
experiment = Split::ExperimentCatalog.find_or_create(
|
13
|
-
expect([
|
12
|
+
experiment = Split::ExperimentCatalog.find_or_create("link_color", { "blue" => 1 }, { "red" => 1 })
|
13
|
+
expect(["red", "blue"]).to include Split::Algorithms::Whiplash.choose_alternative(experiment).name
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should guess floats" do
|
@@ -20,5 +20,4 @@ describe Split::Algorithms::Whiplash do
|
|
20
20
|
expect(Split::Algorithms::Whiplash.send(:arm_guess, 1000, 5).class).to eq(Float)
|
21
21
|
expect(Split::Algorithms::Whiplash.send(:arm_guess, 10, -2).class).to eq(Float)
|
22
22
|
end
|
23
|
-
|
24
23
|
end
|