split 3.4.0 → 4.0.1

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/dependabot.yml +7 -0
  4. data/.github/workflows/ci.yml +71 -0
  5. data/.rubocop.yml +177 -1
  6. data/.rubocop_todo.yml +40 -493
  7. data/CHANGELOG.md +41 -0
  8. data/Gemfile +1 -0
  9. data/README.md +26 -6
  10. data/Rakefile +1 -0
  11. data/gemfiles/{4.2.gemfile → 6.1.gemfile} +1 -1
  12. data/gemfiles/7.0.gemfile +9 -0
  13. data/lib/split/algorithms/block_randomization.rb +1 -0
  14. data/lib/split/algorithms/weighted_sample.rb +2 -1
  15. data/lib/split/algorithms/whiplash.rb +3 -2
  16. data/lib/split/alternative.rb +1 -0
  17. data/lib/split/cache.rb +28 -0
  18. data/lib/split/combined_experiments_helper.rb +1 -0
  19. data/lib/split/configuration.rb +8 -12
  20. data/lib/split/dashboard/helpers.rb +1 -0
  21. data/lib/split/dashboard/pagination_helpers.rb +1 -0
  22. data/lib/split/dashboard/paginator.rb +1 -0
  23. data/lib/split/dashboard/public/dashboard.js +10 -0
  24. data/lib/split/dashboard/public/style.css +5 -0
  25. data/lib/split/dashboard/views/_controls.erb +13 -0
  26. data/lib/split/dashboard.rb +17 -2
  27. data/lib/split/encapsulated_helper.rb +3 -2
  28. data/lib/split/engine.rb +5 -4
  29. data/lib/split/exceptions.rb +1 -0
  30. data/lib/split/experiment.rb +82 -60
  31. data/lib/split/experiment_catalog.rb +1 -3
  32. data/lib/split/extensions/string.rb +1 -0
  33. data/lib/split/goals_collection.rb +1 -0
  34. data/lib/split/helper.rb +26 -7
  35. data/lib/split/metric.rb +2 -1
  36. data/lib/split/persistence/cookie_adapter.rb +6 -1
  37. data/lib/split/persistence/redis_adapter.rb +5 -0
  38. data/lib/split/persistence/session_adapter.rb +1 -0
  39. data/lib/split/persistence.rb +4 -2
  40. data/lib/split/redis_interface.rb +8 -28
  41. data/lib/split/trial.rb +20 -10
  42. data/lib/split/user.rb +15 -3
  43. data/lib/split/version.rb +2 -4
  44. data/lib/split/zscore.rb +1 -0
  45. data/lib/split.rb +9 -3
  46. data/spec/alternative_spec.rb +1 -1
  47. data/spec/cache_spec.rb +88 -0
  48. data/spec/configuration_spec.rb +17 -15
  49. data/spec/dashboard_spec.rb +45 -5
  50. data/spec/encapsulated_helper_spec.rb +1 -1
  51. data/spec/experiment_spec.rb +78 -13
  52. data/spec/goals_collection_spec.rb +1 -1
  53. data/spec/helper_spec.rb +68 -32
  54. data/spec/persistence/cookie_adapter_spec.rb +1 -1
  55. data/spec/persistence/redis_adapter_spec.rb +9 -0
  56. data/spec/redis_interface_spec.rb +0 -69
  57. data/spec/spec_helper.rb +5 -6
  58. data/spec/trial_spec.rb +45 -19
  59. data/spec/user_spec.rb +34 -3
  60. data/split.gemspec +7 -7
  61. metadata +27 -35
  62. data/.travis.yml +0 -60
data/lib/split.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'redis'
3
4
 
4
5
  require 'split/algorithms/block_randomization'
5
6
  require 'split/algorithms/weighted_sample'
6
7
  require 'split/algorithms/whiplash'
7
8
  require 'split/alternative'
9
+ require 'split/cache'
8
10
  require 'split/configuration'
9
11
  require 'split/encapsulated_helper'
10
12
  require 'split/exceptions'
@@ -35,9 +37,9 @@ module Split
35
37
  # `Redis::DistRedis`, or `Redis::Namespace`.
36
38
  def redis=(server)
37
39
  @redis = if server.is_a?(String)
