split 2.0.0 → 3.0.0
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/.codeclimate.yml +30 -0
- data/.csslintrc +2 -0
- data/.eslintignore +1 -0
- data/.eslintrc +213 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +5 -2
- data/Appraisals +5 -4
- data/CHANGELOG.md +46 -0
- data/Gemfile +1 -0
- data/README.md +123 -20
- data/gemfiles/4.2.gemfile +1 -0
- data/gemfiles/5.0.gemfile +10 -0
- data/lib/split/algorithms/block_randomization.rb +22 -0
- data/lib/split/alternative.rb +29 -8
- data/lib/split/configuration.rb +13 -2
- data/lib/split/dashboard/helpers.rb +5 -1
- data/lib/split/dashboard/public/dashboard-filtering.js +3 -3
- data/lib/split/dashboard/views/_experiment.erb +35 -1
- data/lib/split/dashboard.rb +5 -0
- data/lib/split/encapsulated_helper.rb +4 -15
- data/lib/split/experiment.rb +63 -54
- data/lib/split/goals_collection.rb +1 -1
- data/lib/split/helper.rb +20 -0
- data/lib/split/persistence/dual_adapter.rb +3 -0
- data/lib/split/persistence.rb +5 -3
- data/lib/split/redis_interface.rb +51 -0
- data/lib/split/user.rb +3 -1
- data/lib/split/version.rb +1 -1
- data/lib/split/zscore.rb +1 -1
- data/lib/split.rb +34 -41
- data/spec/algorithms/block_randomization_spec.rb +32 -0
- data/spec/alternative_spec.rb +31 -0
- data/spec/configuration_spec.rb +19 -5
- data/spec/dashboard_helpers_spec.rb +14 -0
- data/spec/dashboard_spec.rb +15 -0
- data/spec/encapsulated_helper_spec.rb +35 -4
- data/spec/experiment_spec.rb +38 -7
- data/spec/helper_spec.rb +59 -16
- data/spec/persistence/dual_adapter_spec.rb +102 -0
- data/spec/redis_interface_spec.rb +111 -0
- data/spec/spec_helper.rb +11 -10
- data/spec/split_spec.rb +43 -0
- data/split.gemspec +2 -3
- metadata +23 -26
- data/gemfiles/4.1.gemfile +0 -8
- data/lib/split/algorithms.rb +0 -4
- data/lib/split/extensions/array.rb +0 -5
- data/lib/split/extensions.rb +0 -4
data/spec/experiment_spec.rb
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require 'spec_helper'
|
|
3
|
-
require 'split/experiment'
|
|
4
|
-
require 'split/algorithms'
|
|
5
3
|
require 'time'
|
|
6
4
|
|
|
7
5
|
describe Split::Experiment do
|
|
@@ -13,9 +11,7 @@ describe Split::Experiment do
|
|
|
13
11
|
Split::Alternative.new(color, 'link_color')
|
|
14
12
|
end
|
|
15
13
|
|
|
16
|
-
let(:experiment) {
|
|
17
|
-
new_experiment
|
|
18
|
-
}
|
|
14
|
+
let(:experiment) { new_experiment }
|
|
19
15
|
|
|
20
16
|
let(:blue) { alternative("blue") }
|
|
21
17
|
let(:green) { alternative("green") }
|
|
@@ -242,7 +238,15 @@ describe Split::Experiment do
|
|
|
242
238
|
end
|
|
243
239
|
|
|
244
240
|
describe 'reset' do
|
|
245
|
-
|
|
241
|
+
let(:reset_manually) { false }
|
|
242
|
+
|
|
243
|
+
before do
|
|
244
|
+
allow(Split.configuration).to receive(:reset_manually).and_return(reset_manually)
|
|
245
|
+
experiment.save
|
|
246
|
+
green.increment_participation
|
|
247
|
+
green.increment_participation
|
|
248
|
+
end
|
|
249
|
+
|
|
246
250
|
it 'should reset all alternatives' do
|
|
247
251
|
experiment.winner = 'green'
|
|
248
252
|
|
|
@@ -352,6 +356,34 @@ describe Split::Experiment do
|
|
|
352
356
|
same_experiment_again = same_but_different_alternative
|
|
353
357
|
expect(same_experiment_again.version).to eq(1)
|
|
354
358
|
end
|
|
359
|
+
|
|
360
|
+
context 'when experiment configuration is changed' do
|
|
361
|
+
let(:reset_manually) { false }
|
|
362
|
+
|
|
363
|
+
before do
|
|
364
|
+
experiment.save
|
|
365
|
+
allow(Split.configuration).to receive(:reset_manually).and_return(reset_manually)
|
|
366
|
+
green.increment_participation
|
|
367
|
+
green.increment_participation
|
|
368
|
+
experiment.set_alternatives_and_options(alternatives: %w(blue red green zip),
|
|
369
|
+
goals: %w(purchase))
|
|
370
|
+
experiment.save
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
it 'resets all alternatives' do
|
|
374
|
+
expect(green.participant_count).to eq(0)
|
|
375
|
+
expect(green.completed_count).to eq(0)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
context 'when reset_manually is set' do
|
|
379
|
+
let(:reset_manually) { true }
|
|
380
|
+
|
|
381
|
+
it 'does not reset alternatives' do
|
|
382
|
+
expect(green.participant_count).to eq(2)
|
|
383
|
+
expect(green.completed_count).to eq(0)
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
end
|
|
355
387
|
end
|
|
356
388
|
|
|
357
389
|
describe 'alternatives passed as non-strings' do
|
|
@@ -448,5 +480,4 @@ describe Split::Experiment do
|
|
|
448
480
|
expect(experiment.calc_winning_alternatives).to be nil
|
|
449
481
|
end
|
|
450
482
|
end
|
|
451
|
-
|
|
452
483
|
end
|
data/spec/helper_spec.rb
CHANGED
|
@@ -197,24 +197,51 @@ describe Split::Helper do
|
|
|
197
197
|
expect(button_size_alt.participant_count).to eq(1)
|
|
198
198
|
end
|
|
199
199
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
200
|
+
context "with allow_multiple_experiments = 'control'" do
|
|
201
|
+
it "should let a user participate in many experiment with one non-'control' alternative" do
|
|
202
|
+
Split.configure do |config|
|
|
203
|
+
config.allow_multiple_experiments = 'control'
|
|
204
|
+
end
|
|
205
|
+
groups = 100.times.map do |n|
|
|
206
|
+
ab_test("test#{n}".to_sym, {'control' => (100 - n)}, {"test#{n}-alt" => n})
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
experiments = ab_user.active_experiments
|
|
210
|
+
expect(experiments.size).to be > 1
|
|
211
|
+
|
|
212
|
+
count_control = experiments.values.count { |g| g == 'control' }
|
|
213
|
+
expect(count_control).to eq(experiments.size - 1)
|
|
214
|
+
|
|
215
|
+
count_alts = groups.count { |g| g != 'control' }
|
|
216
|
+
expect(count_alts).to eq(1)
|
|
208
217
|
end
|
|
209
218
|
|
|
210
|
-
|
|
211
|
-
|
|
219
|
+
context "when user already has experiment" do
|
|
220
|
+
let(:mock_user){ Split::User.new(self, {'test_0' => 'test-alt'}) }
|
|
221
|
+
before{
|
|
222
|
+
Split.configure do |config|
|
|
223
|
+
config.allow_multiple_experiments = 'control'
|
|
224
|
+
end
|
|
225
|
+
Split::ExperimentCatalog.find_or_initialize('test_0', 'control', 'test-alt').save
|
|
226
|
+
Split::ExperimentCatalog.find_or_initialize('test_1', 'control', 'test-alt').save
|
|
227
|
+
}
|
|
212
228
|
|
|
213
|
-
|
|
214
|
-
|
|
229
|
+
it "should restore previously selected alternative" do
|
|
230
|
+
expect(ab_user.active_experiments.size).to eq 1
|
|
231
|
+
expect(ab_test(:test_0, {'control' => 100}, {"test-alt" => 1})).to eq 'test-alt'
|
|
232
|
+
expect(ab_test(:test_0, {'control' => 1}, {"test-alt" => 100})).to eq 'test-alt'
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "lets override existing choice" do
|
|
236
|
+
pending "this requires user store reset on first call not depending on whelther it is current trial"
|
|
237
|
+
@params = { 'ab_test' => { 'test_1' => 'test-alt' } }
|
|
238
|
+
|
|
239
|
+
expect(ab_test(:test_0, {'control' => 0}, {"test-alt" => 100})).to eq 'control'
|
|
240
|
+
expect(ab_test(:test_1, {'control' => 100}, {"test-alt" => 1})).to eq 'test-alt'
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
end
|
|
215
244
|
|
|
216
|
-
count_alts = groups.count { |g| g != 'control' }
|
|
217
|
-
expect(count_alts).to eq(1)
|
|
218
245
|
end
|
|
219
246
|
|
|
220
247
|
it "should not over-write a finished key when an experiment is on a later version" do
|
|
@@ -642,6 +669,22 @@ describe Split::Helper do
|
|
|
642
669
|
|
|
643
670
|
it_behaves_like "a disabled test"
|
|
644
671
|
end
|
|
672
|
+
|
|
673
|
+
context "when ignored other address" do
|
|
674
|
+
before do
|
|
675
|
+
@request = OpenStruct.new(:ip => '1.1.1.1')
|
|
676
|
+
Split.configure do |c|
|
|
677
|
+
c.ignore_ip_addresses << '81.19.48.130'
|
|
678
|
+
end
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
it "works as usual" do
|
|
682
|
+
alternative_name = ab_test('link_color', 'red', 'blue')
|
|
683
|
+
expect{
|
|
684
|
+
ab_finished('link_color')
|
|
685
|
+
}.to change(Split::Alternative.new(alternative_name, 'link_color'), :completed_count).by(1)
|
|
686
|
+
end
|
|
687
|
+
end
|
|
645
688
|
end
|
|
646
689
|
|
|
647
690
|
describe 'versioned experiments' do
|
|
@@ -774,7 +817,7 @@ describe Split::Helper do
|
|
|
774
817
|
end
|
|
775
818
|
end
|
|
776
819
|
|
|
777
|
-
expect(Split.configuration.db_failover_on_db_error).to receive(:call)
|
|
820
|
+
expect(Split.configuration.db_failover_on_db_error).to receive(:call).and_call_original
|
|
778
821
|
ab_test('link_color', 'blue', 'red')
|
|
779
822
|
end
|
|
780
823
|
|
|
@@ -833,7 +876,7 @@ describe Split::Helper do
|
|
|
833
876
|
end
|
|
834
877
|
end
|
|
835
878
|
|
|
836
|
-
expect(Split.configuration.db_failover_on_db_error).to receive(:call)
|
|
879
|
+
expect(Split.configuration.db_failover_on_db_error).to receive(:call).and_call_original
|
|
837
880
|
ab_finished('link_color')
|
|
838
881
|
end
|
|
839
882
|
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "spec_helper"
|
|
3
|
+
|
|
4
|
+
describe Split::Persistence::DualAdapter do
|
|
5
|
+
|
|
6
|
+
let(:context){ "some context" }
|
|
7
|
+
|
|
8
|
+
let(:just_adapter){ Class.new }
|
|
9
|
+
let(:selected_adapter_instance){ double }
|
|
10
|
+
let(:selected_adapter){
|
|
11
|
+
c = Class.new
|
|
12
|
+
expect(c).to receive(:new){ selected_adapter_instance }
|
|
13
|
+
c
|
|
14
|
+
}
|
|
15
|
+
let(:not_selected_adapter){
|
|
16
|
+
c = Class.new
|
|
17
|
+
expect(c).not_to receive(:new)
|
|
18
|
+
c
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
shared_examples_for "forwarding calls" do
|
|
22
|
+
it "#[]=" do
|
|
23
|
+
expect(selected_adapter_instance).to receive(:[]=).with('my_key', 'my_value')
|
|
24
|
+
expect_any_instance_of(not_selected_adapter).not_to receive(:[]=)
|
|
25
|
+
subject["my_key"] = "my_value"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "#[]" do
|
|
29
|
+
expect(selected_adapter_instance).to receive(:[]).with('my_key'){'my_value'}
|
|
30
|
+
expect_any_instance_of(not_selected_adapter).not_to receive(:[])
|
|
31
|
+
expect(subject["my_key"]).to eq('my_value')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "#delete" do
|
|
35
|
+
expect(selected_adapter_instance).to receive(:delete).with('my_key'){'my_value'}
|
|
36
|
+
expect_any_instance_of(not_selected_adapter).not_to receive(:delete)
|
|
37
|
+
expect(subject.delete("my_key")).to eq('my_value')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "#keys" do
|
|
41
|
+
expect(selected_adapter_instance).to receive(:keys){'my_value'}
|
|
42
|
+
expect_any_instance_of(not_selected_adapter).not_to receive(:keys)
|
|
43
|
+
expect(subject.keys).to eq('my_value')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context "when logged in" do
|
|
48
|
+
subject {
|
|
49
|
+
described_class.with_config(
|
|
50
|
+
logged_in: -> (context) { true },
|
|
51
|
+
logged_in_adapter: selected_adapter,
|
|
52
|
+
logged_out_adapter: not_selected_adapter
|
|
53
|
+
).new(context)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
it_should_behave_like "forwarding calls"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "when not logged in" do
|
|
60
|
+
subject {
|
|
61
|
+
described_class.with_config(
|
|
62
|
+
logged_in: -> (context) { false },
|
|
63
|
+
logged_in_adapter: not_selected_adapter,
|
|
64
|
+
logged_out_adapter: selected_adapter
|
|
65
|
+
).new(context)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
it_should_behave_like "forwarding calls"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe "when errors in config" do
|
|
72
|
+
before{
|
|
73
|
+
described_class.config.clear
|
|
74
|
+
}
|
|
75
|
+
let(:some_proc){ ->{} }
|
|
76
|
+
it "when no logged in adapter" do
|
|
77
|
+
expect{
|
|
78
|
+
described_class.with_config(
|
|
79
|
+
logged_in: some_proc,
|
|
80
|
+
logged_out_adapter: just_adapter
|
|
81
|
+
).new(context)
|
|
82
|
+
}.to raise_error(StandardError, /:logged_in_adapter/)
|
|
83
|
+
end
|
|
84
|
+
it "when no logged out adapter" do
|
|
85
|
+
expect{
|
|
86
|
+
described_class.with_config(
|
|
87
|
+
logged_in: some_proc,
|
|
88
|
+
logged_in_adapter: just_adapter
|
|
89
|
+
).new(context)
|
|
90
|
+
}.to raise_error(StandardError, /:logged_out_adapter/)
|
|
91
|
+
end
|
|
92
|
+
it "when no logged in detector" do
|
|
93
|
+
expect{
|
|
94
|
+
described_class.with_config(
|
|
95
|
+
logged_in_adapter: just_adapter,
|
|
96
|
+
logged_out_adapter: just_adapter
|
|
97
|
+
).new(context)
|
|
98
|
+
}.to raise_error(StandardError, /:logged_in$/)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Split::RedisInterface do
|
|
4
|
+
let(:list_name) { 'list_name' }
|
|
5
|
+
let(:set_name) { 'set_name' }
|
|
6
|
+
let(:interface) { described_class.new }
|
|
7
|
+
|
|
8
|
+
describe '#persist_list' do
|
|
9
|
+
subject(:persist_list) do
|
|
10
|
+
interface.persist_list(list_name, %w(a b c d))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
specify do
|
|
14
|
+
expect(persist_list).to eq %w(a b c d)
|
|
15
|
+
expect(Split.redis.lindex(list_name, 0)).to eq 'a'
|
|
16
|
+
expect(Split.redis.lindex(list_name, 1)).to eq 'b'
|
|
17
|
+
expect(Split.redis.lindex(list_name, 2)).to eq 'c'
|
|
18
|
+
expect(Split.redis.lindex(list_name, 3)).to eq 'd'
|
|
19
|
+
expect(Split.redis.llen(list_name)).to eq 4
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context 'list is overwritten but not deleted' do
|
|
23
|
+
specify do
|
|
24
|
+
expect(persist_list).to eq %w(a b c d)
|
|
25
|
+
interface.persist_list(list_name, ['z'])
|
|
26
|
+
expect(Split.redis.lindex(list_name, 0)).to eq 'z'
|
|
27
|
+
expect(Split.redis.llen(list_name)).to eq 1
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe '#add_to_list' do
|
|
33
|
+
subject(:add_to_list) do
|
|
34
|
+
interface.add_to_list(list_name, 'y')
|
|
35
|
+
interface.add_to_list(list_name, 'z')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
specify do
|
|
39
|
+
add_to_list
|
|
40
|
+
expect(Split.redis.lindex(list_name, 0)).to eq 'y'
|
|
41
|
+
expect(Split.redis.lindex(list_name, 1)).to eq 'z'
|
|
42
|
+
expect(Split.redis.llen(list_name)).to eq 2
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe '#set_list_index' do
|
|
47
|
+
subject(:set_list_index) do
|
|
48
|
+
interface.add_to_list(list_name, 'y')
|
|
49
|
+
interface.add_to_list(list_name, 'z')
|
|
50
|
+
interface.set_list_index(list_name, 0, 'a')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
specify do
|
|
54
|
+
set_list_index
|
|
55
|
+
expect(Split.redis.lindex(list_name, 0)).to eq 'a'
|
|
56
|
+
expect(Split.redis.lindex(list_name, 1)).to eq 'z'
|
|
57
|
+
expect(Split.redis.llen(list_name)).to eq 2
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe '#list_length' do
|
|
62
|
+
subject(:list_length) do
|
|
63
|
+
interface.add_to_list(list_name, 'y')
|
|
64
|
+
interface.add_to_list(list_name, 'z')
|
|
65
|
+
interface.list_length(list_name)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
specify do
|
|
69
|
+
expect(list_length).to eq 2
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe '#remove_last_item_from_list' do
|
|
74
|
+
subject(:remove_last_item_from_list) do
|
|
75
|
+
interface.add_to_list(list_name, 'y')
|
|
76
|
+
interface.add_to_list(list_name, 'z')
|
|
77
|
+
interface.remove_last_item_from_list(list_name)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
specify do
|
|
81
|
+
remove_last_item_from_list
|
|
82
|
+
expect(Split.redis.lindex(list_name, 0)).to eq 'y'
|
|
83
|
+
expect(Split.redis.llen(list_name)).to eq 1
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
describe '#make_list_length' do
|
|
88
|
+
subject(:make_list_length) do
|
|
89
|
+
interface.add_to_list(list_name, 'y')
|
|
90
|
+
interface.add_to_list(list_name, 'z')
|
|
91
|
+
interface.make_list_length(list_name, 1)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
specify do
|
|
95
|
+
make_list_length
|
|
96
|
+
expect(Split.redis.lindex(list_name, 0)).to eq 'y'
|
|
97
|
+
expect(Split.redis.llen(list_name)).to eq 1
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe '#add_to_set' do
|
|
102
|
+
subject(:add_to_set) do
|
|
103
|
+
interface.add_to_set(set_name, 'something')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
specify do
|
|
107
|
+
add_to_set
|
|
108
|
+
expect(Split.redis.sismember(set_name, 'something')).to be true
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -4,33 +4,34 @@ ENV['RACK_ENV'] = "test"
|
|
|
4
4
|
require 'rubygems'
|
|
5
5
|
require 'bundler/setup'
|
|
6
6
|
|
|
7
|
-
require '
|
|
8
|
-
|
|
7
|
+
require 'simplecov'
|
|
8
|
+
SimpleCov.start
|
|
9
9
|
|
|
10
10
|
require 'split'
|
|
11
11
|
require 'ostruct'
|
|
12
12
|
require 'yaml'
|
|
13
|
-
require 'complex' if RUBY_VERSION.match(/1\.8/)
|
|
14
13
|
|
|
15
14
|
Dir['./spec/support/*.rb'].each { |f| require f }
|
|
16
15
|
|
|
17
16
|
require "fakeredis"
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
G_fakeredis = Redis.new
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
module GlobalSharedContext
|
|
21
|
+
extend RSpec::SharedContext
|
|
22
|
+
let(:mock_user){ Split::User.new(double(session: {})) }
|
|
23
|
+
before(:each) do
|
|
24
24
|
Split.configuration = Split::Configuration.new
|
|
25
|
-
Split.redis =
|
|
25
|
+
Split.redis = G_fakeredis
|
|
26
26
|
Split.redis.flushall
|
|
27
27
|
@ab_user = mock_user
|
|
28
28
|
params = nil
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
RSpec.configure do |config|
|
|
33
|
+
config.order = 'random'
|
|
34
|
+
config.include GlobalSharedContext
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def session
|
data/spec/split_spec.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Split do
|
|
5
|
+
|
|
6
|
+
around(:each) do |ex|
|
|
7
|
+
old_env, old_redis = [ENV.delete('REDIS_URL'), Split.redis]
|
|
8
|
+
ex.run
|
|
9
|
+
ENV['REDIS_URL'] = old_env
|
|
10
|
+
Split.redis = old_redis
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#redis=' do
|
|
14
|
+
it 'accepts a url string' do
|
|
15
|
+
Split.redis = 'redis://localhost:6379'
|
|
16
|
+
expect(Split.redis).to be_a(Redis)
|
|
17
|
+
|
|
18
|
+
client = Split.redis.client
|
|
19
|
+
expect(client.host).to eq("localhost")
|
|
20
|
+
expect(client.port).to eq(6379)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'accepts an options hash' do
|
|
24
|
+
Split.redis = {host: 'localhost', port: 6379, db: 12}
|
|
25
|
+
expect(Split.redis).to be_a(Redis)
|
|
26
|
+
|
|
27
|
+
client = Split.redis.client
|
|
28
|
+
expect(client.host).to eq("localhost")
|
|
29
|
+
expect(client.port).to eq(6379)
|
|
30
|
+
expect(client.db).to eq(12)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'accepts a valid Redis instance' do
|
|
34
|
+
other_redis = Redis.new(url: "redis://localhost:6379")
|
|
35
|
+
Split.redis = other_redis
|
|
36
|
+
expect(Split.redis).to eq(other_redis)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'raises an ArgumentError when server cannot be determined' do
|
|
40
|
+
expect { Split.redis = Object.new }.to raise_error(ArgumentError)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/split.gemspec
CHANGED
|
@@ -21,15 +21,14 @@ Gem::Specification.new do |s|
|
|
|
21
21
|
s.require_paths = ["lib"]
|
|
22
22
|
|
|
23
23
|
s.add_dependency 'redis', '>= 2.1'
|
|
24
|
-
s.add_dependency 'redis-namespace', '>= 1.1.0'
|
|
25
24
|
s.add_dependency 'sinatra', '>= 1.2.6'
|
|
26
25
|
s.add_dependency 'simple-random', '>= 0.9.3'
|
|
27
26
|
|
|
28
27
|
s.add_development_dependency 'bundler', '~> 1.10'
|
|
29
|
-
s.add_development_dependency '
|
|
28
|
+
s.add_development_dependency 'simplecov', '~> 0.12'
|
|
30
29
|
s.add_development_dependency 'rack-test', '~> 0.6'
|
|
31
30
|
s.add_development_dependency 'rake', '~> 11.1'
|
|
32
31
|
s.add_development_dependency 'rspec', '~> 3.4'
|
|
33
32
|
s.add_development_dependency 'pry', '~> 0.10'
|
|
34
|
-
s.add_development_dependency 'fakeredis', '~> 0.
|
|
33
|
+
s.add_development_dependency 'fakeredis', '~> 0.6.0'
|
|
35
34
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: split
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Nesbitt
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2017-03-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: redis
|
|
@@ -24,20 +24,6 @@ dependencies:
|
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '2.1'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: redis-namespace
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.1.0
|
|
34
|
-
type: :runtime
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">="
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: 1.1.0
|
|
41
27
|
- !ruby/object:Gem::Dependency
|
|
42
28
|
name: sinatra
|
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -81,19 +67,19 @@ dependencies:
|
|
|
81
67
|
- !ruby/object:Gem::Version
|
|
82
68
|
version: '1.10'
|
|
83
69
|
- !ruby/object:Gem::Dependency
|
|
84
|
-
name:
|
|
70
|
+
name: simplecov
|
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
|
86
72
|
requirements:
|
|
87
73
|
- - "~>"
|
|
88
74
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '0.
|
|
75
|
+
version: '0.12'
|
|
90
76
|
type: :development
|
|
91
77
|
prerelease: false
|
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
79
|
requirements:
|
|
94
80
|
- - "~>"
|
|
95
81
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '0.
|
|
82
|
+
version: '0.12'
|
|
97
83
|
- !ruby/object:Gem::Dependency
|
|
98
84
|
name: rack-test
|
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -156,14 +142,14 @@ dependencies:
|
|
|
156
142
|
requirements:
|
|
157
143
|
- - "~>"
|
|
158
144
|
- !ruby/object:Gem::Version
|
|
159
|
-
version: 0.
|
|
145
|
+
version: 0.6.0
|
|
160
146
|
type: :development
|
|
161
147
|
prerelease: false
|
|
162
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
163
149
|
requirements:
|
|
164
150
|
- - "~>"
|
|
165
151
|
- !ruby/object:Gem::Version
|
|
166
|
-
version: 0.
|
|
152
|
+
version: 0.6.0
|
|
167
153
|
description:
|
|
168
154
|
email:
|
|
169
155
|
- andrewnez@gmail.com
|
|
@@ -171,7 +157,12 @@ executables: []
|
|
|
171
157
|
extensions: []
|
|
172
158
|
extra_rdoc_files: []
|
|
173
159
|
files:
|
|
160
|
+
- ".codeclimate.yml"
|
|
161
|
+
- ".csslintrc"
|
|
162
|
+
- ".eslintignore"
|
|
163
|
+
- ".eslintrc"
|
|
174
164
|
- ".gitignore"
|
|
165
|
+
- ".rubocop.yml"
|
|
175
166
|
- ".travis.yml"
|
|
176
167
|
- Appraisals
|
|
177
168
|
- CHANGELOG.md
|
|
@@ -181,10 +172,10 @@ files:
|
|
|
181
172
|
- LICENSE
|
|
182
173
|
- README.md
|
|
183
174
|
- Rakefile
|
|
184
|
-
- gemfiles/4.1.gemfile
|
|
185
175
|
- gemfiles/4.2.gemfile
|
|
176
|
+
- gemfiles/5.0.gemfile
|
|
186
177
|
- lib/split.rb
|
|
187
|
-
- lib/split/algorithms.rb
|
|
178
|
+
- lib/split/algorithms/block_randomization.rb
|
|
188
179
|
- lib/split/algorithms/weighted_sample.rb
|
|
189
180
|
- lib/split/algorithms/whiplash.rb
|
|
190
181
|
- lib/split/alternative.rb
|
|
@@ -206,8 +197,6 @@ files:
|
|
|
206
197
|
- lib/split/exceptions.rb
|
|
207
198
|
- lib/split/experiment.rb
|
|
208
199
|
- lib/split/experiment_catalog.rb
|
|
209
|
-
- lib/split/extensions.rb
|
|
210
|
-
- lib/split/extensions/array.rb
|
|
211
200
|
- lib/split/extensions/string.rb
|
|
212
201
|
- lib/split/goals_collection.rb
|
|
213
202
|
- lib/split/helper.rb
|
|
@@ -217,10 +206,12 @@ files:
|
|
|
217
206
|
- lib/split/persistence/dual_adapter.rb
|
|
218
207
|
- lib/split/persistence/redis_adapter.rb
|
|
219
208
|
- lib/split/persistence/session_adapter.rb
|
|
209
|
+
- lib/split/redis_interface.rb
|
|
220
210
|
- lib/split/trial.rb
|
|
221
211
|
- lib/split/user.rb
|
|
222
212
|
- lib/split/version.rb
|
|
223
213
|
- lib/split/zscore.rb
|
|
214
|
+
- spec/algorithms/block_randomization_spec.rb
|
|
224
215
|
- spec/algorithms/weighted_sample_spec.rb
|
|
225
216
|
- spec/algorithms/whiplash_spec.rb
|
|
226
217
|
- spec/alternative_spec.rb
|
|
@@ -234,10 +225,13 @@ files:
|
|
|
234
225
|
- spec/helper_spec.rb
|
|
235
226
|
- spec/metric_spec.rb
|
|
236
227
|
- spec/persistence/cookie_adapter_spec.rb
|
|
228
|
+
- spec/persistence/dual_adapter_spec.rb
|
|
237
229
|
- spec/persistence/redis_adapter_spec.rb
|
|
238
230
|
- spec/persistence/session_adapter_spec.rb
|
|
239
231
|
- spec/persistence_spec.rb
|
|
232
|
+
- spec/redis_interface_spec.rb
|
|
240
233
|
- spec/spec_helper.rb
|
|
234
|
+
- spec/split_spec.rb
|
|
241
235
|
- spec/support/cookies_mock.rb
|
|
242
236
|
- spec/trial_spec.rb
|
|
243
237
|
- spec/user_spec.rb
|
|
@@ -267,6 +261,7 @@ signing_key:
|
|
|
267
261
|
specification_version: 4
|
|
268
262
|
summary: Rack based split testing framework
|
|
269
263
|
test_files:
|
|
264
|
+
- spec/algorithms/block_randomization_spec.rb
|
|
270
265
|
- spec/algorithms/weighted_sample_spec.rb
|
|
271
266
|
- spec/algorithms/whiplash_spec.rb
|
|
272
267
|
- spec/alternative_spec.rb
|
|
@@ -280,11 +275,13 @@ test_files:
|
|
|
280
275
|
- spec/helper_spec.rb
|
|
281
276
|
- spec/metric_spec.rb
|
|
282
277
|
- spec/persistence/cookie_adapter_spec.rb
|
|
278
|
+
- spec/persistence/dual_adapter_spec.rb
|
|
283
279
|
- spec/persistence/redis_adapter_spec.rb
|
|
284
280
|
- spec/persistence/session_adapter_spec.rb
|
|
285
281
|
- spec/persistence_spec.rb
|
|
282
|
+
- spec/redis_interface_spec.rb
|
|
286
283
|
- spec/spec_helper.rb
|
|
284
|
+
- spec/split_spec.rb
|
|
287
285
|
- spec/support/cookies_mock.rb
|
|
288
286
|
- spec/trial_spec.rb
|
|
289
287
|
- spec/user_spec.rb
|
|
290
|
-
has_rdoc:
|
data/gemfiles/4.1.gemfile
DELETED
data/lib/split/algorithms.rb
DELETED
data/lib/split/extensions.rb
DELETED