split 0.7.0 → 0.7.1

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: 2d56d14f269b46f4409460cd7f8805c423d2ba92
4
- data.tar.gz: 574c8c51ccf9a72efad3aace656e80e95301bd3b
3
+ metadata.gz: 1819f5bb2d97f871dce0a859b30f0d6a10a94341
4
+ data.tar.gz: b9026aa79967696c5f29de4ea320972257a0327d
5
5
  SHA512:
6
- metadata.gz: 9be1ddfbab6be25f4458ff893a272523e15b8109727b2eddae3d508db5afed4b810693d61e9c5931b7e5d0ebf36610d442bb08a22869b29c8583ca07e144722f
7
- data.tar.gz: 77b36b084453e5aaa46be4d9d1b0551e22c51547a4df02a642dd9ebf94c23c5fdb6fa8a01a2fe7be8f5052dce3cc5003a7c29c7b178d4c54ee5c57b098fd0240
6
+ metadata.gz: 2736a69b3f38e61393b4bb3a44fa9bea24a5ed2982689eff841d1e3c2e9516cb870795bf2fd7299e394bd8c520a13335e8399163eacf92dd512cdd46cfeff212
7
+ data.tar.gz: 3ca9fecdbe966b6e64586e3bcaa500be8daaa1b2a2aab2cefac664d98e73297f539b218fe9728a567fc612770bb947dcc2bd90db7343dc5a59490a9d3377aefd
data/LICENSE CHANGED
@@ -1,3 +1,5 @@
1
+ The MIT License (MIT)
2
+
1
3
  Copyright (c) 2013 Andrew Nesbitt
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
@@ -45,6 +45,12 @@ module Split
45
45
  redirect url('/')
46
46
  end
47
47
 
48
+ post '/reopen/:experiment' do
49
+ @experiment = Split::Experiment.find(params[:experiment])
50
+ @experiment.reset_winner
51
+ redirect url('/')
52
+ end
53
+
48
54
  delete '/:experiment' do
49
55
  @experiment = Split::Experiment.find(params[:experiment])
50
56
  @experiment.delete
@@ -16,4 +16,9 @@ function confirmWinner() {
16
16
  function confirmStep(step) {
17
17
  var agree = confirm(step);
18
18
  return agree ? true : false;
19
- }
19
+ }
20
+
21
+ function confirmReopen() {
22
+ var agree = confirm("This will reopen the experiment. Are you sure?");
23
+ return agree ? true : false;
24
+ }
@@ -1,3 +1,8 @@
1
+ <% if experiment.has_winner? %>
2
+ <form action="<%= url "/reopen/#{experiment.name}" %>" method='post' onclick="return confirmReopen()">
3
+ <input type="submit" value="Reopen Experiment">
4
+ </form>
5
+ <% end %>
1
6
  <% if experiment.start_time %>
2
7
  <form action="<%= url "/reset/#{experiment.name}" %>" method='post' onclick="return confirmReset()">
3
8
  <input type="submit" value="Reset Data">
@@ -59,7 +59,7 @@
59
59
  <span title='z-score: <%= round(alternative.z_score(goal), 3) %>'><%= confidence_level(alternative.z_score(goal)) %></span>
60
60
  </td>
61
61
  <td>
62
- <% if experiment.winner %>
62
+ <% if experiment.has_winner? %>
63
63
  <% if experiment.winner.name == alternative.name %>
64
64
  Winner
65
65
  <% else %>
@@ -79,7 +79,6 @@ module Split
79
79
  @alternatives.reverse.each {|a| Split.redis.lpush(name, a.name)}
80
80
  @goals.reverse.each {|a| Split.redis.lpush(goals_key, a)} unless @goals.nil?
81
81
  else
82
-
83
82
  existing_alternatives = load_alternatives_from_redis
84
83
  existing_goals = load_goals_from_redis
85
84
  unless existing_alternatives == @alternatives.map(&:name) && existing_goals == @goals
@@ -149,6 +148,10 @@ module Split
149
148
  end
150
149
  end
151
150
 
151
+ def has_winner?
152
+ !winner.nil?
153
+ end
154
+
152
155
  def winner=(winner_name)
153
156
  Split.redis.hset(:experiment_winner, name, winner_name.to_s)
154
157
  end
