split 1.4.1 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 07f7886845453ce92ac81c33d32927c4c0528d39
4
- data.tar.gz: 26a46a18c8a8e743ea532be9f9e2e3c63f516f4e
3
+ metadata.gz: eaed323e2d2459412259d82d76e4000c299c1db6
4
+ data.tar.gz: 54e05592a68ed6285e0ba613dadac52ed79bf23e
5
5
  SHA512:
6
- metadata.gz: 1beb35f67693fa2073877c112cda7ba7abec3dfeff05699fdf13275d79ba0b424c982cc7c2811fdde826fcbced38f4e6b34d1e8db56786bf43aa7edff6d0bb92
7
- data.tar.gz: d5956789a67b6d92d46e86e55b340900f806951a3dfea7419e1889df6844e21ef20a6777420fbb1362a8001f15c30e9114eb6020ec0de8b3bb508c8c58dfc6af
6
+ metadata.gz: a981adeb6a103db7766be39180bd353a31f642c659e4044071fbe8a094f4e36558705b477dd8a4b6eff893b11823209a241c24d5acc8d339201108abb33bf4b0
7
+ data.tar.gz: 7ca7d69fa815e194f9273974df0da3c556cb186f01720e8b342edd9482814725e4478006581b1878b1fb36f9fa53ed6f900176d3809101785d03bbac80e52f43
@@ -1,3 +1,9 @@
1
+ ## 1.4.2 (April 25th, 2016)
2
+
3
+ Misc:
4
+
5
+ - Deprecated some legacy methods (@andreibondarev, #374)
6
+
1
7
  ## 1.4.1 (April 21st, 2016)
2
8
 
3
9
  Bugfixes:
data/README.md CHANGED
@@ -165,6 +165,8 @@ finished(:experiment_name, reset: false)
165
165
 
166
166
  The user will then always see the alternative they started with.
167
167
 
168
+ Any old unfinished experiment key will be deleted from the user's data storage if the experiment had been removed or is over and a winner had been chosen. This allows a user to enroll into any new experiment in cases when the `allow_multiple_experiments` config option is set to `false`.
169
+
168
170
  ### Multiple experiments at once
169
171
 
170
172
  By default Split will avoid users participating in multiple experiments at once. This means you are less likely to skew results by adding in more variation to your tests.
@@ -12,6 +12,7 @@
12
12
  persistence
13
13
  encapsulated_helper
14
14
  trial
15
+ user
15
16
  version
16
17
  zscore].each do |f|
17
18
  require "split/#{f}"
@@ -2,9 +2,9 @@
2
2
  # Split's helper exposes all kinds of methods we don't want to
3
3
  # mix into our model classes.
4
4
  #
5
- # This module exposes only two methods
6
- # - ab_test and
7
- # - ab_test_finished
5
+ # This module exposes only two methods:
6
+ # - ab_test()
7
+ # - finished()
8
8
  # that can safely be mixed into any class.
9
9
  #
10
10
  # Passes the instance of the class that it's mixed into to the
@@ -22,7 +22,7 @@ module Split
22
22
  end
23
23
 
24
24
  def ab_user
25
- @ab_user ||= Split::Persistence.adapter.new(@context)
25
+ @ab_user ||= Split::User.new(@context)
26
26
  end
27
27
  end
28
28
 
@@ -44,6 +44,7 @@ module Split
44
44
  end
45
45
 
46
46
  def ab_test_finished(*arguments)
47
+ warn 'DEPRECATION WARNING: ab_test_finished is deprecated and will be removed from Split 1.5.0'
47
48
  split_context_shim.finished *arguments
48
49
  end
49
50
 
@@ -6,7 +6,6 @@ module Split
6
6
  def ab_test(metric_descriptor, control = nil, *alternatives)
7
7
  begin
8
8
  experiment = ExperimentCatalog.find_or_initialize(metric_descriptor, control, *alternatives)
9
-
10
9
  alternative = if Split.configuration.enabled
11
10
  experiment.save