38
- Redis.new(:url => server, :thread_safe => true)
40
+ Redis.new(url: server)
39
41
  elsif server.is_a?(Hash)
40
- Redis.new(server.merge(:thread_safe => true))
42
+ Redis.new(server)
41
43
  elsif server.respond_to?(:smembers)
42
44
  server
43
45
  else
@@ -64,11 +66,15 @@ module Split
64
66
  self.configuration ||= Configuration.new
65
67
  yield(configuration)
66
68
  end
69
+
70
+ def cache(namespace, key, &block)
71
+ Split::Cache.fetch(namespace, key, &block)
72
+ end
67
73
  end
68
74
 
69
75
  # Check to see if being run in a Rails application. If so, wait until before_initialize to run configuration so Gems that create ENV variables have the chance to initialize first.
70
76
  if defined?(::Rails)
71
- class Railtie < Rails::Railtie
77
+ class Split::Railtie < Rails::Railtie
72
78
  config.before_initialize { Split.configure {} }
73
79
  end
74
80
  else
@@ -126,7 +126,7 @@ describe Split::Alternative do
126
126
 
127
127
  it "should save to redis" do
128
128
  alternative.save
129
- expect(Split.redis.exists('basket_text:Basket')).to be true
129
+ expect(Split.redis.exists?('basket_text:Basket')).to be true
130
130
  end
131
131
 
132
132
  it "should increment participation count" do
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ describe Split::Cache do
5
+
6
+ let(:namespace) { :test_namespace }
7
+ let(:key) { :test_key }
8
+ let(:now) { 1606189017 }
9
+
10
+ before { allow(Time).to receive(:now).and_return(now) }
11
+
12
+ describe 'clear' do
13
+
14
+ before { Split.configuration.cache = true }
15
+
16
+ it 'clears the cache' do
17
+ expect(Time).to receive(:now).and_return(now).exactly(2).times
18
+ Split::Cache.fetch(namespace, key) { Time.now }
19
+ Split::Cache.clear
20
+ Split::Cache.fetch(namespace, key) { Time.now }
21
+ end
22
+ end
23
+
24
+ describe 'clear_key' do
25
+ before { Split.configuration.cache = true }
26
+
27
+ it 'clears the cache' do
28
+ expect(Time).to receive(:now).and_return(now).exactly(3).times
29
+ Split::Cache.fetch(namespace, :key1) { Time.now }
30
+ Split::Cache.fetch(namespace, :key2) { Time.now }
31
+ Split::Cache.clear_key(:key1)
32
+
33
+ Split::Cache.fetch(namespace, :key1) { Time.now }
34
+ Split::Cache.fetch(namespace, :key2) { Time.now }
35
+ end
36
+ end
37
+
38
+ describe 'fetch' do
39
+
40
+ subject { Split::Cache.fetch(namespace, key) { Time.now } }
41
+
42
+ context 'when cache disabled' do
43
+
44
+ before { Split.configuration.cache = false }
45
+
46
+ it 'returns the yield' do
47
+ expect(subject).to eql(now)
48
+ end
49
+
50
+ it 'yields every time' do
51
+ expect(Time).to receive(:now).and_return(now).exactly(2).times
52
+ Split::Cache.fetch(namespace, key) { Time.now }
53
+ Split::Cache.fetch(namespace, key) { Time.now }
54
+ end
55
+ end
56
+
57
+ context 'when cache enabled' do
58
+
59
+ before { Split.configuration.cache = true }
60
+
61
+ it 'returns the yield' do
62
+ expect(subject).to eql(now)
63
+ end
64
+
65
+ it 'yields once' do
66
+ expect(Time).to receive(:now).and_return(now).once
67
+ Split::Cache.fetch(namespace, key) { Time.now }
68
+ Split::Cache.fetch(namespace, key) { Time.now }
69
+ end
70
+
71
+ it 'honors namespace' do
72
+ expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
73
+ expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
74
+
75
+ expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
76
+ expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
77
+ end
78
+
79
+ it 'honors key' do
80
+ expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
81
+ expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
82
+
83
+ expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
84
+ expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -212,23 +212,12 @@ describe Split::Configuration do
212
212
  expect(@config.normalized_experiments).to eq({:my_experiment=>{:alternatives=>[{"control_opt"=>0.67}, [{"second_opt"=>0.1}, {"third_opt"=>0.23}]]}})