@@ -6,16 +6,20 @@ module Split
6
6
  puts 'WARNING: You should always pass the control alternative through as the second argument with any other alternatives as the third because the order of the hash is not preserved in ruby 1.8'
7
7
  end
8
8
 
9
- # Check if array is passed to ab_test: ab_test('name', ['Alt 1', 'Alt 2', 'Alt 3'])
9
+ # Check if array is passed to ab_test
10
+ # e.g. ab_test('name', ['Alt 1', 'Alt 2', 'Alt 3'])
10
11
  if control.is_a? Array and alternatives.length.zero?
11
12
  control, alternatives = control.first, control[1..-1]
12
13
  end
13
14
 
14
15
  begin
15
- experiment_name_with_version, goals = normalize_experiment(metric_descriptor)
16
- experiment_name = experiment_name_with_version.to_s.split(':')[0]
17
- experiment = Split::Experiment.new(experiment_name, :alternatives => [control].compact + alternatives, :goals => goals)
18
- control ||= experiment.control && experiment.control.name
16
+ experiment_name_with_version, goals = normalize_experiment(metric_descriptor)
17
+ experiment_name = experiment_name_with_version.to_s.split(':')[0]
18
+ experiment = Split::Experiment.new(
19
+ experiment_name,
20
+ :alternatives => [control].compact + alternatives,
21
+ :goals => goals)
22
+ control ||= experiment.control && experiment.control.name
19
23
 
20
24
  ret = if Split.configuration.enabled
21
25
  experiment.save
@@ -23,8 +27,7 @@ module Split
23
27
  else
24
28
  control_variable(control)
25
29
  end
26
-
27
- rescue => e
30
+ rescue Errno::ECONNREFUSED => e
28
31
  raise(e) unless Split.configuration.db_failover
29
32
  Split.configuration.db_failover_on_db_error.call(e)
30
33
 
@@ -32,9 +35,7 @@ module Split
32
35
  ret = override_alternative(experiment_name)
33
36
  end
34
37
  ensure
35
- unless ret
36
- ret = control_variable(control)
37
- end
38
+ ret ||= control_variable(control)
38
39
  end
39
40
 
40
41
  if block_given?
@@ -55,7 +56,7 @@ module Split
55
56
  end
56
57
 
57
58
  def finish_experiment(experiment, options = {:reset => true})
58
- return true unless experiment.winner.nil?
59
+ return true if experiment.has_winner?
59
60
  should_reset = experiment.resettable? && options[:reset]
60
61
  if ab_user[experiment.finished_key] && !should_reset
61
62
  return true
@@ -168,7 +169,7 @@ module Split
168
169
  if override_present?(experiment.name)
169
170
  ret = override_alternative(experiment.name)
170
171
  ab_user[experiment.key] = ret if Split.configuration.store_override
171
- elsif ! experiment.winner.nil?
172
+ elsif experiment.has_winner?
172
173
  ret = experiment.winner.name
173
174
  else
174
175
  clean_old_versions(experiment)
@@ -10,7 +10,7 @@ module Split
10
10
  end
11
11
 
12
12
  def alternative
13
- @alternative ||= if experiment.winner
13
+ @alternative ||= if experiment.has_winner?
14
14
  experiment.winner
15
15
  end
16
16
  end
@@ -1,6 +1,6 @@
1
1
  module Split
2
2
  MAJOR = 0
3
3
  MINOR = 7
4
- PATCH = 0
4
+ PATCH = 1
5
5
  VERSION = [MAJOR, MINOR, PATCH].join('.')
6
6
  end
@@ -11,32 +11,22 @@ describe Split::Alternative do
11
11
  Split::Alternative.new('Cart', 'basket_text')
12
12
  }
13
13
 
