split 3.3.2 → 4.0.0.pre2
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/.eslintrc +1 -1
- data/.github/FUNDING.yml +1 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
- data/.github/dependabot.yml +7 -0
- data/.github/workflows/ci.yml +61 -0
- data/.rspec +1 -0
- data/.rubocop.yml +71 -1044
- data/.rubocop_todo.yml +226 -0
- data/Appraisals +1 -1
- data/CHANGELOG.md +62 -0
- data/CODE_OF_CONDUCT.md +3 -3
- data/Gemfile +2 -0
- data/README.md +40 -18
- data/Rakefile +2 -0
- data/gemfiles/6.0.gemfile +1 -1
- data/lib/split/algorithms/block_randomization.rb +2 -0
- data/lib/split/algorithms/weighted_sample.rb +2 -1
- data/lib/split/algorithms/whiplash.rb +3 -2
- data/lib/split/alternative.rb +4 -3
- data/lib/split/cache.rb +28 -0
- data/lib/split/combined_experiments_helper.rb +2 -1
- data/lib/split/configuration.rb +13 -14
- data/lib/split/dashboard/helpers.rb +1 -0
- data/lib/split/dashboard/pagination_helpers.rb +3 -3
- data/lib/split/dashboard/paginator.rb +1 -0
- data/lib/split/dashboard/public/dashboard.js +10 -0
- data/lib/split/dashboard/public/style.css +5 -0
- data/lib/split/dashboard/views/_controls.erb +13 -0
- data/lib/split/dashboard/views/layout.erb +1 -1
- data/lib/split/dashboard.rb +19 -1
- data/lib/split/encapsulated_helper.rb +3 -2
- data/lib/split/engine.rb +7 -4
- data/lib/split/exceptions.rb +1 -0
- data/lib/split/experiment.rb +98 -65
- data/lib/split/experiment_catalog.rb +1 -3
- data/lib/split/extensions/string.rb +1 -0
- data/lib/split/goals_collection.rb +2 -0
- data/lib/split/helper.rb +28 -8
- data/lib/split/metric.rb +2 -1
- data/lib/split/persistence/cookie_adapter.rb +6 -1
- data/lib/split/persistence/dual_adapter.rb +54 -12
- data/lib/split/persistence/redis_adapter.rb +5 -0
- data/lib/split/persistence/session_adapter.rb +1 -0
- data/lib/split/persistence.rb +4 -2
- data/lib/split/redis_interface.rb +9 -28
- data/lib/split/trial.rb +21 -11
- data/lib/split/user.rb +20 -4
- data/lib/split/version.rb +2 -4
- data/lib/split/zscore.rb +1 -0
- data/lib/split.rb +9 -3
- data/spec/alternative_spec.rb +1 -1
- data/spec/cache_spec.rb +88 -0
- data/spec/configuration_spec.rb +17 -15
- data/spec/dashboard/pagination_helpers_spec.rb +3 -1
- data/spec/dashboard_helpers_spec.rb +2 -2
- data/spec/dashboard_spec.rb +78 -17
- data/spec/encapsulated_helper_spec.rb +2 -2
- data/spec/experiment_spec.rb +116 -12
- data/spec/goals_collection_spec.rb +1 -1
- data/spec/helper_spec.rb +186 -112
- data/spec/persistence/cookie_adapter_spec.rb +1 -1
- data/spec/persistence/dual_adapter_spec.rb +160 -68
- data/spec/persistence/redis_adapter_spec.rb +9 -0
- data/spec/redis_interface_spec.rb +0 -69
- data/spec/spec_helper.rb +5 -6
- data/spec/trial_spec.rb +45 -19
- data/spec/user_spec.rb +45 -3
- data/split.gemspec +8 -9
- metadata +28 -36
- data/.travis.yml +0 -66
- data/gemfiles/4.2.gemfile +0 -9
data/spec/helper_spec.rb
CHANGED
@@ -183,8 +183,7 @@ describe Split::Helper do
|
|
183
183
|
ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2)
|
184
184
|
experiment = Split::ExperimentCatalog.find('link_color')
|
185
185
|
expect(experiment.alternatives.map(&:name)).to eq(['blue', 'red'])
|
186
|
-
|
187
|
-
# expect(experiment.alternatives.collect{|a| a.weight}).to eq([0.01, 0.2])
|
186
|
+
expect(experiment.alternatives.collect{|a| a.weight}).to match_array([0.01, 0.2])
|
188
187
|
end
|
189
188
|
|
190
189
|
it "should only let a user participate in one experiment at a time" do
|
@@ -230,13 +229,15 @@ describe Split::Helper do
|
|
230
229
|
|
231
230
|
context "when user already has experiment" do
|
232
231
|
let(:mock_user){ Split::User.new(self, {'test_0' => 'test-alt'}) }
|
233
|
-
|
232
|
+
|
233
|
+
before do
|
234
234
|
Split.configure do |config|
|
235
235
|
config.allow_multiple_experiments = 'control'
|
236
236
|
end
|
237
|
+
|
237
238
|
Split::ExperimentCatalog.find_or_initialize('test_0', 'control', 'test-alt').save
|
238
239
|
Split::ExperimentCatalog.find_or_initialize('test_1', 'control', 'test-alt').save
|
239
|
-
|
240
|
+
end
|
240
241
|
|
241
242
|
it "should restore previously selected alternative" do
|
242
243
|
expect(ab_user.active_experiments.size).to eq 1
|
@@ -244,6 +245,16 @@ describe Split::Helper do
|
|
244
245
|
expect(ab_test(:test_0, {'control' => 1}, {"test-alt" => 100})).to eq 'test-alt'
|
245
246
|
end
|
246
247
|
|
248
|
+
it "should select the correct alternatives after experiment resets" do
|
249
|
+
experiment = Split::ExperimentCatalog.find(:test_0)
|
250
|
+
experiment.reset
|
251
|
+
mock_user[experiment.key] = 'test-alt'
|
252
|
+
|
253
|
+
expect(ab_user.active_experiments.size).to eq 1
|
254
|
+
expect(ab_test(:test_0, {'control' => 100}, {"test-alt" => 1})).to eq 'test-alt'
|
255
|
+
expect(ab_test(:test_0, {'control' => 0}, {"test-alt" => 100})).to eq 'test-alt'
|
256
|
+
end
|
257
|
+
|
247
258
|
it "lets override existing choice" do
|
248
259
|
pending "this requires user store reset on first call not depending on whelther it is current trial"
|
249
260
|
@params = { 'ab_test' => { 'test_1' => 'test-alt' } }
|
@@ -266,129 +277,187 @@ describe Split::Helper do
|
|
266
277
|
end
|
267
278
|
|
268
279
|
describe 'metadata' do
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
:
|
273
|
-
|
274
|
-
|
280
|
+
context 'is defined' do
|
281
|
+
before do
|
282
|
+
Split.configuration.experiments = {
|
283
|
+
:my_experiment => {
|
284
|
+
:alternatives => ["one", "two"],
|
285
|
+
:resettable => false,
|
286
|
+
:metadata => { 'one' => 'Meta1', 'two' => 'Meta2' }
|
287
|
+
}
|
275
288
|
}
|
276
|
-
|
277
|
-
end
|
289
|
+
end
|
278
290
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
291
|
+
it 'should be passed to helper block' do
|
292
|
+
@params = { 'ab_test' => { 'my_experiment' => 'two' } }
|
293
|
+
expect(ab_test('my_experiment')).to eq 'two'
|
294
|
+
expect(ab_test('my_experiment') do |alternative, meta|
|
295
|
+
meta
|
296
|
+
end).to eq('Meta2')
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'should pass control metadata helper block if library disabled' do
|
300
|
+
Split.configure do |config|
|
301
|
+
config.enabled = false
|
302
|
+
end
|
303
|
+
|
304
|
+
expect(ab_test('my_experiment')).to eq 'one'
|
305
|
+
expect(ab_test('my_experiment') do |_, meta|
|
306
|
+
meta
|
307
|
+
end).to eq('Meta1')
|
308
|
+
end
|
285
309
|
end
|
286
310
|
|
287
|
-
|
288
|
-
|
289
|
-
|
311
|
+
context 'is not defined' do
|
312
|
+
before do
|
313
|
+
Split.configuration.experiments = {
|
314
|
+
:my_experiment => {
|
315
|
+
:alternatives => ["one", "two"],
|
316
|
+
:resettable => false,
|
317
|
+
:metadata => nil
|
318
|
+
}
|
319
|
+
}
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'should be passed to helper block' do
|
323
|
+
expect(ab_test('my_experiment') do |alternative, meta|
|
324
|
+
meta
|
325
|
+
end).to eq({})
|
290
326
|
end
|
291
327
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
328
|
+
it 'should pass control metadata helper block if library disabled' do
|
329
|
+
Split.configure do |config|
|
330
|
+
config.enabled = false
|
331
|
+
end
|
332
|
+
|
333
|
+
expect(ab_test('my_experiment') do |_, meta|
|
334
|
+
meta
|
335
|
+
end).to eq({})
|
336
|
+
end
|
296
337
|
end
|
297
338
|
end
|
298
339
|
|
299
340
|
describe 'ab_finished' do
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
341
|
+
context 'for an experiment that the user participates in' do
|
342
|
+
before(:each) do
|
343
|
+
@experiment_name = 'link_color'
|
344
|
+
@alternatives = ['blue', 'red']
|
345
|
+
@experiment = Split::ExperimentCatalog.find_or_create(@experiment_name, *@alternatives)
|
346
|
+
@alternative_name = ab_test(@experiment_name, *@alternatives)
|
347
|
+
@previous_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
|
348
|
+
end
|
307
349
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
350
|
+
it 'should increment the counter for the completed alternative' do
|
351
|
+
ab_finished(@experiment_name)
|
352
|
+
new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
|
353
|
+
expect(new_completion_count).to eq(@previous_completion_count + 1)
|
354
|
+
end
|
313
355
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
356
|
+
it "should set experiment's finished key if reset is false" do
|
357
|
+
ab_finished(@experiment_name, {:reset => false})
|
358
|
+
expect(ab_user[@experiment.key]).to eq(@alternative_name)
|
359
|
+
expect(ab_user[@experiment.finished_key]).to eq(true)
|
360
|
+
end
|
319
361
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
362
|
+
it 'should not increment the counter if reset is false and the experiment has been already finished' do
|
363
|
+
2.times { ab_finished(@experiment_name, {:reset => false}) }
|
364
|
+
new_completion_count = Split::Alternative.new(@alternative_name, @experiment_name).completed_count
|
365
|
+
expect(new_completion_count).to eq(@previous_completion_count + 1)
|
366
|
+
end
|
325
367
|
|
326
|
-
|
327
|
-
|
368
|
+
it 'should not increment the counter for an ended experiment' do
|
369
|
+
e = Split::ExperimentCatalog.find_or_create('button_size', 'small', 'big')
|
370
|
+
e.winner = 'small'
|
371
|
+
a = ab_test('button_size', 'small', 'big')
|
372
|
+
expect(a).to eq('small')
|
373
|
+
expect(lambda {
|
374
|
+
ab_finished('button_size')
|
375
|
+
}).not_to change { Split::Alternative.new(a, 'button_size').completed_count }
|
376
|
+
end
|
328
377
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
ab_finished('button_size')
|
335
|
-
}).not_to change { Split::Alternative.new('small', 'button_size').completed_count }
|
336
|
-
end
|
378
|
+
it "should clear out the user's participation from their session" do
|
379
|
+
expect(ab_user[@experiment.key]).to eq(@alternative_name)
|
380
|
+
ab_finished(@experiment_name)
|
381
|
+
expect(ab_user.keys).to be_empty
|
382
|
+
end
|
337
383
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
ab_finished('button_size')
|
345
|
-
}).not_to change { Split::Alternative.new(a, 'button_size').completed_count }
|
346
|
-
end
|
384
|
+
it "should not clear out the users session if reset is false" do
|
385
|
+
expect(ab_user[@experiment.key]).to eq(@alternative_name)
|
386
|
+
ab_finished(@experiment_name, {:reset => false})
|
387
|
+
expect(ab_user[@experiment.key]).to eq(@alternative_name)
|
388
|
+
expect(ab_user[@experiment.finished_key]).to eq(true)
|
389
|
+
end
|
347
390
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
391
|
+
it "should reset the users session when experiment is not versioned" do
|
392
|
+
expect(ab_user[@experiment.key]).to eq(@alternative_name)
|
393
|
+
ab_finished(@experiment_name)
|
394
|
+
expect(ab_user.keys).to be_empty
|
395
|
+
end
|
353
396
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
expect(ab_user[@experiment.key]).to eq(@alternative_name)
|
358
|
-
expect(ab_user[@experiment.finished_key]).to eq(true)
|
359
|
-
end
|
397
|
+
it "should reset the users session when experiment is versioned" do
|
398
|
+
@experiment.increment_version
|
399
|
+
@alternative_name = ab_test(@experiment_name, *@alternatives)
|
360
400
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
end
|
401
|
+
expect(ab_user[@experiment.key]).to eq(@alternative_name)
|
402
|
+
ab_finished(@experiment_name)
|
403
|
+
expect(ab_user.keys).to be_empty
|
404
|
+
end
|
366
405
|
|
367
|
-
|
368
|
-
|
369
|
-
|
406
|
+
context "when on_trial_complete is set" do
|
407
|
+
before { Split.configuration.on_trial_complete = :some_method }
|
408
|
+
it "should call the method" do
|
409
|
+
expect(self).to receive(:some_method)
|
410
|
+
ab_finished(@experiment_name)
|
411
|
+
end
|
370
412
|
|
371
|
-
|
372
|
-
|
373
|
-
|
413
|
+
it "should not call the method without alternative" do
|
414
|
+
ab_user[@experiment.key] = nil
|
415
|
+
expect(self).not_to receive(:some_method)
|
416
|
+
ab_finished(@experiment_name)
|
417
|
+
end
|
418
|
+
end
|
374
419
|
end
|
375
420
|
|
376
|
-
|
377
|
-
|
378
|
-
|
421
|
+
context 'for an experiment that the user is excluded from' do
|
422
|
+
before do
|
423
|
+
alternative = ab_test('link_color', 'blue', 'red')
|
424
|
+
expect(Split::Alternative.new(alternative, 'link_color').participant_count).to eq(1)
|
425
|
+
alternative = ab_test('button_size', 'small', 'big')
|
426
|
+
expect(Split::Alternative.new(alternative, 'button_size').participant_count).to eq(0)
|
427
|
+
end
|
428
|
+
|
429
|
+
it 'should not increment the completed counter' do
|
430
|
+
# So, user should be participating in the link_color experiment and
|
431
|
+
# receive the control for button_size. As the user is not participating in
|
432
|
+
# the button size experiment, finishing it should not increase the
|
433
|
+
# completion count for that alternative.
|
434
|
+
expect(lambda {
|
435
|
+
ab_finished('button_size')
|
436
|
+
}).not_to change { Split::Alternative.new('small', 'button_size').completed_count }
|
437
|
+
end
|
379
438
|
end
|
380
439
|
|
381
|
-
context
|
382
|
-
before
|
383
|
-
|
384
|
-
|
385
|
-
|
440
|
+
context 'for an experiment that the user does not participate in' do
|
441
|
+
before do
|
442
|
+
Split::ExperimentCatalog.find_or_create(:not_started_experiment, 'control', 'alt')
|
443
|
+
end
|
444
|
+
it 'should not raise an exception' do
|
445
|
+
expect { ab_finished(:not_started_experiment) }.not_to raise_exception
|
386
446
|
end
|
387
447
|
|
388
|
-
it
|
389
|
-
ab_user[
|
390
|
-
|
391
|
-
|
448
|
+
it 'should not change the user state when reset is false' do
|
449
|
+
expect { ab_finished(:not_started_experiment, reset: false) }.not_to change { ab_user.keys}.from([])
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'should not change the user state when reset is true' do
|
453
|
+
expect(self).not_to receive(:reset!)
|
454
|
+
ab_finished(:not_started_experiment)
|
455
|
+
end
|
456
|
+
|
457
|
+
it 'should not increment the completed counter' do
|
458
|
+
ab_finished(:not_started_experiment)
|
459
|
+
expect(Split::Alternative.new('control', :not_started_experiment).completed_count).to eq(0)
|
460
|
+
expect(Split::Alternative.new('alt', :not_started_experiment).completed_count).to eq(0)
|
392
461
|
end
|
393
462
|
end
|
394
463
|
end
|
@@ -528,6 +597,17 @@ describe Split::Helper do
|
|
528
597
|
expect(active_experiments.first[0]).to eq "link_color"
|
529
598
|
end
|
530
599
|
|
600
|
+
it 'should show versioned tests properly' do
|
601
|
+
10.times { experiment.reset }
|
602
|
+
|
603
|
+
alternative = ab_test(experiment.name, 'blue', 'red')
|
604
|
+
ab_finished(experiment.name, reset: false)
|
605
|
+
|
606
|
+
expect(experiment.version).to eq(10)
|
607
|
+
expect(active_experiments.count).to eq 1
|
608
|
+
expect(active_experiments).to eq({'link_color' => alternative })
|
609
|
+
end
|
610
|
+
|
531
611
|
it 'should show multiple tests' do
|
532
612
|
Split.configure do |config|
|
533
613
|
config.allow_multiple_experiments = true
|
@@ -545,7 +625,7 @@ describe Split::Helper do
|
|
545
625
|
end
|
546
626
|
e = Split::ExperimentCatalog.find_or_create('def', '4', '5', '6')
|
547
627
|
e.winner = '4'
|
548
|
-
|
628
|
+
ab_test('def', '4', '5', '6')
|
549
629
|
another_alternative = ab_test('ghi', '7', '8', '9')
|
550
630
|
expect(active_experiments.count).to eq 1
|
551
631
|
expect(active_experiments.first[0]).to eq "ghi"
|
@@ -1021,8 +1101,8 @@ describe Split::Helper do
|
|
1021
1101
|
|
1022
1102
|
it 'should handle multiple experiments correctly' do
|
1023
1103
|
experiment2 = Split::ExperimentCatalog.find_or_create('link_color2', 'blue', 'red')
|
1024
|
-
|
1025
|
-
|
1104
|
+
ab_test('link_color', 'blue', 'red')
|
1105
|
+
ab_test('link_color2', 'blue', 'red')
|
1026
1106
|
ab_finished('link_color2')
|
1027
1107
|
|
1028
1108
|
experiment2.alternatives.each do |alt|
|
@@ -1058,15 +1138,9 @@ describe Split::Helper do
|
|
1058
1138
|
end
|
1059
1139
|
|
1060
1140
|
it "should increment the counter for the specified-goal completed alternative" do
|
1061
|
-
expect(
|
1062
|
-
|
1063
|
-
|
1064
|
-
}).not_to change {
|
1065
|
-
Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal2)
|
1066
|
-
}
|
1067
|
-
}).to change {
|
1068
|
-
Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal1)
|
1069
|
-
}.by(1)
|
1141
|
+
expect{ ab_finished({"link_color" => ["purchase"]}) }
|
1142
|
+
.to change{ Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal2) }.by(0)
|
1143
|
+
.and change{ Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal1) }.by(1)
|
1070
1144
|
end
|
1071
1145
|
end
|
1072
1146
|
end
|
@@ -52,7 +52,7 @@ describe Split::Persistence::CookieAdapter do
|
|
52
52
|
it "puts multiple experiments in a single cookie" do
|
53
53
|
subject["foo"] = "FOO"
|
54
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} -
|
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} [A-Z]{3}\Z/)
|
56
56
|
end
|
57
57
|
|
58
58
|
it "ensure other added cookies are not overriden" do
|
@@ -1,102 +1,194 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
3
4
|
|
4
5
|
describe Split::Persistence::DualAdapter do
|
6
|
+
let(:context) { 'some context' }
|
5
7
|
|
6
|
-
let(:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
let(:
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
8
|
+
let(:logged_in_adapter_instance) { double }
|
9
|
+
let(:logged_in_adapter) do
|
10
|
+
Class.new.tap { |c| allow(c).to receive(:new) { logged_in_adapter_instance } }
|
11
|
+
end
|
12
|
+
let(:logged_out_adapter_instance) { double }
|
13
|
+
let(:logged_out_adapter) do
|
14
|
+
Class.new.tap { |c| allow(c).to receive(:new) { logged_out_adapter_instance } }
|
15
|
+
end
|
27
16
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
17
|
+
context 'when fallback_to_logged_out_adapter is false' do
|
18
|
+
context 'when logged in' do
|
19
|
+
subject do
|
20
|
+
described_class.with_config(
|
21
|
+
logged_in: lambda { |context| true },
|
22
|
+
logged_in_adapter: logged_in_adapter,
|
23
|
+
logged_out_adapter: logged_out_adapter,
|
24
|
+
fallback_to_logged_out_adapter: false
|
25
|
+
).new(context)
|
26
|
+
end
|
27
|
+
|
28
|
+
it '#[]=' do
|
29
|
+
expect(logged_in_adapter_instance).to receive(:[]=).with('my_key', 'my_value')
|
30
|
+
expect_any_instance_of(logged_out_adapter).not_to receive(:[]=)
|
31
|
+
subject['my_key'] = 'my_value'
|
32
|
+
end
|
33
|
+
|
34
|
+
it '#[]' do
|
35
|
+
expect(logged_in_adapter_instance).to receive(:[]).with('my_key') { 'my_value' }
|
36
|
+
expect_any_instance_of(logged_out_adapter).not_to receive(:[])
|
37
|
+
expect(subject['my_key']).to eq('my_value')
|
38
|
+
end
|
33
39
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
it '#delete' do
|
41
|
+
expect(logged_in_adapter_instance).to receive(:delete).with('my_key') { 'my_value' }
|
42
|
+
expect_any_instance_of(logged_out_adapter).not_to receive(:delete)
|
43
|
+
expect(subject.delete('my_key')).to eq('my_value')
|
44
|
+
end
|
45
|
+
|
46
|
+
it '#keys' do
|
47
|
+
expect(logged_in_adapter_instance).to receive(:keys) { ['my_value'] }
|
48
|
+
expect_any_instance_of(logged_out_adapter).not_to receive(:keys)
|
49
|
+
expect(subject.keys).to eq(['my_value'])
|
50
|
+
end
|
38
51
|
end
|
39
52
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
53
|
+
context 'when logged out' do
|
54
|
+
subject do
|
55
|
+
described_class.with_config(
|
56
|
+
logged_in: lambda { |context| false },
|
57
|
+
logged_in_adapter: logged_in_adapter,
|
58
|
+
logged_out_adapter: logged_out_adapter,
|
59
|
+
fallback_to_logged_out_adapter: false
|
60
|
+
).new(context)
|
61
|
+
end
|
62
|
+
|
63
|
+
it '#[]=' do
|
64
|
+
expect_any_instance_of(logged_in_adapter).not_to receive(:[]=)
|
65
|
+
expect(logged_out_adapter_instance).to receive(:[]=).with('my_key', 'my_value')
|
66
|
+
subject['my_key'] = 'my_value'
|
67
|
+
end
|
68
|
+
|
69
|
+
it '#[]' do
|
70
|
+
expect_any_instance_of(logged_in_adapter).not_to receive(:[])
|
71
|
+
expect(logged_out_adapter_instance).to receive(:[]).with('my_key') { 'my_value' }
|
72
|
+
expect(subject['my_key']).to eq('my_value')
|
73
|
+
end
|
74
|
+
|
75
|
+
it '#delete' do
|
76
|
+
expect_any_instance_of(logged_in_adapter).not_to receive(:delete)
|
77
|
+
expect(logged_out_adapter_instance).to receive(:delete).with('my_key') { 'my_value' }
|
78
|
+
expect(subject.delete('my_key')).to eq('my_value')
|
79
|
+
end
|
80
|
+
|
81
|
+
it '#keys' do
|
82
|
+
expect_any_instance_of(logged_in_adapter).not_to receive(:keys)
|
83
|
+
expect(logged_out_adapter_instance).to receive(:keys) { ['my_value', 'my_value2'] }
|
84
|
+
expect(subject.keys).to eq(['my_value', 'my_value2'])
|
85
|
+
end
|
44
86
|
end
|
45
87
|
end
|
46
88
|
|
47
|
-
context
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
89
|
+
context 'when fallback_to_logged_out_adapter is true' do
|
90
|
+
context 'when logged in' do
|
91
|
+
subject do
|
92
|
+
described_class.with_config(
|
93
|
+
logged_in: lambda { |context| true },
|
94
|
+
logged_in_adapter: logged_in_adapter,
|
95
|
+
logged_out_adapter: logged_out_adapter,
|
96
|
+
fallback_to_logged_out_adapter: true
|
53
97
|
).new(context)
|
54
|
-
|
98
|
+
end
|
55
99
|
|
56
|
-
|
57
|
-
|
100
|
+
it '#[]=' do
|
101
|
+
expect(logged_in_adapter_instance).to receive(:[]=).with('my_key', 'my_value')
|
102
|
+
expect(logged_out_adapter_instance).to receive(:[]=).with('my_key', 'my_value')
|
103
|
+
expect(logged_out_adapter_instance).to receive(:[]).with('my_key') { nil }
|
104
|
+
subject['my_key'] = 'my_value'
|
105
|
+
end
|
106
|
+
|
107
|
+
it '#[]' do
|
108
|
+
expect(logged_in_adapter_instance).to receive(:[]).with('my_key') { 'my_value' }
|
109
|
+
expect_any_instance_of(logged_out_adapter).not_to receive(:[])
|
110
|
+
expect(subject['my_key']).to eq('my_value')
|
111
|
+
end
|
112
|
+
|
113
|
+
it '#delete' do
|
114
|
+
expect(logged_in_adapter_instance).to receive(:delete).with('my_key') { 'my_value' }
|
115
|
+
expect(logged_out_adapter_instance).to receive(:delete).with('my_key') { 'my_value' }
|
116
|
+
expect(subject.delete('my_key')).to eq('my_value')
|
117
|
+
end
|
118
|
+
|
119
|
+
it '#keys' do
|
120
|
+
expect(logged_in_adapter_instance).to receive(:keys) { ['my_value'] }
|
121
|
+
expect(logged_out_adapter_instance).to receive(:keys) { ['my_value', 'my_value2'] }
|
122
|
+
expect(subject.keys).to eq(['my_value', 'my_value2'])
|
123
|
+
end
|
124
|
+
end
|
58
125
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
126
|
+
context 'when logged out' do
|
127
|
+
subject do
|
128
|
+
described_class.with_config(
|
129
|
+
logged_in: lambda { |context| false },
|
130
|
+
logged_in_adapter: logged_in_adapter,
|
131
|
+
logged_out_adapter: logged_out_adapter,
|
132
|
+
fallback_to_logged_out_adapter: true
|
65
133
|
).new(context)
|
66
|
-
|
134
|
+
end
|
135
|
+
|
136
|
+
it '#[]=' do
|
137
|
+
expect_any_instance_of(logged_in_adapter).not_to receive(:[]=)
|
138
|
+
expect(logged_out_adapter_instance).to receive(:[]=).with('my_key', 'my_value')
|
139
|
+
expect(logged_out_adapter_instance).to receive(:[]).with('my_key') { nil }
|
140
|
+
subject['my_key'] = 'my_value'
|
141
|
+
end
|
142
|
+
|
143
|
+
it '#[]' do
|
144
|
+
expect_any_instance_of(logged_in_adapter).not_to receive(:[])
|
145
|
+
expect(logged_out_adapter_instance).to receive(:[]).with('my_key') { 'my_value' }
|
146
|
+
expect(subject['my_key']).to eq('my_value')
|
147
|
+
end
|
148
|
+
|
149
|
+
it '#delete' do
|
150
|
+
expect(logged_in_adapter_instance).to receive(:delete).with('my_key') { 'my_value' }
|
151
|
+
expect(logged_out_adapter_instance).to receive(:delete).with('my_key') { 'my_value' }
|
152
|
+
expect(subject.delete('my_key')).to eq('my_value')
|
153
|
+
end
|
67
154
|
|
68
|
-
|
155
|
+
it '#keys' do
|
156
|
+
expect(logged_in_adapter_instance).to receive(:keys) { ['my_value'] }
|
157
|
+
expect(logged_out_adapter_instance).to receive(:keys) { ['my_value', 'my_value2'] }
|
158
|
+
expect(subject.keys).to eq(['my_value', 'my_value2'])
|
159
|
+
end
|
160
|
+
end
|
69
161
|
end
|
70
162
|
|
71
|
-
describe
|
72
|
-
before{
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
it "when no logged in adapter" do
|
163
|
+
describe 'when errors in config' do
|
164
|
+
before { described_class.config.clear }
|
165
|
+
let(:some_proc) { ->{} }
|
166
|
+
|
167
|
+
it 'when no logged in adapter' do
|
77
168
|
expect{
|
78
169
|
described_class.with_config(
|
79
170
|
logged_in: some_proc,
|
80
|
-
logged_out_adapter:
|
81
|
-
|
171
|
+
logged_out_adapter: logged_out_adapter
|
172
|
+
).new(context)
|
82
173
|
}.to raise_error(StandardError, /:logged_in_adapter/)
|
83
174
|
end
|
84
|
-
|
175
|
+
|
176
|
+
it 'when no logged out adapter' do
|
85
177
|
expect{
|
86
178
|
described_class.with_config(
|
87
179
|
logged_in: some_proc,
|
88
|
-
logged_in_adapter:
|
89
|
-
|
180
|
+
logged_in_adapter: logged_in_adapter
|
181
|
+
).new(context)
|
90
182
|
}.to raise_error(StandardError, /:logged_out_adapter/)
|
91
183
|
end
|
92
|
-
|
184
|
+
|
185
|
+
it 'when no logged in detector' do
|
93
186
|
expect{
|
94
187
|
described_class.with_config(
|
95
|
-
logged_in_adapter:
|
96
|
-
logged_out_adapter:
|
97
|
-
|
188
|
+
logged_in_adapter: logged_in_adapter,
|
189
|
+
logged_out_adapter: logged_out_adapter
|
190
|
+
).new(context)
|
98
191
|
}.to raise_error(StandardError, /:logged_in$/)
|
99
192
|
end
|
100
193
|
end
|
101
|
-
|
102
194
|
end
|