213
213
  end
214
214
 
215
- context 'redis_url configuration [DEPRECATED]' do
216
- it 'should warn on set and assign to #redis' do
217
- expect(@config).to receive(:warn).with(/\[DEPRECATED\]/) { nil }
218
- @config.redis_url = 'example_url'
219
- expect(@config.redis).to eq('example_url')
220
- end
221
-
222
- it 'should warn on get and return #redis' do
223
- expect(@config).to receive(:warn).with(/\[DEPRECATED\]/) { nil }
224
- @config.redis = 'example_url'
225
- expect(@config.redis_url).to eq('example_url')
226
- end
227
- end
228
-
229
215
  context "redis configuration" do
230
216
  it "should default to local redis server" do
231
- expect(@config.redis).to eq("redis://localhost:6379")
217
+ old_redis_url = ENV['REDIS_URL']
218
+ ENV.delete('REDIS_URL')
219
+ expect(Split::Configuration.new.redis).to eq("redis://localhost:6379")
220
+ ENV['REDIS_URL'] = old_redis_url
232
221
  end
233
222
 
234
223
  it "should allow for redis url to be configured" do
@@ -238,8 +227,10 @@ describe Split::Configuration do
238
227
 
239
228
  context "provided REDIS_URL environment variable" do
240
229
  it "should use the ENV variable" do
230
+ old_redis_url = ENV['REDIS_URL']
241
231
  ENV['REDIS_URL'] = "env_redis_url"
242
232
  expect(Split::Configuration.new.redis).to eq("env_redis_url")
233
+ ENV['REDIS_URL'] = old_redis_url
243
234
  end
244
235
  end
245
236
  end
@@ -255,4 +246,15 @@ describe Split::Configuration do
255
246
  end
256
247
  end
257
248
 
249
+ context "persistence cookie domain" do
250
+ it "should default to nil" do
251
+ expect(@config.persistence_cookie_domain).to eq(nil)
252
+ end
253
+
254
+ it "should allow the persistence cookie domain to be configured" do
255
+ @config.persistence_cookie_domain = '.acme.com'
256
+ expect(@config.persistence_cookie_domain).to eq('.acme.com')
257
+ end
258
+ end
259
+
258
260
  end
@@ -6,8 +6,16 @@ require 'split/dashboard'
6
6
  describe Split::Dashboard do
7
7
  include Rack::Test::Methods
8
8
 
9
+ class TestDashboard < Split::Dashboard
10
+ include Split::Helper
11
+
12
+ get '/my_experiment' do
13
+ ab_test(params[:experiment], 'blue', 'red')
14
+ end
15
+ end
16
+
9
17
  def app
10
- @app ||= Split::Dashboard
18
+ @app ||= TestDashboard
11
19
  end
12
20
 
13
21
  def link(color)
@@ -90,8 +98,17 @@ describe Split::Dashboard do
90
98
  it "should set current user's alternative" do
91
99
  blue_link.participant_count = 7
92
100
  post "/force_alternative?experiment=#{experiment.name}", alternative: "blue"
93
- expect(user[experiment.key]).to eq("blue")
94
- expect(blue_link.participant_count).to eq(8)
101
+
102
+ get "/my_experiment?experiment=#{experiment.name}"
103
+ expect(last_response.body).to include("blue")
104
+ end
105
+
106
+ it "should not modify an existing user" do
107
+ blue_link.participant_count = 7
108
+ post "/force_alternative?experiment=#{experiment.name}", alternative: "blue"
109
+
110
+ expect(user[experiment.key]).to eq("red")
111
+ expect(blue_link.participant_count).to eq(7)
95
112
  end
96
113
  end
97
114
 
@@ -108,8 +125,9 @@ describe Split::Dashboard do
108
125
  it "should set current user's alternative" do
109
126
  blue_link.participant_count = 7
110
127
  post "/force_alternative?experiment=#{experiment.name}", alternative: "blue"
111
- expect(user[experiment.key]).to eq("blue")
112
- expect(blue_link.participant_count).to eq(8)
128
+
129
+ get "/my_experiment?experiment=#{experiment.name}"
130
+ expect(last_response.body).to include("blue")
113
131
  end