14
- let(:experiment) {
14
+ let!(:experiment) {
15
15
  Split::Experiment.find_or_create({"basket_text" => ["purchase", "refund"]}, "Basket", "Cart")
16
16
  }
17
17
 
18
18
  let(:goal1) { "purchase" }
19
19
  let(:goal2) { "refund" }
20
20
 
21
- # setup experiment
22
- before do
23
- experiment
24
- end
25
-
26
21
  it "should have goals" do
27
22
  alternative.goals.should eql(["purchase", "refund"])
28
23
  end
29
24
 
30
- it "should have a name" do
31
- alternative.name.should eql('Basket')
32
- end
33
-
34
- it "return only the name" do
25
+ it "should have and only return the name" do
35
26
  alternative.name.should eql('Basket')
36
27
  end
37
28
 
38
29
  describe 'weights' do
39
-
40
30
  it "should set the weights" do
41
31
  experiment = Split::Experiment.new('basket_text', :alternatives => [{'Basket' => 0.6}, {"Cart" => 0.4}])
42
32
  first = experiment.alternatives[0]
@@ -248,7 +238,7 @@ describe Split::Alternative do
248
238
 
249
239
  alternative2.participant_count = 142
250
240
  alternative2.set_completed_count(119)
251
-
241
+
252
242
  alternative2.z_score.round(2).should eql(2.58)
253
243
  end
254
244
 
@@ -59,6 +59,53 @@ describe Split::Dashboard do
59
59
  end
60
60
  end
61
61
 
62
+ describe "index page" do
63
+ context "with winner" do
64
+ before { experiment.winner = 'red' }
65
+
66
+ it "displays `Reopen Experiment` button" do
67
+ get '/'
68
+
69
+ expect(last_response.body).to include('Reopen Experiment')
70
+ end
71
+ end
72
+
73
+ context "without winner" do
74
+ it "should not display `Reopen Experiment` button" do
75
+ get '/'
76
+
77
+ expect(last_response.body).to_not include('Reopen Experiment')
78
+ end
79
+ end
80
+ end
81
+
82
+ describe "reopen experiment" do
83
+ before { experiment.winner = 'red' }
84
+
85
+ it 'redirects' do
86
+ post "/reopen/#{experiment.name}"
87
+
88
+ expect(last_response).to be_redirect
89
+ end
90
+
91
+ it "removes winner" do
92
+ post "/reopen/#{experiment.name}"
93
+
94
+ expect(experiment).to_not have_winner
95
+ end
96
+
97
+ it "keeps existing stats" do
98
+ red_link.participant_count = 5
99
+ blue_link.participant_count = 7
100
+ experiment.winner = 'blue'
101
+
102
+ post "/reopen/#{experiment.name}"
103
+
104
+ expect(red_link.participant_count).to eq(5)
105
+ expect(blue_link.participant_count).to eq(7)
106
+ end
107
+ end
108
+
62
109
  it "should reset an experiment" do
63
110
  red_link.participant_count = 5
64
111
  blue_link.participant_count = 7
@@ -201,6 +201,22 @@ describe Split::Experiment do
201
201
  end
202
202
  end
203
203
 
204
+ describe 'has_winner?' do
205
+ context 'with winner' do
206
+ before { experiment.winner = 'red' }
207
+
208
+ it 'returns true' do
209
+ expect(experiment).to have_winner
210
+ end
211
+ end
212
+
213
+ context 'without winner' do
214
+ it 'returns false' do
215
+ expect(experiment).to_not have_winner
216
+ end
217
+ end
218
+ end
219
+
204
220
  describe 'reset' do
205
221
  before { experiment.save }
206
222
  it 'should reset all alternatives' do
@@ -10,7 +10,6 @@ describe Split::Helper do
10
10
  }
11
11
 
12
12
  describe "ab_test" do
13
-
14
13
  it "should not raise an error when passed strings for alternatives" do
15
14
  lambda { ab_test('xyz', '1', '2', '3') }.should_not raise_error
16
15
  end
@@ -41,7 +40,6 @@ describe Split::Helper do
41
40
  end
42
41
 
43
42
  it "should increment the participation counter after assignment to a new user" do
44
-
45
43
  previous_red_count = Split::Alternative.new('red', 'link_color').participant_count
46
44
  previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
47
45
 
@@ -81,9 +79,7 @@ describe Split::Helper do
81
79
  end
82
80
 
83
81
  it "should return the given alternative for an existing user" do
84
- alternative = ab_test('link_color', 'blue', 'red')
85
- repeat_alternative = ab_test('link_color', 'blue', 'red')
86
- alternative.should eql repeat_alternative
82
+ ab_test('link_color', 'blue', 'red').should eql ab_test('link_color', 'blue', 'red')
87
83
  end
88
84
 
89
85
  it 'should always return the winner if one is present' do
@@ -105,7 +101,7 @@ describe Split::Helper do
105
101
  alternative.should eql('red')
106
102
  end
107
103
 