12
11
  trial = Trial.new(:user => ab_user, :experiment => experiment,
@@ -88,13 +87,14 @@ module Split
88
87
  end
89
88
 
90
89
  def begin_experiment(experiment, alternative_name = nil)
90
+ warn 'DEPRECATION WARNING: begin_experiment is deprecated and will be removed from Split 1.5.0'
91
91
  alternative_name ||= experiment.control.name
92
92
  ab_user[experiment.key] = alternative_name
93
93
  alternative_name
94
94
  end
95
95
 
96
96
  def ab_user
97
- @ab_user ||= Split::Persistence.adapter.new(self)
97
+ @ab_user ||= User.new(self)
98
98
  end
99
99
 
100
100
  def exclude_visitor?
@@ -50,6 +50,7 @@ module Split
50
50
  # method is guaranteed to only run once, and will skip the alternative choosing process if run
51
51
  # a second time.
52
52
  def choose!(context = nil)
53
+ @user.cleanup_old_experiments
53
54
  # Only run the process once
54
55
  return alternative if @alternative_choosen
55
56
 
@@ -0,0 +1,24 @@
1
+ module Split
2
+ class User
3
+ extend Forwardable
4
+ def_delegators :@user, :keys, :[], :[]=, :delete
5
+ attr_reader :user
6
+
7
+ def initialize(context)
8
+ @user = Split::Persistence.adapter.new(context)
9
+ end
10
+
11
+ def cleanup_old_experiments
12
+ user.keys.each do |key|
13
+ experiment = ExperimentCatalog.find key_without_version(key)
14
+ if experiment.nil? || experiment.has_winner? || experiment.start_time.nil?
15
+ user.delete key
16
+ end
17
+ end
18
+ end
19
+
20
+ def key_without_version(key)
21
+ key.split(/\:\d(?!\:)/)[0]
22
+ end
23
+ end
24
+ end
@@ -2,6 +2,6 @@
2
2
  module Split
3
3
  MAJOR = 1
4
4
  MINOR = 4
5
- PATCH = 1
5
+ PATCH = 2
6
6
  VERSION = [MAJOR, MINOR, PATCH].join('.')
7
7
  end
@@ -6,7 +6,7 @@ describe Split::EncapsulatedHelper do
6
6
 
7
7
  before do
8
8
  allow_any_instance_of(Split::EncapsulatedHelper::ContextShim).to receive(:ab_user)
9
- .and_return({})
9
+ .and_return(mock_user)
10
10
  end
11
11
 
12
12
  def params
@@ -172,7 +172,7 @@ describe Split::Helper do
172
172
  it "should only let a user participate in one experiment at a time" do
173
173
  link_color = ab_test('link_color', 'blue', 'red')
174
174
  ab_test('button_size', 'small', 'big')
175
- expect(ab_user).to eq({'link_color' => link_color})
175
+ expect(ab_user['link_color']).to eq(link_color)
176
176
  big = Split::Alternative.new('big', 'button_size')
177
177
  expect(big.participant_count).to eq(0)
178
178
  small = Split::Alternative.new('small', 'button_size')
@@ -185,7 +185,8 @@ describe Split::Helper do
185
185
  end
186
186
  link_color = ab_test('link_color', 'blue', 'red')
187
187
  button_size = ab_test('button_size', 'small', 'big')
188
- expect(ab_user).to eq({'link_color' => link_color, 'button_size' => button_size})
188
+ expect(ab_user['link_color']).to eq(link_color)
189
+ expect(ab_user['button_size']).to eq(button_size)
189
190
  button_size_alt = Split::Alternative.new(button_size, 'button_size')
190
191
  expect(button_size_alt.participant_count).to eq(1)
191
192
  end
@@ -193,9 +194,9 @@ describe Split::Helper do
193
194
  it "should not over-write a finished key when an experiment is on a later version" do
194
195
  experiment.increment_version
195
196
  ab_user = { experiment.key => 'blue', experiment.finished_key => true }
196
- finshed_session = ab_user.dup
197
+ finished_session = ab_user.dup
197
198
  ab_test('link_color', 'blue', 'red')
198
- expect(ab_user).to eq(finshed_session)
199
+ expect(ab_user).to eq(finished_session)
199
200
  end
200
201
  end
201
202
 
@@ -247,7 +248,8 @@ describe Split::Helper do
247
248
 
248
249
  it "should set experiment's finished key if reset is false" do
249
250
  finished(@experiment_name, {:reset => false})
250
- expect(ab_user).to eq(@experiment.key => @alternative_name, @experiment.finished_key => true)
251
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
252
+ expect(ab_user[@experiment.finished_key]).to eq(true)
251
253
  end
252
254
 
253
255
  it 'should not increment the counter if reset is false and the experiment has been already finished' do
@@ -279,30 +281,31 @@ describe Split::Helper do
279
281
  end
280
282
 
281
283
  it "should clear out the user's participation from their session" do
282
- expect(ab_user).to eq(@experiment.key => @alternative_name)
284
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
283
285
  finished(@experiment_name)
284
- expect(ab_user).to eq({})
286
+ expect(ab_user.keys).to be_empty
285
287
  end
286
288
 
287
289
  it "should not clear out the users session if reset is false" do
288
- expect(ab_user).to eq(@experiment.key => @alternative_name)
290
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
289
291
  finished(@experiment_name, {:reset => false})
290
- expect(ab_user).to eq(@experiment.key => @alternative_name, @experiment.finished_key => true)
292
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
293
+ expect(ab_user[@experiment.finished_key]).to eq(true)
291
294
  end
292
295
 
293
296
  it "should reset the users session when experiment is not versioned" do
294
- expect(ab_user).to eq(@experiment.key => @alternative_name)
297
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
295
298
  finished(@experiment_name)
296
- expect(ab_user).to eq({})
299
+ expect(ab_user.keys).to be_empty
297
300
  end
298
301
 
299
302
  it "should reset the users session when experiment is versioned" do
300
303
  @experiment.increment_version
301
304
  @alternative_name = ab_test(@experiment_name, *@alternatives)
302
305
 
303
- expect(ab_user).to eq(@experiment.key => @alternative_name)
306
+ expect(ab_user[@experiment.key]).to eq(@alternative_name)
304
307
  finished(@experiment_name)
305
- expect(ab_user).to eq({})
308
+ expect(ab_user.keys).to be_empty
306
309
  end
307
310
 
308
311
  it "should do nothing where the experiment was not started by this user" do
@@ -337,7 +340,8 @@ describe Split::Helper do
337
340
  experiment = Split::ExperimentCatalog.find :my_experiment
338
341
 
339
342
  finished :my_experiment
340
- expect(ab_user).to eq(experiment.key => alternative, experiment.finished_key => true)
343
+ expect(ab_user[experiment.key]).to eq(alternative)
344
+ expect(ab_user[experiment.finished_key]).to eq(true)
341
345
  end
342
346
  end
343
347
 
@@ -618,28 +622,28 @@ describe Split::Helper do
618
622
  it "should use version zero if no version is present" do
619
623
  alternative_name = ab_test('link_color', 'blue', 'red')
620
624
  expect(experiment.version).to eq(0)
621
- expect(ab_user).to eq({'link_color' => alternative_name})
625
+ expect(ab_user['link_color']).to eq(alternative_name)
622
626
  end
623
627
 
624
628
  it "should save the version of the experiment to the session" do
625
629
  experiment.reset
626
630
  expect(experiment.version).to eq(1)
627
631
  alternative_name = ab_test('link_color', 'blue', 'red')
628
- expect(ab_user).to eq({'link_color:1' => alternative_name})
632
+ expect(ab_user['link_color:1']).to eq(alternative_name)
629
633
  end
630
634
 
631
635
  it "should load the experiment even if the version is not 0" do
632
636
  experiment.reset
633
637
  expect(experiment.version).to eq(1)
634
638
  alternative_name = ab_test('link_color', 'blue', 'red')
635
- expect(ab_user).to eq({'link_color:1' => alternative_name})
639
+ expect(ab_user['link_color:1']).to eq(alternative_name)
636
640
  return_alternative_name = ab_test('link_color', 'blue', 'red')
637
641
  expect(return_alternative_name).to eq(alternative_name)
638
642
  end
639
643
 
640
644
  it "should reset the session of a user on an older version of the experiment" do
641
645
  alternative_name = ab_test('link_color', 'blue', 'red')
642
- expect(ab_user).to eq({'link_color' => alternative_name})
646
+ expect(ab_user['link_color']).to eq(alternative_name)
643
647
  alternative = Split::Alternative.new(alternative_name, 'link_color')
644
648
  expect(alternative.participant_count).to eq(1)
645
649
 
@@ -656,7 +660,7 @@ describe Split::Helper do
656
660
 
657
661
  it "should cleanup old versions of experiments from the session" do
658
662
  alternative_name = ab_test('link_color', 'blue', 'red')
659
- expect(ab_user).to eq({'link_color' => alternative_name})
663
+ expect(ab_user['link_color']).to eq(alternative_name)
660
664
  alternative = Split::Alternative.new(alternative_name, 'link_color')
661
665
  expect(alternative.participant_count).to eq(1)
662
666
 
@@ -666,12 +670,12 @@ describe Split::Helper do
666
670
  expect(alternative.participant_count).to eq(0)
667
671
 
668
672
  new_alternative_name = ab_test('link_color', 'blue', 'red')
669
- expect(ab_user).to eq({'link_color:1' => new_alternative_name})
673
+ expect(ab_user['link_color:1']).to eq(new_alternative_name)
670
674
  end
671
675
 
672
676
  it "should only count completion of users on the current version" do
673
677
  alternative_name = ab_test('link_color', 'blue', 'red')
674
- expect(ab_user).to eq({'link_color' => alternative_name})
678
+ expect(ab_user['link_color']).to eq(alternative_name)
675
679
  alternative = Split::Alternative.new(alternative_name, 'link_color')
676
680
 
677
681
  experiment.reset
@@ -19,11 +19,15 @@ RSpec.configure do |config|
19
19
  config.before(:each) do
20
20
  Split.configuration = Split::Configuration.new
21
21
  Split.redis.flushall
22
- @ab_user = {}
22
+ @ab_user = mock_user
23
23
  params = nil
24
24
  end
25
25
  end
26
26
 
27
+ def mock_user
28
+ Split::User.new(double(session: {}))
29
+ end
30
+
27
31
  def session
28
32
  @session ||= {}
29
33
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  require 'split/trial'
4
4
 
5
5
  describe Split::Trial do
6
- let(:user) { Split::Persistence.adapter.new(double(session: {})) }
6
+ let(:user) { mock_user }
7
7
  let(:experiment) do
8
8
  Split::Experiment.new('basket_text', :alternatives => ['basket', 'cart']).save
9
9
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'split/experiment_catalog'
3
+ require 'split/experiment'
4
+ require 'split/user'
5
+
6
+ describe Split::User do
7
+ let(:context) do
8
+ double(:session => { split: { 'link_color' => 'blue' } })
9
+ end
10
+
11
+ before(:each) do
12
+ @subject = described_class.new(context)
13
+ end
14
+
15
+ it 'delegates methods correctly' do
16
+ expect(@subject['link_color']).to eq(@subject.user['link_color'])
17
+ end
18
+
19
+ context '#cleanup_old_experiments' do
20
+ let(:experiment) { Split::Experiment.new('link_color') }
21
+
22
+ it 'removes key if experiment is not found' do
23
+ @subject.cleanup_old_experiments
24
+ expect(@subject.keys).to be_empty
25
+ end
26
+
27
+ it 'removes key if experiment has a winner' do
28
+ allow(Split::ExperimentCatalog).to receive(:find).with('link_color').and_return(experiment)
29
+ allow(experiment).to receive(:start_time).and_return(Date.today)
30
+ allow(experiment).to receive(:has_winner?).and_return(true)
31
+ @subject.cleanup_old_experiments
32
+ expect(@subject.keys).to be_empty
33
+ end
34
+
35
+ it 'removes key if experiment has not started yet' do
36
+ allow(Split::ExperimentCatalog).to receive(:find).with('link_color').and_return(experiment)
37
+ allow(experiment).to receive(:has_winner?).and_return(false)
38
+ @subject.cleanup_old_experiments
39
+ expect(@subject.keys).to be_empty
40
+ end
41
+ end
42
+ 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: 1.4.1
4
+ version: 1.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-21 00:00:00.000000000 Z
11
+ date: 2016-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -203,6 +203,7 @@ files:
203
203
  - lib/split/persistence/redis_adapter.rb
204
204
  - lib/split/persistence/session_adapter.rb
205
205
  - lib/split/trial.rb
206
+ - lib/split/user.rb
206
207
  - lib/split/version.rb
207
208
  - lib/split/zscore.rb
208
209
  - spec/algorithms/weighted_sample_spec.rb
@@ -224,6 +225,7 @@ files:
224
225
  - spec/spec_helper.rb
225
226
  - spec/support/cookies_mock.rb
226
227
  - spec/trial_spec.rb
228
+ - spec/user_spec.rb
227
229
  - split.gemspec
228
230
  homepage: https://github.com/splitrb/split
229
231
  licenses:
@@ -269,3 +271,4 @@ test_files:
269
271
  - spec/spec_helper.rb
270
272
  - spec/support/cookies_mock.rb
271
273
  - spec/trial_spec.rb
274
+ - spec/user_spec.rb