split 4.0.1 → 4.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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/spec/split_spec.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'spec_helper'
|
3
2
|
|
4
|
-
|
3
|
+
require "spec_helper"
|
5
4
|
|
5
|
+
RSpec.describe Split do
|
6
6
|
around(:each) do |ex|
|
7
|
-
old_env, old_redis = [ENV.delete(
|
7
|
+
old_env, old_redis = [ENV.delete("REDIS_URL"), Split.redis]
|
8
8
|
ex.run
|
9
|
-
ENV[
|
9
|
+
ENV["REDIS_URL"] = old_env
|
10
10
|
Split.redis = old_redis
|
11
11
|
end
|
12
12
|
|
13
|
-
describe
|
14
|
-
it
|
15
|
-
Split.redis =
|
13
|
+
describe "#redis=" do
|
14
|
+
it "accepts a url string" do
|
15
|
+
Split.redis = "redis://localhost:6379"
|
16
16
|
expect(Split.redis).to be_a(Redis)
|
17
17
|
|
18
18
|
client = Split.redis.connection
|
@@ -20,8 +20,8 @@ RSpec.describe Split do
|
|
20
20
|
expect(client[:port]).to eq(6379)
|
21
21
|
end
|
22
22
|
|
23
|
-
it
|
24
|
-
Split.redis = {host:
|
23
|
+
it "accepts an options hash" do
|
24
|
+
Split.redis = { host: "localhost", port: 6379, db: 12 }
|
25
25
|
expect(Split.redis).to be_a(Redis)
|
26
26
|
|
27
27
|
client = Split.redis.connection
|
@@ -30,13 +30,13 @@ RSpec.describe Split do
|
|
30
30
|
expect(client[:db]).to eq(12)
|
31
31
|
end
|
32
32
|
|
33
|
-
it
|
33
|
+
it "accepts a valid Redis instance" do
|
34
34
|
other_redis = Redis.new(url: "redis://localhost:6379")
|
35
35
|
Split.redis = other_redis
|
36
36
|
expect(Split.redis).to eq(other_redis)
|
37
37
|
end
|
38
38
|
|
39
|
-
it
|
39
|
+
it "raises an ArgumentError when server cannot be determined" do
|
40
40
|
expect { Split.redis = Object.new }.to raise_error(ArgumentError)
|
41
41
|
end
|
42
42
|
end
|
data/spec/trial_spec.rb
CHANGED
@@ -1,54 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "split/trial"
|
4
5
|
|
5
6
|
describe Split::Trial do
|
6
7
|
let(:user) { mock_user }
|
7
|
-
let(:alternatives) { [
|
8
|
+
let(:alternatives) { ["basket", "cart"] }
|
8
9
|
let(:experiment) do
|
9
|
-
Split::Experiment.new(
|
10
|
+
Split::Experiment.new("basket_text", alternatives: alternatives).save
|
10
11
|
end
|
11
12
|
|
12
13
|
it "should be initializeable" do
|
13
|
-
experiment = double(
|
14
|
-
alternative = double(
|
15
|
-
trial = Split::Trial.new(:
|
14
|
+
experiment = double("experiment")
|
15
|
+
alternative = double("alternative", kind_of?: Split::Alternative)
|
16
|
+
trial = Split::Trial.new(experiment: experiment, alternative: alternative)
|
16
17
|
expect(trial.experiment).to eq(experiment)
|
17
18
|
expect(trial.alternative).to eq(alternative)
|
18
19
|
end
|
19
20
|
|
20
21
|
describe "alternative" do
|
21
22
|
it "should use the alternative if specified" do
|
22
|
-
alternative = double(
|
23
|
-
trial = Split::Trial.new(:
|
24
|
-
:
|
23
|
+
alternative = double("alternative", kind_of?: Split::Alternative)
|
24
|
+
trial = Split::Trial.new(experiment: double("experiment"),
|
25
|
+
alternative: alternative, user: user)
|
25
26
|
expect(trial).not_to receive(:choose)
|
26
27
|
expect(trial.alternative).to eq(alternative)
|
27
28
|
end
|
28
29
|
|
29
30
|
it "should load the alternative when the alternative name is set" do
|
30
|
-
experiment = Split::Experiment.new(
|
31
|
+
experiment = Split::Experiment.new("basket_text", alternatives: ["basket", "cart"])
|
31
32
|
experiment.save
|
32
33
|
|
33
|
-
trial = Split::Trial.new(:
|
34
|
-
expect(trial.alternative.name).to eq(
|
34
|
+
trial = Split::Trial.new(experiment: experiment, alternative: "basket")
|
35
|
+
expect(trial.alternative.name).to eq("basket")
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
38
39
|
describe "metadata" do
|
39
40
|
let(:metadata) { Hash[alternatives.map { |k| [k, "Metadata for #{k}"] }] }
|
40
41
|
let(:experiment) do
|
41
|
-
Split::Experiment.new(
|
42
|
+
Split::Experiment.new("basket_text", alternatives: alternatives, metadata: metadata).save
|
42
43
|
end
|
43
44
|
|
44
|
-
it
|
45
|
-
trial = Split::Trial.new(:
|
46
|
-
:
|
47
|
-
expect(trial.metadata).to eq(metadata[
|
45
|
+
it "has metadata on each trial" do
|
46
|
+
trial = Split::Trial.new(experiment: experiment, user: user, metadata: metadata["cart"],
|
47
|
+
override: "cart")
|
48
|
+
expect(trial.metadata).to eq(metadata["cart"])
|
48
49
|
end
|
49
50
|
|
50
|
-
it
|
51
|
-
trial = Split::Trial.new(:
|
51
|
+
it "has metadata on each trial from the experiment" do
|
52
|
+
trial = Split::Trial.new(experiment: experiment, user: user)
|
52
53
|
trial.choose!
|
53
54
|
expect(trial.metadata).to eq(metadata[trial.alternative.name])
|
54
55
|
expect(trial.metadata).to match(/#{trial.alternative.name}/)
|
@@ -56,24 +57,24 @@ describe Split::Trial do
|
|
56
57
|
end
|
57
58
|
|
58
59
|
describe "#choose!" do
|
59
|
-
let(:context) { double(on_trial_callback:
|
60
|
+
let(:context) { double(on_trial_callback: "test callback") }
|
60
61
|
let(:trial) do
|
61
|
-
Split::Trial.new(:
|
62
|
+
Split::Trial.new(user: user, experiment: experiment)
|
62
63
|
end
|
63
64
|
|
64
|
-
shared_examples_for
|
65
|
-
it
|
65
|
+
shared_examples_for "a trial with callbacks" do
|
66
|
+
it "does not run if on_trial callback is not respondable" do
|
66
67
|
Split.configuration.on_trial = :foo
|
67
68
|
allow(context).to receive(:respond_to?).with(:foo, true).and_return false
|
68
69
|
expect(context).to_not receive(:foo)
|
69
70
|
trial.choose! context
|
70
71
|
end
|
71
|
-
it
|
72
|
+
it "runs on_trial callback" do
|
72
73
|
Split.configuration.on_trial = :on_trial_callback
|
73
74
|
expect(context).to receive(:on_trial_callback)
|
74
75
|
trial.choose! context
|
75
76
|
end
|
76
|
-
it
|
77
|
+
it "does not run nil on_trial callback" do
|
77
78
|
Split.configuration.on_trial = nil
|
78
79
|
expect(context).not_to receive(:on_trial_callback)
|
79
80
|
trial.choose! context
|
@@ -88,12 +89,12 @@ describe Split::Trial do
|
|
88
89
|
end
|
89
90
|
|
90
91
|
context "when override is present" do
|
91
|
-
let(:override) {
|
92
|
+
let(:override) { "cart" }
|
92
93
|
let(:trial) do
|
93
|
-
Split::Trial.new(:
|
94
|
+
Split::Trial.new(user: user, experiment: experiment, override: override)
|
94
95
|
end
|
95
96
|
|
96
|
-
it_behaves_like
|
97
|
+
it_behaves_like "a trial with callbacks"
|
97
98
|
|
98
99
|
it "picks the override" do
|
99
100
|
expect(experiment).to_not receive(:next_alternative)
|
@@ -102,7 +103,7 @@ describe Split::Trial do
|
|
102
103
|
|
103
104
|
context "when alternative doesn't exist" do
|
104
105
|
let(:override) { nil }
|
105
|
-
it
|
106
|
+
it "falls back on next_alternative" do
|
106
107
|
expect(experiment).to receive(:next_alternative).and_call_original
|
107
108
|
expect_alternative(trial, alternatives)
|
108
109
|
end
|
@@ -111,7 +112,7 @@ describe Split::Trial do
|
|
111
112
|
|
112
113
|
context "when disabled option is true" do
|
113
114
|
let(:trial) do
|
114
|
-
Split::Trial.new(:
|
115
|
+
Split::Trial.new(user: user, experiment: experiment, disabled: true)
|
115
116
|
end
|
116
117
|
|
117
118
|
it "picks the control", :aggregate_failures do
|
@@ -120,7 +121,7 @@ describe Split::Trial do
|
|
120
121
|
|
121
122
|
expect(context).not_to receive(:on_trial_callback)
|
122
123
|
|
123
|
-
expect_alternative(trial,
|
124
|
+
expect_alternative(trial, "basket")
|
124
125
|
Split.configuration.on_trial = nil
|
125
126
|
end
|
126
127
|
end
|
@@ -132,7 +133,7 @@ describe Split::Trial do
|
|
132
133
|
|
133
134
|
expect(experiment).to_not receive(:next_alternative)
|
134
135
|
expect(context).not_to receive(:on_trial_callback)
|
135
|
-
expect_alternative(trial,
|
136
|
+
expect_alternative(trial, "basket")
|
136
137
|
|
137
138
|
Split.configuration.enabled = true
|
138
139
|
Split.configuration.on_trial = nil
|
@@ -141,45 +142,45 @@ describe Split::Trial do
|
|
141
142
|
|
142
143
|
context "when experiment has winner" do
|
143
144
|
let(:trial) do
|
144
|
-
Split::Trial.new(:
|
145
|
+
Split::Trial.new(user: user, experiment: experiment)
|
145
146
|
end
|
146
147
|
|
147
|
-
it_behaves_like
|
148
|
+
it_behaves_like "a trial with callbacks"
|
148
149
|
|
149
150
|
it "picks the winner" do
|
150
|
-
experiment.winner =
|
151
|
+
experiment.winner = "cart"
|
151
152
|
expect(experiment).to_not receive(:next_alternative)
|
152
153
|
|
153
|
-
expect_alternative(trial,
|
154
|
+
expect_alternative(trial, "cart")
|
154
155
|
end
|
155
156
|
end
|
156
157
|
|
157
158
|
context "when exclude is true" do
|
158
159
|
let(:trial) do
|
159
|
-
Split::Trial.new(:
|
160
|
+
Split::Trial.new(user: user, experiment: experiment, exclude: true)
|
160
161
|
end
|
161
162
|
|
162
|
-
it_behaves_like
|
163
|
+
it_behaves_like "a trial with callbacks"
|
163
164
|
|
164
165
|
it "picks the control" do
|
165
166
|
expect(experiment).to_not receive(:next_alternative)
|
166
|
-
expect_alternative(trial,
|
167
|
+
expect_alternative(trial, "basket")
|
167
168
|
end
|
168
169
|
end
|
169
170
|
|
170
171
|
context "when user is already participating" do
|
171
|
-
it_behaves_like
|
172
|
+
it_behaves_like "a trial with callbacks"
|
172
173
|
|
173
174
|
it "picks the same alternative" do
|
174
|
-
user[experiment.key] =
|
175
|
+
user[experiment.key] = "basket"
|
175
176
|
expect(experiment).to_not receive(:next_alternative)
|
176
177
|
|
177
|
-
expect_alternative(trial,
|
178
|
+
expect_alternative(trial, "basket")
|
178
179
|
end
|
179
180
|
|
180
181
|
context "when alternative is not found" do
|
181
182
|
it "falls back on next_alternative" do
|
182
|
-
user[experiment.key] =
|
183
|
+
user[experiment.key] = "notfound"
|
183
184
|
expect(experiment).to receive(:next_alternative).and_call_original
|
184
185
|
expect_alternative(trial, alternatives)
|
185
186
|
end
|
@@ -213,7 +214,7 @@ describe Split::Trial do
|
|
213
214
|
|
214
215
|
expect(experiment).to_not receive(:next_alternative)
|
215
216
|
expect(context).not_to receive(:on_trial_callback)
|
216
|
-
expect_alternative(trial,
|
217
|
+
expect_alternative(trial, "basket")
|
217
218
|
|
218
219
|
Split.configuration.enabled = true
|
219
220
|
Split.configuration.on_trial = nil
|
@@ -229,9 +230,9 @@ describe Split::Trial do
|
|
229
230
|
end
|
230
231
|
|
231
232
|
describe "#complete!" do
|
232
|
-
context
|
233
|
-
let(:trial) { Split::Trial.new(:
|
234
|
-
it
|
233
|
+
context "when there are no goals" do
|
234
|
+
let(:trial) { Split::Trial.new(user: user, experiment: experiment) }
|
235
|
+
it "should complete the trial" do
|
235
236
|
trial.choose!
|
236
237
|
old_completed_count = trial.alternative.completed_count
|
237
238
|
trial.complete!
|
@@ -241,11 +242,11 @@ describe Split::Trial do
|
|
241
242
|
|
242
243
|
context "when there are many goals" do
|
243
244
|
let(:goals) { [ "goal1", "goal2" ] }
|
244
|
-
let(:trial) { Split::Trial.new(:
|
245
|
+
let(:trial) { Split::Trial.new(user: user, experiment: experiment, goals: goals) }
|
245
246
|
|
246
247
|
it "increments the completed count corresponding to the goals" do
|
247
248
|
trial.choose!
|
248
|
-
old_completed_counts = goals.map{ |goal| [goal, trial.alternative.completed_count(goal)] }.to_h
|
249
|
+
old_completed_counts = goals.map { |goal| [goal, trial.alternative.completed_count(goal)] }.to_h
|
249
250
|
trial.complete!
|
250
251
|
goals.each { | goal | expect(trial.alternative.completed_count(goal)).to eq(old_completed_counts[goal] + 1) }
|
251
252
|
end
|
@@ -253,7 +254,7 @@ describe Split::Trial do
|
|
253
254
|
|
254
255
|
context "when there is 1 goal of type string" do
|
255
256
|
let(:goal) { "goal" }
|
256
|
-
let(:trial) { Split::Trial.new(:
|
257
|
+
let(:trial) { Split::Trial.new(user: user, experiment: experiment, goals: goal) }
|
257
258
|
it "increments the completed count corresponding to the goal" do
|
258
259
|
trial.choose!
|
259
260
|
old_completed_count = trial.alternative.completed_count(goal)
|
@@ -268,7 +269,7 @@ describe Split::Trial do
|
|
268
269
|
|
269
270
|
context "when override is present" do
|
270
271
|
it "stores when store_override is true" do
|
271
|
-
trial = Split::Trial.new(:
|
272
|
+
trial = Split::Trial.new(user: user, experiment: experiment, override: "basket")
|
272
273
|
|
273
274
|
Split.configuration.store_override = true
|
274
275
|
expect(user).to receive("[]=")
|
@@ -277,7 +278,7 @@ describe Split::Trial do
|
|
277
278
|
end
|
278
279
|
|
279
280
|
it "does not store when store_override is false" do
|
280
|
-
trial = Split::Trial.new(:
|
281
|
+
trial = Split::Trial.new(user: user, experiment: experiment, override: "basket")
|
281
282
|
|
282
283
|
expect(user).to_not receive("[]=")
|
283
284
|
trial.choose!
|
@@ -286,7 +287,7 @@ describe Split::Trial do
|
|
286
287
|
|
287
288
|
context "when disabled is present" do
|
288
289
|
it "stores when store_override is true" do
|
289
|
-
trial = Split::Trial.new(:
|
290
|
+
trial = Split::Trial.new(user: user, experiment: experiment, disabled: true)
|
290
291
|
|
291
292
|
Split.configuration.store_override = true
|
292
293
|
expect(user).to receive("[]=")
|
@@ -294,7 +295,7 @@ describe Split::Trial do
|
|
294
295
|
end
|
295
296
|
|
296
297
|
it "does not store when store_override is false" do
|
297
|
-
trial = Split::Trial.new(:
|
298
|
+
trial = Split::Trial.new(user: user, experiment: experiment, disabled: true)
|
298
299
|
|
299
300
|
expect(user).to_not receive("[]=")
|
300
301
|
trial.choose!
|
@@ -303,20 +304,20 @@ describe Split::Trial do
|
|
303
304
|
|
304
305
|
context "when exclude is present" do
|
305
306
|
it "does not store" do
|
306
|
-
trial = Split::Trial.new(:
|
307
|
+
trial = Split::Trial.new(user: user, experiment: experiment, exclude: true)
|
307
308
|
|
308
309
|
expect(user).to_not receive("[]=")
|
309
310
|
trial.choose!
|
310
311
|
end
|
311
312
|
end
|
312
313
|
|
313
|
-
context
|
314
|
+
context "when experiment has winner" do
|
314
315
|
let(:trial) do
|
315
|
-
experiment.winner =
|
316
|
-
Split::Trial.new(:
|
316
|
+
experiment.winner = "cart"
|
317
|
+
Split::Trial.new(user: user, experiment: experiment)
|
317
318
|
end
|
318
319
|
|
319
|
-
it
|
320
|
+
it "does not store" do
|
320
321
|
expect(user).to_not receive("[]=")
|
321
322
|
trial.choose!
|
322
323
|
end
|
data/spec/user_spec.rb
CHANGED
@@ -1,71 +1,73 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "split/experiment_catalog"
|
5
|
+
require "split/experiment"
|
6
|
+
require "split/user"
|
5
7
|
|
6
8
|
describe Split::User do
|
7
|
-
let(:user_keys) { {
|
8
|
-
let(:context) { double(:
|
9
|
-
let(:experiment) { Split::Experiment.new(
|
9
|
+
let(:user_keys) { { "link_color" => "blue" } }
|
10
|
+
let(:context) { double(session: { split: user_keys }) }
|
11
|
+
let(:experiment) { Split::Experiment.new("link_color") }
|
10
12
|
|
11
13
|
before(:each) do
|
12
14
|
@subject = described_class.new(context)
|
13
15
|
end
|
14
16
|
|
15
|
-
it
|
16
|
-
expect(@subject[
|
17
|
+
it "delegates methods correctly" do
|
18
|
+
expect(@subject["link_color"]).to eq(@subject.user["link_color"])
|
17
19
|
end
|
18
20
|
|
19
|
-
context
|
21
|
+
context "#cleanup_old_versions!" do
|
20
22
|
let(:experiment_version) { "#{experiment.name}:1" }
|
21
23
|
let(:second_experiment_version) { "#{experiment.name}_another:1" }
|
22
24
|
let(:third_experiment_version) { "variation_of_#{experiment.name}:1" }
|
23
25
|
let(:user_keys) do
|
24
26
|
{
|
25
|
-
experiment_version =>
|
26
|
-
second_experiment_version =>
|
27
|
-
third_experiment_version =>
|
27
|
+
experiment_version => "blue",
|
28
|
+
second_experiment_version => "red",
|
29
|
+
third_experiment_version => "yellow"
|
28
30
|
}
|
29
31
|
end
|
30
32
|
|
31
33
|
before(:each) { @subject.cleanup_old_versions!(experiment) }
|
32
34
|
|
33
|
-
it
|
35
|
+
it "removes key if old experiment is found" do
|
34
36
|
expect(@subject.keys).not_to include(experiment_version)
|
35
37
|
end
|
36
38
|
|
37
|
-
it
|
39
|
+
it "does not remove other keys" do
|
38
40
|
expect(@subject.keys).to include(second_experiment_version, third_experiment_version)
|
39
41
|
end
|
40
|
-
end
|
42
|
+
end
|
41
43
|
|
42
|
-
context
|
43
|
-
it
|
44
|
+
context "#cleanup_old_experiments!" do
|
45
|
+
it "removes key if experiment is not found" do
|
44
46
|
@subject.cleanup_old_experiments!
|
45
47
|
expect(@subject.keys).to be_empty
|
46
48
|
end
|
47
49
|
|
48
|
-
it
|
49
|
-
allow(Split::ExperimentCatalog).to receive(:find).with(
|
50
|
+
it "removes key if experiment has a winner" do
|
51
|
+
allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment)
|
50
52
|
allow(experiment).to receive(:start_time).and_return(Date.today)
|
51
53
|
allow(experiment).to receive(:has_winner?).and_return(true)
|
52
54
|
@subject.cleanup_old_experiments!
|
53
55
|
expect(@subject.keys).to be_empty
|
54
56
|
end
|
55
57
|
|
56
|
-
it
|
57
|
-
allow(Split::ExperimentCatalog).to receive(:find).with(
|
58
|
+
it "removes key if experiment has not started yet" do
|
59
|
+
allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment)
|
58
60
|
allow(experiment).to receive(:has_winner?).and_return(false)
|
59
61
|
@subject.cleanup_old_experiments!
|
60
62
|
expect(@subject.keys).to be_empty
|
61
63
|
end
|
62
64
|
|
63
|
-
context
|
64
|
-
let(:user_keys) { {
|
65
|
+
context "with finished key" do
|
66
|
+
let(:user_keys) { { "link_color" => "blue", "link_color:finished" => true } }
|
65
67
|
|
66
|
-
it
|
67
|
-
allow(Split::ExperimentCatalog).to receive(:find).with(
|
68
|
-
allow(Split::ExperimentCatalog).to receive(:find).with(
|
68
|
+
it "does not remove finished key for experiment without a winner" do
|
69
|
+
allow(Split::ExperimentCatalog).to receive(:find).with("link_color").and_return(experiment)
|
70
|
+
allow(Split::ExperimentCatalog).to receive(:find).with("link_color:finished").and_return(nil)
|
69
71
|
allow(experiment).to receive(:start_time).and_return(Date.today)
|
70
72
|
allow(experiment).to receive(:has_winner?).and_return(false)
|
71
73
|
@subject.cleanup_old_experiments!
|
@@ -74,33 +76,32 @@ describe Split::User do
|
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
77
|
-
context
|
79
|
+
context "when already cleaned up" do
|
78
80
|
before do
|
79
81
|
@subject.cleanup_old_experiments!
|
80
82
|
end
|
81
83
|
|
82
|
-
it
|
84
|
+
it "does not clean up again" do
|
83
85
|
expect(@subject).to_not receive(:keys_without_finished)
|
84
86
|
@subject.cleanup_old_experiments!
|
85
87
|
end
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
89
|
-
context
|
90
|
-
it
|
91
|
+
context "allows user to be loaded from adapter" do
|
92
|
+
it "loads user from adapter (RedisAdapter)" do
|
91
93
|
user = Split::Persistence::RedisAdapter.new(nil, 112233)
|
92
|
-
user[
|
94
|
+
user["foo"] = "bar"
|
93
95
|
|
94
96
|
ab_user = Split::User.find(112233, :redis)
|
95
97
|
|
96
|
-
expect(ab_user[
|
98
|
+
expect(ab_user["foo"]).to eql("bar")
|
97
99
|
end
|
98
100
|
|
99
|
-
it
|
101
|
+
it "returns nil if adapter does not implement a finder method" do
|
100
102
|
ab_user = Split::User.find(112233, :dual_adapter)
|
101
103
|
expect(ab_user).to be_nil
|
102
104
|
end
|
103
|
-
|
104
105
|
end
|
105
106
|
|
106
107
|
context "instantiated with custom adapter" do
|
@@ -114,5 +115,4 @@ describe Split::User do
|
|
114
115
|
expect(@subject.user).to eq(custom_adapter)
|
115
116
|
end
|
116
117
|
end
|
117
|
-
|
118
118
|
end
|
data/split.gemspec
CHANGED
@@ -9,36 +9,37 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.version = Split::VERSION
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.authors = ["Andrew Nesbitt"]
|
12
|
-
s.licenses = [
|
12
|
+
s.licenses = ["MIT"]
|
13
13
|
s.email = ["andrewnez@gmail.com"]
|
14
14
|
s.homepage = "https://github.com/splitrb/split"
|
15
15
|
s.summary = "Rack based split testing framework"
|
16
16
|
|
17
17
|
s.metadata = {
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
"homepage_uri" => "https://github.com/splitrb/split",
|
19
|
+
"changelog_uri" => "https://github.com/splitrb/split/blob/main/CHANGELOG.md",
|
20
|
+
"source_code_uri" => "https://github.com/splitrb/split",
|
21
|
+
"bug_tracker_uri" => "https://github.com/splitrb/split/issues",
|
22
|
+
"wiki_uri" => "https://github.com/splitrb/split/wiki",
|
23
|
+
"mailing_list_uri" => "https://groups.google.com/d/forum/split-ruby"
|
24
|
+
}
|
25
25
|
|
26
|
-
s.required_ruby_version =
|
27
|
-
s.required_rubygems_version =
|
26
|
+
s.required_ruby_version = ">= 2.5.0"
|
27
|
+
s.required_rubygems_version = ">= 2.0.0"
|
28
28
|
|
29
29
|
s.files = `git ls-files`.split("\n")
|
30
30
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
31
31
|
s.require_paths = ["lib"]
|
32
32
|
|
33
|
-
s.add_dependency
|
34
|
-
s.add_dependency
|
35
|
-
s.add_dependency
|
33
|
+
s.add_dependency "redis", ">= 4.2"
|
34
|
+
s.add_dependency "sinatra", ">= 1.2.6"
|
35
|
+
s.add_dependency "rubystats", ">= 0.3.0"
|
36
|
+
s.add_dependency "matrix"
|
36
37
|
|
37
|
-
s.add_development_dependency
|
38
|
-
s.add_development_dependency
|
39
|
-
s.add_development_dependency
|
40
|
-
s.add_development_dependency
|
41
|
-
s.add_development_dependency
|
42
|
-
s.add_development_dependency
|
43
|
-
s.add_development_dependency
|
38
|
+
s.add_development_dependency "bundler", ">= 1.17"
|
39
|
+
s.add_development_dependency "simplecov", "~> 0.15"
|
40
|
+
s.add_development_dependency "rack-test", "~> 2.0"
|
41
|
+
s.add_development_dependency "rake", "~> 13"
|
42
|
+
s.add_development_dependency "rspec", "~> 3.7"
|
43
|
+
s.add_development_dependency "pry", "~> 0.10"
|
44
|
+
s.add_development_dependency "rails", ">= 5.0"
|
44
45
|
end
|