114
132
  end
115
133
  end
@@ -161,6 +179,28 @@ describe Split::Dashboard do
161
179
  end
162
180
  end
163
181
 
182
+ describe "update cohorting" do
183
+ it "calls enable of cohorting when action is enable" do
184
+ post "/update_cohorting?experiment=#{experiment.name}", { "cohorting_action": "enable" }
185
+
186
+ expect(experiment.cohorting_disabled?).to eq false
187
+ end
188
+
189
+ it "calls disable of cohorting when action is disable" do
190
+ post "/update_cohorting?experiment=#{experiment.name}", { "cohorting_action": "disable" }
191
+
192
+ expect(experiment.cohorting_disabled?).to eq true
193
+ end
194
+
195
+ it "calls neither enable or disable cohorting when passed invalid action" do
196
+ previous_value = experiment.cohorting_disabled?
197
+
198
+ post "/update_cohorting?experiment=#{experiment.name}", { "cohorting_action": "other" }
199
+
200
+ expect(experiment.cohorting_disabled?).to eq previous_value
201
+ end
202
+ end
203
+
164
204
  it "should reset an experiment" do
165
205
  red_link.participant_count = 5
166
206
  blue_link.participant_count = 7
@@ -21,7 +21,7 @@ describe Split::EncapsulatedHelper do
21
21
  end
22
22
 
23
23
  it "calls the block with selected alternative" do
24
- expect{|block| ab_test('link_color', 'red', 'red', &block) }.to yield_with_args('red', nil)
24
+ expect{|block| ab_test('link_color', 'red', 'red', &block) }.to yield_with_args('red', {})
25
25
  end
26
26
 
27
27
  context "inside a view" do
@@ -35,15 +35,9 @@ describe Split::Experiment do
35
35
  expect(experiment.resettable).to be_truthy
36
36
  end
37
37
 
38
- it "should be resettable when loading from configuration" do
39
- allow(Split.configuration).to receive(:experiment_for).with('some_experiment') { { alternatives: %w(a b) } }
40
-
41
- expect(Split::Experiment.new('some_experiment')).to be_resettable
42
- end
43
-
44
38
  it "should save to redis" do
45
39
  experiment.save
46
- expect(Split.redis.exists('basket_text')).to be true
40
+ expect(Split.redis.exists?('basket_text')).to be true
47
41
  end
48
42
 
49
43
  it "should save the start time to redis" do
@@ -91,7 +85,7 @@ describe Split::Experiment do
91
85
  it "should not create duplicates when saving multiple times" do
92
86
  experiment.save
93
87
  experiment.save
94
- expect(Split.redis.exists('basket_text')).to be true
88
+ expect(Split.redis.exists?('basket_text')).to be true
95
89
  expect(Split.redis.lrange('basket_text', 0, -1)).to eq(['{"Basket":1}', '{"Cart":1}'])
96
90
  end
97
91
 
@@ -124,6 +118,23 @@ describe Split::Experiment do
124
118
  experiment = Split::Experiment.new('basket_text', :alternatives => ['Basket', "Cart"], :resettable => false)
125
119
  expect(experiment.resettable).to be_falsey
126
120
  end
121
+
122
+ context 'from configuration' do
123
+ let(:experiment_name) { :my_experiment }
124
+ let(:experiments) do
125
+ {
126
+ experiment_name => {
127
+ :alternatives => ['Control Opt', 'Alt one']
128
+ }
129
+ }
130
+ end
131
+
132
+ before { Split.configuration.experiments = experiments }
133
+
134
+ it 'assigns default values to the experiment' do
135
+ expect(Split::Experiment.new(experiment_name).resettable).to eq(true)
136
+ end
137
+ end
127
138
  end
128
139
 
129
140
  describe 'persistent configuration' do
@@ -140,10 +151,23 @@ describe Split::Experiment do
140
151
 
141
152
  describe '#metadata' do
142
153
  let(:experiment) { Split::Experiment.new('basket_text', :alternatives => ['Basket', "Cart"], :algorithm => Split::Algorithms::Whiplash, :metadata => meta) }