108
- it "should not store the via params forced alternative" do
104
+ it "should not store the split when a param forced alternative" do
109
105
  @params = {'link_color' => 'blue'}
110
106
  ab_user.should_not_receive(:[]=)
111
107
  ab_test('link_color', 'blue', 'red')
@@ -135,7 +131,7 @@ describe Split::Helper do
135
131
  ret.should eql("shared/#{alt}")
136
132
  end
137
133
 
138
- it "should allow the share of visitors see an alternative to be specificed" do
134
+ it "should allow the share of visitors see an alternative to be specified" do
139
135
  ab_test('link_color', {'blue' => 0.8}, {'red' => 20})
140
136
  ['red', 'blue'].should include(ab_user['link_color'])
141
137
  end
@@ -277,7 +273,6 @@ describe Split::Helper do
277
273
  finished(@experiment_name)
278
274
  end
279
275
  end
280
-
281
276
  end
282
277
 
283
278
  context "finished with config" do
@@ -374,7 +369,6 @@ describe Split::Helper do
374
369
  ab_user[exp.key].should == alternative_name
375
370
  ab_user[exp.finished_key].should == true
376
371
  end
377
-
378
372
  end
379
373
 
380
374
  describe 'conversions' do
@@ -431,7 +425,6 @@ describe Split::Helper do
431
425
  end
432
426
  end
433
427
 
434
-
435
428
  describe 'when providing custom ignore logic' do
436
429
  context "using a proc to configure custom logic" do
437
430
 
@@ -452,7 +445,6 @@ describe Split::Helper do
452
445
  end
453
446
 
454
447
  shared_examples_for "a disabled test" do
455
-
456
448
  describe 'ab_test' do
457
449
  it 'should return the control' do
458
450
  alternative = ab_test('link_color', 'blue', 'red')
@@ -460,7 +452,6 @@ describe Split::Helper do
460
452
  end
461
453
 
462
454
  it "should not increment the participation count" do
463
-
464
455
  previous_red_count = Split::Alternative.new('red', 'link_color').participant_count
465
456
  previous_blue_count = Split::Alternative.new('blue', 'link_color').participant_count
466
457
 
@@ -489,9 +480,7 @@ describe Split::Helper do
489
480
  end
490
481
 
491
482
  describe 'when ip address is ignored' do
492
-
493
483
  context "individually" do
494
-
495
484
  before(:each) do
496
485
  @request = OpenStruct.new(:ip => '81.19.48.130')
497
486
  Split.configure do |c|
@@ -500,11 +489,9 @@ describe Split::Helper do
500
489
  end
501
490
 
502
491
  it_behaves_like "a disabled test"
503
-
504
492
  end
505
493
 
506
494
  context "for a range" do
507
-
508
495
  before(:each) do
509
496
  @request = OpenStruct.new(:ip => '81.19.48.129')
510
497
  Split.configure do |c|
@@ -513,11 +500,9 @@ describe Split::Helper do
513
500
  end
514
501
 
515
502
  it_behaves_like "a disabled test"
516
-
517
503
  end
518
504
 
519
505
  context "using both a range and a specific value" do
520
-
521
506
  before(:each) do
522
507
  @request = OpenStruct.new(:ip => '81.19.48.128')
523
508
  Split.configure do |c|
@@ -527,9 +512,7 @@ describe Split::Helper do
527
512
  end
528
513
 
529
514
  it_behaves_like "a disabled test"
530
-
531
515
  end
532
-
533
516
  end
534
517
 
535
518
  describe 'versioned experiments' do
@@ -602,13 +585,11 @@ describe Split::Helper do
602
585
  end
603
586
 
604
587
  context 'when redis is not available' do
605
-
606
588
  before(:each) do
607
589
  Split.stub(:redis).and_raise(Errno::ECONNREFUSED.new)
608
590
  end
609
591
 
610
592
  context 'and db_failover config option is turned off' do
611
-
612
593
  before(:each) do
613
594
  Split.configure do |config|
614
595
  config.db_failover = false
@@ -617,55 +598,35 @@ describe Split::Helper do
617
598
 
618
599
  describe 'ab_test' do
619
600
  it 'should raise an exception' do
620
- lambda {
621
- ab_test('link_color', 'blue', 'red')
622
- }.should raise_error
601
+ lambda { ab_test('link_color', 'blue', 'red') }.should raise_error
623
602
  end
624
603
  end
