split 2.2.0 → 3.3.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 +5 -5
- data/.rubocop.yml +2 -2
- data/.travis.yml +39 -3
- data/Appraisals +8 -5
- data/CHANGELOG.md +59 -0
- data/CONTRIBUTING.md +54 -5
- data/LICENSE +1 -1
- data/README.md +193 -113
- data/gemfiles/4.2.gemfile +1 -1
- data/gemfiles/5.0.gemfile +1 -2
- data/gemfiles/{4.1.gemfile → 5.1.gemfile} +2 -2
- data/gemfiles/5.2.gemfile +9 -0
- data/lib/split/algorithms/block_randomization.rb +22 -0
- data/lib/split/alternative.rb +32 -7
- data/lib/split/combined_experiments_helper.rb +37 -0
- data/lib/split/configuration.rb +8 -0
- data/lib/split/dashboard/helpers.rb +5 -1
- data/lib/split/dashboard/pagination_helpers.rb +87 -0
- data/lib/split/dashboard/paginator.rb +16 -0
- data/lib/split/dashboard/public/style.css +9 -0
- data/lib/split/dashboard/views/_experiment.erb +31 -1
- data/lib/split/dashboard/views/index.erb +5 -1
- data/lib/split/dashboard.rb +2 -0
- data/lib/split/encapsulated_helper.rb +2 -0
- data/lib/split/engine.rb +2 -0
- data/lib/split/experiment.rb +5 -4
- data/lib/split/helper.rb +34 -1
- data/lib/split/persistence/cookie_adapter.rb +53 -15
- data/lib/split/persistence/dual_adapter.rb +3 -0
- data/lib/split/persistence.rb +5 -3
- data/lib/split/redis_interface.rb +1 -3
- data/lib/split/user.rb +2 -0
- data/lib/split/version.rb +2 -2
- data/lib/split/zscore.rb +1 -1
- data/lib/split.rb +21 -19
- data/spec/algorithms/block_randomization_spec.rb +32 -0
- data/spec/alternative_spec.rb +43 -0
- data/spec/combined_experiments_helper_spec.rb +57 -0
- data/spec/dashboard/pagination_helpers_spec.rb +198 -0
- data/spec/dashboard/paginator_spec.rb +37 -0
- data/spec/dashboard_helpers_spec.rb +14 -0
- data/spec/experiment_spec.rb +1 -3
- data/spec/helper_spec.rb +20 -0
- data/spec/persistence/cookie_adapter_spec.rb +90 -23
- data/spec/persistence/dual_adapter_spec.rb +2 -2
- data/spec/split_spec.rb +7 -7
- data/split.gemspec +16 -6
- metadata +36 -19
- data/lib/split/algorithms.rb +0 -4
- data/lib/split/extensions.rb +0 -4
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
require 'split/combined_experiments_helper'
|
|
4
|
+
|
|
5
|
+
describe Split::CombinedExperimentsHelper do
|
|
6
|
+
include Split::CombinedExperimentsHelper
|
|
7
|
+
|
|
8
|
+
describe 'ab_combined_test' do
|
|
9
|
+
let!(:config_enabled) { true }
|
|
10
|
+
let!(:combined_experiments) { [:exp_1_click, :exp_1_scroll ]}
|
|
11
|
+
let!(:allow_multiple_experiments) { true }
|
|
12
|
+
|
|
13
|
+
before do
|
|
14
|
+
Split.configuration.experiments = {
|
|
15
|
+
:combined_exp_1 => {
|
|
16
|
+
:alternatives => [ {"control"=> 0.5}, {"test-alt"=> 0.5} ],
|
|
17
|
+
:metric => :my_metric,
|
|
18
|
+
:combined_experiments => combined_experiments
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
Split.configuration.enabled = config_enabled
|
|
22
|
+
Split.configuration.allow_multiple_experiments = allow_multiple_experiments
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'without config enabled' do
|
|
26
|
+
let!(:config_enabled) { false }
|
|
27
|
+
|
|
28
|
+
it "raises an error" do
|
|
29
|
+
expect(lambda { ab_combined_test :combined_exp_1 }).to raise_error(Split::InvalidExperimentsFormatError )
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context 'multiple experiments disabled' do
|
|
34
|
+
let!(:allow_multiple_experiments) { false }
|
|
35
|
+
|
|
36
|
+
it "raises an error if multiple experiments is disabled" do
|
|
37
|
+
expect(lambda { ab_combined_test :combined_exp_1 }).to raise_error(Split::InvalidExperimentsFormatError)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'without combined experiments' do
|
|
42
|
+
let!(:combined_experiments) { nil }
|
|
43
|
+
|
|
44
|
+
it "raises an error" do
|
|
45
|
+
expect(lambda { ab_combined_test :combined_exp_1 }).to raise_error(Split::InvalidExperimentsFormatError )
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "uses same alternative for all sub experiments and returns the alternative" do
|
|
50
|
+
allow(self).to receive(:get_alternative) { "test-alt" }
|
|
51
|
+
expect(self).to receive(:ab_test).with(:exp_1_click, {"control"=>0.5}, {"test-alt"=>0.5}) { "test-alt" }
|
|
52
|
+
expect(self).to receive(:ab_test).with(:exp_1_scroll, [{"control" => 0, "test-alt" => 1}])
|
|
53
|
+
|
|
54
|
+
expect(ab_combined_test('combined_exp_1')).to eq('test-alt')
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'split/dashboard/pagination_helpers'
|
|
3
|
+
|
|
4
|
+
describe Split::DashboardPaginationHelpers do
|
|
5
|
+
include Split::DashboardPaginationHelpers
|
|
6
|
+
|
|
7
|
+
let(:url) { '/split/' }
|
|
8
|
+
|
|
9
|
+
describe '#pagination_per' do
|
|
10
|
+
context 'when params empty' do
|
|
11
|
+
let(:params) { Hash[] }
|
|
12
|
+
|
|
13
|
+
it 'returns 10' do
|
|
14
|
+
expect(pagination_per).to eql 10
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context 'when params[:per] is 5' do
|
|
19
|
+
let(:params) { Hash[per: 5] }
|
|
20
|
+
|
|
21
|
+
it 'returns 5' do
|
|
22
|
+
expect(pagination_per).to eql 5
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe '#page_number' do
|
|
28
|
+
context 'when params empty' do
|
|
29
|
+
let(:params) { Hash[] }
|
|
30
|
+
|
|
31
|
+
it 'returns 1' do
|
|
32
|
+
expect(page_number).to eql 1
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'when params[:page] is "2"' do
|
|
37
|
+
let(:params) { Hash[page: '2'] }
|
|
38
|
+
|
|
39
|
+
it 'returns 2' do
|
|
40
|
+
expect(page_number).to eql 2
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe '#paginated' do
|
|
46
|
+
let(:collection) { (1..20).to_a }
|
|
47
|
+
let(:params) { Hash[per: '5', page: '3'] }
|
|
48
|
+
|
|
49
|
+
it { expect(paginated(collection)).to eql [11, 12, 13, 14, 15] }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe '#show_first_page_tag?' do
|
|
53
|
+
context 'when page is 1' do
|
|
54
|
+
it { expect(show_first_page_tag?).to be false }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'when page is 3' do
|
|
58
|
+
let(:params) { Hash[page: '3'] }
|
|
59
|
+
it { expect(show_first_page_tag?).to be true }
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe '#first_page_tag' do
|
|
64
|
+
it { expect(first_page_tag).to eql '<a href="/split?page=1&per=10">1</a>' }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe '#show_first_ellipsis_tag?' do
|
|
68
|
+
context 'when page is 1' do
|
|
69
|
+
it { expect(show_first_ellipsis_tag?).to be false }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context 'when page is 4' do
|
|
73
|
+
let(:params) { Hash[page: '4'] }
|
|
74
|
+
it { expect(show_first_ellipsis_tag?).to be true }
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe '#ellipsis_tag' do
|
|
79
|
+
it { expect(ellipsis_tag).to eql '<span>...</span>' }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe '#show_prev_page_tag?' do
|
|
83
|
+
context 'when page is 1' do
|
|
84
|
+
it { expect(show_prev_page_tag?).to be false }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
context 'when page is 2' do
|
|
88
|
+
let(:params) { Hash[page: '2'] }
|
|
89
|
+
it { expect(show_prev_page_tag?).to be true }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe '#prev_page_tag' do
|
|
94
|
+
context 'when page is 2' do
|
|
95
|
+
let(:params) { Hash[page: '2'] }
|
|
96
|
+
|
|
97
|
+
it do
|
|
98
|
+
expect(prev_page_tag).to eql '<a href="/split?page=1&per=10">1</a>'
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
context 'when page is 3' do
|
|
103
|
+
let(:params) { Hash[page: '3'] }
|
|
104
|
+
|
|
105
|
+
it do
|
|
106
|
+
expect(prev_page_tag).to eql '<a href="/split?page=2&per=10">2</a>'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe '#show_prev_page_tag?' do
|
|
112
|
+
context 'when page is 1' do
|
|
113
|
+
it { expect(show_prev_page_tag?).to be false }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
context 'when page is 2' do
|
|
117
|
+
let(:params) { Hash[page: '2'] }
|
|
118
|
+
it { expect(show_prev_page_tag?).to be true }
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe '#current_page_tag' do
|
|
123
|
+
context 'when page is 1' do
|
|
124
|
+
let(:params) { Hash[page: '1'] }
|
|
125
|
+
it { expect(current_page_tag).to eql '<span><b>1</b></span>' }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context 'when page is 2' do
|
|
129
|
+
let(:params) { Hash[page: '2'] }
|
|
130
|
+
it { expect(current_page_tag).to eql '<span><b>2</b></span>' }
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
describe '#show_next_page_tag?' do
|
|
135
|
+
context 'when page is 2' do
|
|
136
|
+
let(:params) { Hash[page: '2'] }
|
|
137
|
+
|
|
138
|
+
context 'when collection length is 20' do
|
|
139
|
+
let(:collection) { (1..20).to_a }
|
|
140
|
+
it { expect(show_next_page_tag?(collection)).to be false }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context 'when collection length is 25' do
|
|
144
|
+
let(:collection) { (1..25).to_a }
|
|
145
|
+
it { expect(show_next_page_tag?(collection)).to be true }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
describe '#next_page_tag' do
|
|
151
|
+
context 'when page is 1' do
|
|
152
|
+
let(:params) { Hash[page: '1'] }
|
|
153
|
+
it { expect(next_page_tag).to eql '<a href="/split?page=2&per=10">2</a>' }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
context 'when page is 2' do
|
|
157
|
+
let(:params) { Hash[page: '2'] }
|
|
158
|
+
it { expect(next_page_tag).to eql '<a href="/split?page=3&per=10">3</a>' }
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
describe '#total_pages' do
|
|
163
|
+
context 'when collection length is 30' do
|
|
164
|
+
let(:collection) { (1..30).to_a }
|
|
165
|
+
it { expect(total_pages(collection)).to eql 3 }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
context 'when collection length is 35' do
|
|
169
|
+
let(:collection) { (1..35).to_a }
|
|
170
|
+
it { expect(total_pages(collection)).to eql 4 }
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
describe '#show_last_ellipsis_tag?' do
|
|
175
|
+
let(:collection) { (1..30).to_a }
|
|
176
|
+
let(:params) { Hash[per: '5', page: '2'] }
|
|
177
|
+
it { expect(show_last_ellipsis_tag?(collection)).to be true }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
describe '#show_last_page_tag?' do
|
|
181
|
+
let(:collection) { (1..30).to_a }
|
|
182
|
+
|
|
183
|
+
context 'when page is 5/6' do
|
|
184
|
+
let(:params) { Hash[per: '5', page: '5'] }
|
|
185
|
+
it { expect(show_last_page_tag?(collection)).to be false }
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
context 'when page is 4/6' do
|
|
189
|
+
let(:params) { Hash[per: '5', page: '4'] }
|
|
190
|
+
it { expect(show_last_page_tag?(collection)).to be true }
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
describe '#last_page_tag' do
|
|
195
|
+
let(:collection) { (1..30).to_a }
|
|
196
|
+
it { expect(last_page_tag(collection)).to eql '<a href="/split?page=3&per=10">3</a>' }
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
require 'split/dashboard/paginator'
|
|
4
|
+
|
|
5
|
+
describe Split::DashboardPaginator do
|
|
6
|
+
context 'when collection is 1..20' do
|
|
7
|
+
let(:collection) { (1..20).to_a }
|
|
8
|
+
|
|
9
|
+
context 'when per 5 for page' do
|
|
10
|
+
let(:per) { 5 }
|
|
11
|
+
|
|
12
|
+
it 'when page number is 1 result is [1, 2, 3, 4, 5]' do
|
|
13
|
+
result = Split::DashboardPaginator.new(collection, 1, per).paginate
|
|
14
|
+
expect(result).to eql [1, 2, 3, 4, 5]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'when page number is 2 result is [6, 7, 8, 9, 10]' do
|
|
18
|
+
result = Split::DashboardPaginator.new(collection, 2, per).paginate
|
|
19
|
+
expect(result).to eql [6, 7, 8, 9, 10]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context 'when per 10 for page' do
|
|
24
|
+
let(:per) { 10 }
|
|
25
|
+
|
|
26
|
+
it 'when page number is 1 result is [1..10]' do
|
|
27
|
+
result = Split::DashboardPaginator.new(collection, 1, per).paginate
|
|
28
|
+
expect(result).to eql [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'when page number is 2 result is [10..20]' do
|
|
32
|
+
result = Split::DashboardPaginator.new(collection, 2, per).paginate
|
|
33
|
+
expect(result).to eql [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -24,5 +24,19 @@ describe Split::DashboardHelpers do
|
|
|
24
24
|
expect(confidence_level(2.58)).to eq('99% confidence')
|
|
25
25
|
expect(confidence_level(3.00)).to eq('99% confidence')
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
describe '#round' do
|
|
29
|
+
it 'can round number strings' do
|
|
30
|
+
expect(round('3.1415')).to eq BigDecimal.new('3.14')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'can round number strings for precsion' do
|
|
34
|
+
expect(round('3.1415', 1)).to eq BigDecimal.new('3.1')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'can handle invalid number strings' do
|
|
38
|
+
expect(round('N/A')).to be_zero
|
|
39
|
+
end
|
|
40
|
+
end
|
|
27
41
|
end
|
|
28
42
|
end
|
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
|
|
@@ -460,7 +458,7 @@ describe Split::Experiment do
|
|
|
460
458
|
expect(experiment.alternatives[0].p_winner).to be_within(0.04).of(0.50)
|
|
461
459
|
end
|
|
462
460
|
|
|
463
|
-
it "should calculate the probability of being the winning alternative separately for each goal" do
|
|
461
|
+
it "should calculate the probability of being the winning alternative separately for each goal", :skip => true do
|
|
464
462
|
experiment = Split::ExperimentCatalog.find_or_create({'link_color3' => ["purchase", "refund"]}, 'blue', 'red', 'green')
|
|
465
463
|
goal1 = experiment.goals[0]
|
|
466
464
|
goal2 = experiment.goals[1]
|
data/spec/helper_spec.rb
CHANGED
|
@@ -35,6 +35,18 @@ describe Split::Helper do
|
|
|
35
35
|
expect(lambda { ab_test({'link_color' => "purchase"}, 'blue', 'red') }).not_to raise_error
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
it "raises an appropriate error when processing combined expirements" do
|
|
39
|
+
Split.configuration.experiments = {
|
|
40
|
+
:combined_exp_1 => {
|
|
41
|
+
:alternatives => [ { name: "control", percent: 50 }, { name: "test-alt", percent: 50 } ],
|
|
42
|
+
:metric => :my_metric,
|
|
43
|
+
:combined_experiments => [:combined_exp_1_sub_1]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
Split::ExperimentCatalog.find_or_create('combined_exp_1')
|
|
47
|
+
expect(lambda { ab_test('combined_exp_1')}).to raise_error(Split::InvalidExperimentsFormatError )
|
|
48
|
+
end
|
|
49
|
+
|
|
38
50
|
it "should assign a random alternative to a new user when there are an equal number of alternatives assigned" do
|
|
39
51
|
ab_test('link_color', 'blue', 'red')
|
|
40
52
|
expect(['red', 'blue']).to include(ab_user['link_color'])
|
|
@@ -687,6 +699,14 @@ describe Split::Helper do
|
|
|
687
699
|
end
|
|
688
700
|
end
|
|
689
701
|
|
|
702
|
+
describe 'when user is previewing' do
|
|
703
|
+
before(:each) do
|
|
704
|
+
@request = OpenStruct.new(headers: { 'x-purpose' => 'preview' })
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
it_behaves_like "a disabled test"
|
|
708
|
+
end
|
|
709
|
+
|
|
690
710
|
describe 'versioned experiments' do
|
|
691
711
|
it "should use version zero if no version is present" do
|
|
692
712
|
alternative_name = ab_test('link_color', 'blue', 'red')
|
|
@@ -1,39 +1,106 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "spec_helper"
|
|
3
|
+
require 'rack/test'
|
|
3
4
|
|
|
4
5
|
describe Split::Persistence::CookieAdapter do
|
|
6
|
+
subject { described_class.new(context) }
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
shared_examples "sets cookies correctly" do
|
|
9
|
+
describe "#[] and #[]=" do
|
|
10
|
+
it "set and return the value for given key" do
|
|
11
|
+
subject["my_key"] = "my_value"
|
|
12
|
+
expect(subject["my_key"]).to eq("my_value")
|
|
13
|
+
end
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
it "handles invalid JSON" do
|
|
16
|
+
context.request.cookies[:split] = {
|
|
17
|
+
:value => '{"foo":2,',
|
|
18
|
+
:expires => Time.now
|
|
19
|
+
}
|
|
20
|
+
expect(subject["my_key"]).to be_nil
|
|
21
|
+
subject["my_key"] = "my_value"
|
|
22
|
+
expect(subject["my_key"]).to eq("my_value")
|
|
23
|
+
end
|
|
13
24
|
end
|
|
14
|
-
end
|
|
15
25
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
describe "#delete" do
|
|
27
|
+
it "should delete the given key" do
|
|
28
|
+
subject["my_key"] = "my_value"
|
|
29
|
+
subject.delete("my_key")
|
|
30
|
+
expect(subject["my_key"]).to be_nil
|
|
31
|
+
end
|
|
21
32
|
end
|
|
22
|
-
end
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
describe "#keys" do
|
|
35
|
+
it "should return an array of the session's stored keys" do
|
|
36
|
+
subject["my_key"] = "my_value"
|
|
37
|
+
subject["my_second_key"] = "my_second_value"
|
|
38
|
+
expect(subject.keys).to match(["my_key", "my_second_key"])
|
|
39
|
+
end
|
|
29
40
|
end
|
|
30
41
|
end
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
|
|
44
|
+
context "when using Rack" do
|
|
45
|
+
let(:env) { Rack::MockRequest.env_for("http://example.com:8080/") }
|
|
46
|
+
let(:request) { Rack::Request.new(env) }
|
|
47
|
+
let(:response) { Rack::MockResponse.new(200, {}, "") }
|
|
48
|
+
let(:context) { double(request: request, response: response, cookies: CookiesMock.new) }
|
|
49
|
+
|
|
50
|
+
include_examples "sets cookies correctly"
|
|
51
|
+
|
|
52
|
+
it "puts multiple experiments in a single cookie" do
|
|
53
|
+
subject["foo"] = "FOO"
|
|
54
|
+
subject["bar"] = "BAR"
|
|
55
|
+
expect(context.response.headers["Set-Cookie"]).to match(/\Asplit=%7B%22foo%22%3A%22FOO%22%2C%22bar%22%3A%22BAR%22%7D; path=\/; expires=[a-zA-Z]{3}, \d{2} [a-zA-Z]{3} \d{4} \d{2}:\d{2}:\d{2} -0000\Z/)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "ensure other added cookies are not overriden" do
|
|
59
|
+
context.response.set_cookie 'dummy', 'wow'
|
|
60
|
+
subject["foo"] = "FOO"
|
|
61
|
+
expect(context.response.headers["Set-Cookie"]).to include("dummy=wow")
|
|
62
|
+
expect(context.response.headers["Set-Cookie"]).to include("split=")
|
|
63
|
+
end
|
|
37
64
|
end
|
|
38
65
|
|
|
66
|
+
context "when @context is an ActionController::Base" do
|
|
67
|
+
before :context do
|
|
68
|
+
require "rails"
|
|
69
|
+
require "action_controller/railtie"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
let(:context) do
|
|
73
|
+
controller = controller_class.new
|
|
74
|
+
if controller.respond_to?(:set_request!)
|
|
75
|
+
controller.set_request!(ActionDispatch::Request.new({}))
|
|
76
|
+
else # Before rails 5.0
|
|
77
|
+
controller.send(:"request=", ActionDispatch::Request.new({}))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
response = ActionDispatch::Response.new(200, {}, '').tap do |res|
|
|
81
|
+
res.request = controller.request
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if controller.respond_to?(:set_response!)
|
|
85
|
+
controller.set_response!(response)
|
|
86
|
+
else # Before rails 5.0
|
|
87
|
+
controller.send(:set_response!, response)
|
|
88
|
+
end
|
|
89
|
+
controller
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
let(:controller_class) { Class.new(ActionController::Base) }
|
|
93
|
+
|
|
94
|
+
include_examples "sets cookies correctly"
|
|
95
|
+
|
|
96
|
+
it "puts multiple experiments in a single cookie" do
|
|
97
|
+
subject["foo"] = "FOO"
|
|
98
|
+
subject["bar"] = "BAR"
|
|
99
|
+
expect(subject.keys).to eq(["foo", "bar"])
|
|
100
|
+
expect(subject["foo"]).to eq("FOO")
|
|
101
|
+
expect(subject["bar"]).to eq("BAR")
|
|
102
|
+
cookie_jar = context.request.env["action_dispatch.cookies"]
|
|
103
|
+
expect(cookie_jar['split']).to eq("{\"foo\":\"FOO\",\"bar\":\"BAR\"}")
|
|
104
|
+
end
|
|
105
|
+
end
|
|
39
106
|
end
|
|
@@ -47,7 +47,7 @@ describe Split::Persistence::DualAdapter do
|
|
|
47
47
|
context "when logged in" do
|
|
48
48
|
subject {
|
|
49
49
|
described_class.with_config(
|
|
50
|
-
logged_in:
|
|
50
|
+
logged_in: lambda { |context| true },
|
|
51
51
|
logged_in_adapter: selected_adapter,
|
|
52
52
|
logged_out_adapter: not_selected_adapter
|
|
53
53
|
).new(context)
|
|
@@ -59,7 +59,7 @@ describe Split::Persistence::DualAdapter do
|
|
|
59
59
|
context "when not logged in" do
|
|
60
60
|
subject {
|
|
61
61
|
described_class.with_config(
|
|
62
|
-
logged_in:
|
|
62
|
+
logged_in: lambda { |context| false },
|
|
63
63
|
logged_in_adapter: not_selected_adapter,
|
|
64
64
|
logged_out_adapter: selected_adapter
|
|
65
65
|
).new(context)
|
data/spec/split_spec.rb
CHANGED
|
@@ -15,19 +15,19 @@ RSpec.describe Split do
|
|
|
15
15
|
Split.redis = 'redis://localhost:6379'
|
|
16
16
|
expect(Split.redis).to be_a(Redis)
|
|
17
17
|
|
|
18
|
-
client = Split.redis.
|
|
19
|
-
expect(client
|
|
20
|
-
expect(client
|
|
18
|
+
client = Split.redis.connection
|
|
19
|
+
expect(client[:host]).to eq("localhost")
|
|
20
|
+
expect(client[:port]).to eq(6379)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
it 'accepts an options hash' do
|
|
24
24
|
Split.redis = {host: 'localhost', port: 6379, db: 12}
|
|
25
25
|
expect(Split.redis).to be_a(Redis)
|
|
26
26
|
|
|
27
|
-
client = Split.redis.
|
|
28
|
-
expect(client
|
|
29
|
-
expect(client
|
|
30
|
-
expect(client
|
|
27
|
+
client = Split.redis.connection
|
|
28
|
+
expect(client[:host]).to eq("localhost")
|
|
29
|
+
expect(client[:port]).to eq(6379)
|
|
30
|
+
expect(client[:db]).to eq(12)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
it 'accepts a valid Redis instance' do
|
data/split.gemspec
CHANGED
|
@@ -12,7 +12,17 @@ Gem::Specification.new do |s|
|
|
|
12
12
|
s.homepage = "https://github.com/splitrb/split"
|
|
13
13
|
s.summary = "Rack based split testing framework"
|
|
14
14
|
|
|
15
|
-
s.
|
|
15
|
+
s.metadata = {
|
|
16
|
+
"homepage_uri" => "https://github.com/splitrb/split",
|
|
17
|
+
"changelog_uri" => "https://github.com/splitrb/split/blob/master/CHANGELOG.md",
|
|
18
|
+
"source_code_uri" => "https://github.com/splitrb/split",
|
|
19
|
+
"bug_tracker_uri" => "https://github.com/splitrb/split/issues",
|
|
20
|
+
"wiki_uri" => "https://github.com/splitrb/split/wiki",
|
|
21
|
+
"mailing_list_uri" => "https://groups.google.com/d/forum/split-ruby"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
s.required_ruby_version = '>= 1.9.3'
|
|
25
|
+
s.required_rubygems_version = '>= 2.0.0'
|
|
16
26
|
|
|
17
27
|
s.rubyforge_project = "split"
|
|
18
28
|
|
|
@@ -24,11 +34,11 @@ Gem::Specification.new do |s|
|
|
|
24
34
|
s.add_dependency 'sinatra', '>= 1.2.6'
|
|
25
35
|
s.add_dependency 'simple-random', '>= 0.9.3'
|
|
26
36
|
|
|
27
|
-
s.add_development_dependency 'bundler', '~> 1.
|
|
28
|
-
s.add_development_dependency 'simplecov', '~> 0.
|
|
37
|
+
s.add_development_dependency 'bundler', '~> 1.14'
|
|
38
|
+
s.add_development_dependency 'simplecov', '~> 0.15'
|
|
29
39
|
s.add_development_dependency 'rack-test', '~> 0.6'
|
|
30
|
-
s.add_development_dependency 'rake', '~>
|
|
31
|
-
s.add_development_dependency 'rspec', '~> 3.
|
|
40
|
+
s.add_development_dependency 'rake', '~> 12'
|
|
41
|
+
s.add_development_dependency 'rspec', '~> 3.7'
|
|
32
42
|
s.add_development_dependency 'pry', '~> 0.10'
|
|
33
|
-
s.add_development_dependency 'fakeredis', '~> 0.
|
|
43
|
+
s.add_development_dependency 'fakeredis', '~> 0.7'
|
|
34
44
|
end
|