154
+ let(:meta) { { a: 'b' }}
155
+
156
+ before do
157
+ experiment.save
158
+ end
159
+
160
+ it "should delete the key when metadata is removed" do
161
+ experiment.metadata = nil
162
+ experiment.save
163
+
164
+ expect(Split.redis.exists?(experiment.metadata_key)).to be_falsey
165
+ end
166
+
143
167
  context 'simple hash' do
144
168
  let(:meta) { { 'basket' => 'a', 'cart' => 'b' } }
169
+
145
170
  it "should persist metadata in redis" do
146
- experiment.save
147
171
  e = Split::ExperimentCatalog.find('basket_text')
148
172
  expect(e).to eq(experiment)
149
173
  expect(e.metadata).to eq(meta)
@@ -153,7 +177,6 @@ describe Split::Experiment do
153
177
  context 'nested hash' do
154
178
  let(:meta) { { 'basket' => { 'one' => 'two' }, 'cart' => 'b' } }
155
179
  it "should persist metadata in redis" do
156
- experiment.save
157
180
  e = Split::ExperimentCatalog.find('basket_text')
158
181
  expect(e).to eq(experiment)
159
182
  expect(e.metadata).to eq(meta)
@@ -186,7 +209,7 @@ describe Split::Experiment do
186
209
  experiment.save
187
210
 
188
211
  experiment.delete
189
- expect(Split.redis.exists('link_color')).to be false
212
+ expect(Split.redis.exists?('link_color')).to be false
190
213
  expect(Split::ExperimentCatalog.find('link_color')).to be_nil
191
214
  end
192
215
 
@@ -212,8 +235,14 @@ describe Split::Experiment do
212
235
  experiment.delete
213
236
  expect(experiment.start_time).to be_nil
214
237
  end
215
- end
216
238
 
239
+ it "should default cohorting back to false" do
240
+ experiment.disable_cohorting
241
+ expect(experiment.cohorting_disabled?).to eq(true)
242
+ experiment.delete
243
+ expect(experiment.cohorting_disabled?).to eq(false)
244
+ end
245
+ end
217
246
 
218
247
  describe 'winner' do
219
248
  it "should have no winner initially" do
@@ -222,12 +251,17 @@ describe Split::Experiment do
222
251
  end
223
252
 
224
253
  describe 'winner=' do
225
- it "should allow you to specify a winner" do
254
+ it 'should allow you to specify a winner' do
226
255
  experiment.save
227
256
  experiment.winner = 'red'
228
257
  expect(experiment.winner.name).to eq('red')
229
258
  end
230
259
 
260
+ it 'should call the on_experiment_winner_choose hook' do
261
+ expect(Split.configuration.on_experiment_winner_choose).to receive(:call)
262
+ experiment.winner = 'green'
263
+ end
264
+
231
265
  context 'when has_winner state is memoized' do
232
266
  before { expect(experiment).to_not have_winner }
233
267
 
@@ -376,6 +410,22 @@ describe Split::Experiment do
376
410
  end
377
411
  end
378
412
 
413
+ describe "#cohorting_disabled?" do
414
+ it "returns false when nothing has been configured" do
415
+ expect(experiment.cohorting_disabled?).to eq false
416
+ end
417
+
418
+ it "returns true when enable_cohorting is performed" do
419
+ experiment.enable_cohorting
420
+ expect(experiment.cohorting_disabled?).to eq false
421
+ end
422
+
423
+ it "returns false when nothing has been configured" do
424
+ experiment.disable_cohorting
425
+ expect(experiment.cohorting_disabled?).to eq true
426
+ end
427
+ end
428
+
379
429
  describe 'changing an existing experiment' do
380
430
  def same_but_different_alternative
381
431
  Split::ExperimentCatalog.find_or_create('link_color', 'blue', 'yellow', 'orange')
@@ -398,6 +448,21 @@ describe Split::Experiment do
398
448
  expect(same_experiment_again.version).to eq(1)
399
449
  end
400
450
 
451
+ context "when metadata is changed" do
452
+ it "should increase version" do
453
+ experiment.save
454
+ experiment.metadata = { 'foo' => 'bar' }
455
+
456
+ expect { experiment.save }.to change { experiment.version }.by(1)
457
+ end
458
+
459
+ it "does not increase version" do
460
+ experiment.metadata = nil
461
+ experiment.save
462
+ expect { experiment.save }.to change { experiment.version }.by(0)
463
+ end
464
+ end
465
+
401
466
  context 'when experiment configuration is changed' do
402
467
  let(:reset_manually) { false }
403
468
 
@@ -48,7 +48,7 @@ describe Split::GoalsCollection do
48
48
  goals_collection.save
49
49
 
50
50
  goals_collection.delete
51
- expect(Split.redis.exists(goals_key)).to be false
51
+ expect(Split.redis.exists?(goals_key)).to be false
52
52
  end
53
53
  end
54
54
 
data/spec/helper_spec.rb CHANGED
@@ -229,13 +229,15 @@ describe Split::Helper do
229
229
 
230
230
  context "when user already has experiment" do
231
231
  let(:mock_user){ Split::User.new(self, {'test_0' => 'test-alt'}) }
232
- before{
232
+
233
+ before do
233
234
  Split.configure do |config|
234
235
  config.allow_multiple_experiments = 'control'
235
236
  end
237
+
236
238
  Split::ExperimentCatalog.find_or_initialize('test_0', 'control', 'test-alt').save
237
239
  Split::ExperimentCatalog.find_or_initialize('test_1', 'control', 'test-alt').save
238
- }
240
+ end
239
241
 
240
242
  it "should restore previously selected alternative" do
241
243
  expect(ab_user.active_experiments.size).to eq 1
@@ -243,6 +245,16 @@ describe Split::Helper do
243
245
  expect(ab_test(:test_0, {'control' => 1}, {"test-alt" => 100})).to eq 'test-alt'
244
246
  end
245
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
+
246
258
  it "lets override existing choice" do
247
259
  pending "this requires user store reset on first call not depending on whelther it is current trial"
248
260
  @params = { 'ab_test' => { 'test_1' => 'test-alt' } }
@@ -265,33 +277,63 @@ describe Split::Helper do
265
277
  end
266
278
 
267
279
  describe 'metadata' do
268
- before do
269
- Split.configuration.experiments = {
270
- :my_experiment => {
271
- :alternatives => ["one", "two"],
272
- :resettable => false,
273
- :metadata => { 'one' => 'Meta1', 'two' => 'Meta2' }
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
+ }
274
288
  }
275
- }
276
- end
289
+ end
277
290
 
278
- it 'should be passed to helper block' do
279
- @params = { 'ab_test' => { 'my_experiment' => 'one' } }
280
- expect(ab_test('my_experiment')).to eq 'one'
281
- expect(ab_test('my_experiment') do |alternative, meta|
282
- meta
283
- end).to eq('Meta1')
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
284
309
  end
285
310
 
286
- it 'should pass empty hash to helper block if library disabled' do
287
- Split.configure do |config|
288
- config.enabled = false
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({})
289
326
  end
290
327
 
291
- expect(ab_test('my_experiment')).to eq 'one'
292
- expect(ab_test('my_experiment') do |_, meta|
293
- meta
294
- end).to eq({})
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
295
337
  end
296
338
  end
297
339
 
@@ -1096,15 +1138,9 @@ describe Split::Helper do
1096
1138
  end
1097
1139
 
1098
1140
  it "should increment the counter for the specified-goal completed alternative" do
1099
- expect(lambda {
1100
- expect(lambda {
1101
- ab_finished({"link_color" => ["purchase"]})
1102
- }).not_to change {
1103
- Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal2)
1104
- }
1105
- }).to change {
1106
- Split::Alternative.new(@alternative_name, @experiment_name).completed_count(@goal1)
1107
- }.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)
1108
1144
  end
1109
1145
  end
1110
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} -0000\Z/)
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
@@ -60,6 +60,15 @@ describe Split::Persistence::RedisAdapter do
60
60
  end
61
61
  end
62
62
 
63
+ describe '#find' do
64
+ before { Split::Persistence::RedisAdapter.with_config(:lookup_by => proc{'frag'}, :namespace => 'a_namespace') }
65
+
66
+ it "should create and user from a given key" do
67
+ adapter = Split::Persistence::RedisAdapter.find(2)
68
+ expect(adapter.redis_key).to eq("a_namespace:2")
69
+ end
70
+ end
71
+
63
72
  context 'functional tests' do
64
73
  before { Split::Persistence::RedisAdapter.with_config(:lookup_by => 'lookup') }
65
74