625
604
 
626
605
  describe 'finished' do
627
606
  it 'should raise an exception' do
628
- lambda {
629
- finished('link_color')
630
- }.should raise_error
607
+ lambda { finished('link_color') }.should raise_error
631
608
  end
632
609
  end
633
610
 
634
611
  describe "disable split testing" do
635
-
636
612
  before(:each) do
637
613
  Split.configure do |config|
638
614
  config.enabled = false
639
615
  end
640
616
  end
641
617
 
642
- after(:each) do
643
- Split.configure do |config|
644
- config.enabled = true
645
- end
646
- end
647
-
648
618
  it "should not attempt to connect to redis" do
649
-
650
- lambda {
651
- ab_test('link_color', 'blue', 'red')
652
- }.should_not raise_error
619
+ lambda { ab_test('link_color', 'blue', 'red') }.should_not raise_error
653
620
  end
654
621
 
655
622
  it "should return control variable" do
656
623
  ab_test('link_color', 'blue', 'red').should eq('blue')
657
- lambda {
658
- finished('link_color')
659
- }.should_not raise_error
624
+ lambda { finished('link_color') }.should_not raise_error
660
625
  end
661
-
662
626
  end
663
-
664
-
665
627
  end
666
628
 
667
629
  context 'and db_failover config option is turned on' do
668
-
669
630
  before(:each) do
670
631
  Split.configure do |config|
671
632
  config.db_failover = true
@@ -674,10 +635,9 @@ describe Split::Helper do
674
635
 
675
636
  describe 'ab_test' do
676
637
  it 'should not raise an exception' do
677
- lambda {
678
- ab_test('link_color', 'blue', 'red')
679
- }.should_not raise_error
638
+ lambda { ab_test('link_color', 'blue', 'red') }.should_not raise_error
680
639
  end
640
+
681
641
  it 'should call db_failover_on_db_error proc with error as parameter' do
682
642
  Split.configure do |config|
683
643
  config.db_failover_on_db_error = proc do |error|
@@ -687,6 +647,7 @@ describe Split::Helper do
687
647
  Split.configuration.db_failover_on_db_error.should_receive(:call)
688
648
  ab_test('link_color', 'blue', 'red')
689
649
  end
650
+
690
651
  it 'should always use first alternative' do
691
652
  ab_test('link_color', 'blue', 'red').should eq('blue')
692
653
  ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2).should eq('blue')
@@ -732,16 +693,16 @@ describe Split::Helper do
732
693
 
733
694
  describe 'finished' do
734
695
  it 'should not raise an exception' do
735
- lambda {
736
- finished('link_color')
737
- }.should_not raise_error
696
+ lambda { finished('link_color') }.should_not raise_error
738
697
  end
698
+
739
699
  it 'should call db_failover_on_db_error proc with error as parameter' do
740
700
  Split.configure do |config|
741
701
  config.db_failover_on_db_error = proc do |error|
742
702
  error.should be_a(Errno::ECONNREFUSED)
743
703
  end
744
704
  end
705
+
745
706
  Split.configuration.db_failover_on_db_error.should_receive(:call)
746
707
  finished('link_color')
747
708
  end
@@ -812,7 +773,6 @@ describe Split::Helper do
812
773
  ab_test :my_experiment
813
774
  experiment = Split::Experiment.new(:my_experiment)
814
775
  experiment.alternatives.collect{|a| [a.name, a.weight]}.should == [['control_opt', 0.67], ['second_opt', 0.1], ['third_opt', 0.23]]
815
-
816
776
  end
817
777
 
818
778
  it "accepts probability on some alternatives" do
@@ -842,7 +802,7 @@ describe Split::Helper do
842
802
  ab_test :my_experiment
843
803
  experiment = Split::Experiment.new(:my_experiment)
844
804
  names_and_weights = experiment.alternatives.collect{|a| [a.name, a.weight]}
845
- names_and_weights.should == [['control_opt', 0.18], ['second_opt', 0.18], ['third_opt', 0.64]]
805
+ names_and_weights.should == [['control_opt', 0.18], ['second_opt', 0.18], ['third_opt', 0.64]]
846
806
  names_and_weights.inject(0){|sum, nw| sum + nw[1]}.should == 1.0
847
807
  end
848
808
 
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: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-26 00:00:00.000000000 Z
11
+ date: 2014-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis