split 4.0.0.pre2 → 4.0.2
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 +14 -1
- data/.rubocop.yml +2 -5
- data/CHANGELOG.md +26 -2
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +2 -1
- data/README.md +4 -2
- data/Rakefile +4 -5
- data/gemfiles/5.2.gemfile +1 -3
- data/gemfiles/6.0.gemfile +1 -3
- data/gemfiles/{5.0.gemfile → 6.1.gemfile} +2 -4
- data/gemfiles/{5.1.gemfile → 7.0.gemfile} +3 -4
- data/lib/split/algorithms/block_randomization.rb +5 -6
- data/lib/split/algorithms/whiplash.rb +16 -18
- data/lib/split/algorithms.rb +22 -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/index.erb +19 -4
- data/lib/split/dashboard.rb +29 -23
- data/lib/split/encapsulated_helper.rb +4 -6
- data/lib/split/experiment.rb +84 -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 +19 -19
- 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 +2 -3
- data/lib/split/persistence/session_adapter.rb +0 -2
- data/lib/split/persistence.rb +4 -4
- data/lib/split/redis_interface.rb +1 -2
- 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 +67 -35
- data/spec/encapsulated_helper_spec.rb +12 -14
- data/spec/experiment_catalog_spec.rb +14 -13
- data/spec/experiment_spec.rb +121 -123
- data/spec/goals_collection_spec.rb +17 -15
- data/spec/helper_spec.rb +379 -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 +25 -26
- data/spec/persistence/session_adapter_spec.rb +2 -3
- data/spec/persistence_spec.rb +1 -2
- data/spec/redis_interface_spec.rb +16 -14
- data/spec/spec_helper.rb +15 -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 +20 -20
- metadata +9 -10
- data/.rubocop_todo.yml +0 -226
- data/Appraisals +0 -19
@@ -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
|
|
@@ -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,
|
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
|
data/spec/alternative_spec.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
|
-
require 'split/alternative'
|
4
2
|
|
5
|
-
|
3
|
+
require "spec_helper"
|
4
|
+
require "split/alternative"
|
6
5
|
|
6
|
+
describe Split::Alternative do
|
7
7
|
let(:alternative) {
|
8
|
-
Split::Alternative.new(
|
8
|
+
Split::Alternative.new("Basket", "basket_text")
|
9
9
|
}
|
10
10
|
|
11
11
|
let(:alternative2) {
|
12
|
-
Split::Alternative.new(
|
12
|
+
Split::Alternative.new("Cart", "basket_text")
|
13
13
|
}
|
14
14
|
|
15
15
|
let!(:experiment) {
|
16
|
-
Split::ExperimentCatalog.find_or_create({"basket_text" => ["purchase", "refund"]}, "Basket", "Cart")
|
16
|
+
Split::ExperimentCatalog.find_or_create({ "basket_text" => ["purchase", "refund"] }, "Basket", "Cart")
|
17
17
|
}
|
18
18
|
|
19
19
|
let(:goal1) { "purchase" }
|
@@ -24,48 +24,48 @@ describe Split::Alternative do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should have and only return the name" do
|
27
|
-
expect(alternative.name).to eq(
|
27
|
+
expect(alternative.name).to eq("Basket")
|
28
28
|
end
|
29
29
|
|
30
|
-
describe
|
30
|
+
describe "weights" do
|
31
31
|
it "should set the weights" do
|
32
|
-
experiment = Split::Experiment.new(
|
32
|
+
experiment = Split::Experiment.new("basket_text", alternatives: [{ "Basket" => 0.6 }, { "Cart" => 0.4 }])
|
33
33
|
first = experiment.alternatives[0]
|
34
|
-
expect(first.name).to eq(
|
34
|
+
expect(first.name).to eq("Basket")
|
35
35
|
expect(first.weight).to eq(0.6)
|
36
36
|
|
37
37
|
second = experiment.alternatives[1]
|
38
|
-
expect(second.name).to eq(
|
38
|
+
expect(second.name).to eq("Cart")
|
39
39
|
expect(second.weight).to eq(0.4)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "accepts probability on alternatives" do
|
43
43
|
Split.configuration.experiments = {
|
44
|
-
:
|
45
|
-
:
|
46
|
-
{ :
|
47
|
-
{ :
|
48
|
-
{ :
|
44
|
+
my_experiment: {
|
45
|
+
alternatives: [
|
46
|
+
{ name: "control_opt", percent: 67 },
|
47
|
+
{ name: "second_opt", percent: 10 },
|
48
|
+
{ name: "third_opt", percent: 23 },
|
49
49
|
]
|
50
50
|
}
|
51
51
|
}
|
52
52
|
experiment = Split::Experiment.new(:my_experiment)
|
53
53
|
first = experiment.alternatives[0]
|
54
|
-
expect(first.name).to eq(
|
54
|
+
expect(first.name).to eq("control_opt")
|
55
55
|
expect(first.weight).to eq(0.67)
|
56
56
|
|
57
57
|
second = experiment.alternatives[1]
|
58
|
-
expect(second.name).to eq(
|
58
|
+
expect(second.name).to eq("second_opt")
|
59
59
|
expect(second.weight).to eq(0.1)
|
60
60
|
end
|
61
61
|
|
62
62
|
it "accepts probability on some alternatives" do
|
63
63
|
Split.configuration.experiments = {
|
64
|
-
:
|
65
|
-
:
|
66
|
-
{ :
|
64
|
+
my_experiment: {
|
65
|
+
alternatives: [
|
66
|
+
{ name: "control_opt", percent: 34 },
|
67
67
|
"second_opt",
|
68
|
-
{ :
|
68
|
+
{ name: "third_opt", percent: 23 },
|
69
69
|
"fourth_opt",
|
70
70
|
],
|
71
71
|
}
|
@@ -87,11 +87,11 @@ describe Split::Alternative do
|
|
87
87
|
#
|
88
88
|
it "allows name param without probability" do
|
89
89
|
Split.configuration.experiments = {
|
90
|
-
:
|
91
|
-
:
|
92
|
-
{ :
|
90
|
+
my_experiment: {
|
91
|
+
alternatives: [
|
92
|
+
{ name: "control_opt" },
|
93
93
|
"second_opt",
|
94
|
-
{ :
|
94
|
+
{ name: "third_opt", percent: 64 },
|
95
95
|
],
|
96
96
|
}
|
97
97
|
}
|
@@ -126,7 +126,7 @@ describe Split::Alternative do
|
|
126
126
|
|
127
127
|
it "should save to redis" do
|
128
128
|
alternative.save
|
129
|
-
expect(Split.redis.exists?(
|
129
|
+
expect(Split.redis.exists?("basket_text:Basket")).to be true
|
130
130
|
end
|
131
131
|
|
132
132
|
it "should increment participation count" do
|
@@ -166,7 +166,7 @@ describe Split::Alternative do
|
|
166
166
|
expect(alternative2.control?).to be_falsey
|
167
167
|
end
|
168
168
|
|
169
|
-
describe
|
169
|
+
describe "unfinished_count" do
|
170
170
|
it "should be difference between participant and completed counts" do
|
171
171
|
alternative.increment_participation
|
172
172
|
expect(alternative.unfinished_count).to eq(alternative.participant_count)
|
@@ -182,7 +182,7 @@ describe Split::Alternative do
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
-
describe
|
185
|
+
describe "conversion rate" do
|
186
186
|
it "should be 0 if there are no conversions" do
|
187
187
|
expect(alternative.completed_count).to eq(0)
|
188
188
|
expect(alternative.conversion_rate).to eq(0)
|
@@ -225,8 +225,7 @@ describe Split::Alternative do
|
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
-
describe
|
229
|
-
|
228
|
+
describe "z score" do
|
230
229
|
it "should return an error string when the control has 0 people" do
|
231
230
|
expect(alternative2.z_score).to eq("Needs 30+ participants.")
|
232
231
|
expect(alternative2.z_score(goal1)).to eq("Needs 30+ participants.")
|
@@ -269,9 +268,9 @@ describe Split::Alternative do
|
|
269
268
|
|
270
269
|
it "should be N/A for the control" do
|
271
270
|
control = experiment.control
|
272
|
-
expect(control.z_score).to eq(
|
273
|
-
expect(control.z_score(goal1)).to eq(
|
274
|
-
expect(control.z_score(goal2)).to eq(
|
271
|
+
expect(control.z_score).to eq("N/A")
|
272
|
+
expect(control.z_score(goal1)).to eq("N/A")
|
273
|
+
expect(control.z_score(goal2)).to eq("N/A")
|
275
274
|
end
|
276
275
|
|
277
276
|
it "should not blow up for Conversion Rates > 1" do
|
@@ -289,8 +288,8 @@ describe Split::Alternative do
|
|
289
288
|
|
290
289
|
describe "extra_info" do
|
291
290
|
it "reads saved value of recorded_info in redis" do
|
292
|
-
saved_recorded_info = {"key_1" => 1, "key_2" => "2"}
|
293
|
-
Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}",
|
291
|
+
saved_recorded_info = { "key_1" => 1, "key_2" => "2" }
|
292
|
+
Split.redis.hset "#{alternative.experiment_name}:#{alternative.name}", "recorded_info", saved_recorded_info.to_json
|
294
293
|
extra_info = alternative.extra_info
|
295
294
|
|
296
295
|
expect(extra_info).to eql(saved_recorded_info)
|
data/spec/cache_spec.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
2
|
|
4
|
-
|
3
|
+
require "spec_helper"
|
5
4
|
|
5
|
+
describe Split::Cache do
|
6
6
|
let(:namespace) { :test_namespace }
|
7
7
|
let(:key) { :test_key }
|
8
8
|
let(:now) { 1606189017 }
|
9
9
|
|
10
10
|
before { allow(Time).to receive(:now).and_return(now) }
|
11
11
|
|
12
|
-
describe
|
13
|
-
|
12
|
+
describe "clear" do
|
14
13
|
before { Split.configuration.cache = true }
|
15
14
|
|
16
|
-
it
|
15
|
+
it "clears the cache" do
|
17
16
|
expect(Time).to receive(:now).and_return(now).exactly(2).times
|
18
17
|
Split::Cache.fetch(namespace, key) { Time.now }
|
19
18
|
Split::Cache.clear
|
@@ -21,10 +20,10 @@ describe Split::Cache do
|
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
24
|
-
describe
|
23
|
+
describe "clear_key" do
|
25
24
|
before { Split.configuration.cache = true }
|
26
25
|
|
27
|
-
it
|
26
|
+
it "clears the cache" do
|
28
27
|
expect(Time).to receive(:now).and_return(now).exactly(3).times
|
29
28
|
Split::Cache.fetch(namespace, :key1) { Time.now }
|
30
29
|
Split::Cache.fetch(namespace, :key2) { Time.now }
|
@@ -35,40 +34,37 @@ describe Split::Cache do
|
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
|
-
describe
|
39
|
-
|
37
|
+
describe "fetch" do
|
40
38
|
subject { Split::Cache.fetch(namespace, key) { Time.now } }
|
41
39
|
|
42
|
-
context
|
43
|
-
|
40
|
+
context "when cache disabled" do
|
44
41
|
before { Split.configuration.cache = false }
|
45
42
|
|
46
|
-
it
|
43
|
+
it "returns the yield" do
|
47
44
|
expect(subject).to eql(now)
|
48
45
|
end
|
49
46
|
|
50
|
-
it
|
47
|
+
it "yields every time" do
|
51
48
|
expect(Time).to receive(:now).and_return(now).exactly(2).times
|
52
49
|
Split::Cache.fetch(namespace, key) { Time.now }
|
53
50
|
Split::Cache.fetch(namespace, key) { Time.now }
|
54
51
|
end
|
55
52
|
end
|
56
53
|
|
57
|
-
context
|
58
|
-
|
54
|
+
context "when cache enabled" do
|
59
55
|
before { Split.configuration.cache = true }
|
60
56
|
|
61
|
-
it
|
57
|
+
it "returns the yield" do
|
62
58
|
expect(subject).to eql(now)
|
63
59
|
end
|
64
60
|
|
65
|
-
it
|
61
|
+
it "yields once" do
|
66
62
|
expect(Time).to receive(:now).and_return(now).once
|
67
63
|
Split::Cache.fetch(namespace, key) { Time.now }
|
68
64
|
Split::Cache.fetch(namespace, key) { Time.now }
|
69
65
|
end
|
70
66
|
|
71
|
-
it
|
67
|
+
it "honors namespace" do
|
72
68
|
expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
|
73
69
|
expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
|
74
70
|
|
@@ -76,7 +72,7 @@ describe Split::Cache do
|
|
76
72
|
expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
|
77
73
|
end
|
78
74
|
|
79
|
-
it
|
75
|
+
it "honors key" do
|
80
76
|
expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
|
81
77
|
expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
|
82
78
|
|