split 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,14 @@
1
+ ## 0.6.1 (May 4, 2013)
2
+
3
+ Bugfixes:
4
+
5
+ - Use the specified algorithm for the experiment instead of the default (@woodhull, #165)
6
+
7
+ Misc:
8
+
9
+ - Ensure experiements are valid when configuring (@ashmckenzie, #159)
10
+ - Allow arrays to be passed to ab_test (@fenelon, #156)
11
+
1
12
  ## 0.6.0 (April 4, 2013)
2
13
 
3
14
  Features:
@@ -19,7 +30,7 @@ Misc:
19
30
  - updated minimum json version requirement
20
31
  - Refactor Yaml Configuration (@rtwomey, #124)
21
32
  - Refactoring of Experiments (@iangreenleaf @tamird, #117 #118)
22
- - Added more known Bots, including Pingdom, Bing, YandexBot (@julesie, @zinkkrysty)
33
+ - Added more known Bots, including Pingdom, Bing, YandexBot (@julesie, @zinkkrysty, @dimko)
23
34
  - Improved Readme (@iangreenleaf @phoet)
24
35
 
25
36
  ## 0.5.0 (January 28, 2013)
@@ -30,7 +30,7 @@ module Split
30
30
 
31
31
  @redis = Redis::Namespace.new(namespace, :redis => redis)
32
32
  elsif server.respond_to? :namespace=
33
- @redis = server
33
+ @redis = server
34
34
  else
35
35
  @redis = Redis::Namespace.new(:split, :redis => server)
36
36
  end
@@ -9,10 +9,11 @@ module Split
9
9
  attr_accessor :db_failover_allow_parameter_override
10
10
  attr_accessor :allow_multiple_experiments
11
11
  attr_accessor :enabled
12
- attr_accessor :experiments
13
12
  attr_accessor :persistence
14
13
  attr_accessor :algorithm
15
14
 
15
+ attr_reader :experiments
16
+
16
17
  def bots
17
18
  @bots ||= {
18
19
  # Indexers
@@ -63,6 +64,11 @@ module Split
63
64
  }
64
65
  end
65
66
 
67
+ def experiments= experiments
68
+ raise InvalidExperimentsFormatError.new('Experiments must be a Hash') unless experiments.respond_to?(:keys)
69
+ @experiments = experiments
70
+ end
71
+
66
72
  def disabled?
67
73
  !enabled
68
74
  end
@@ -1,4 +1,5 @@
1
1
  module Split
2
2
  class InvalidPersistenceAdapterError < StandardError; end
3
3
  class ExperimentNotFound < StandardError; end
4
- end
4
+ class InvalidExperimentsFormatError < StandardError; end
5
+ end
@@ -166,7 +166,7 @@ module Split
166
166
 
167
167
  def random_alternative
168
168
  if alternatives.length > 1
169
- Split.configuration.algorithm.choose_alternative(self)
169
+ algorithm.choose_alternative(self)
170
170
  else
171
171
  alternatives.first
172
172
  end
@@ -6,6 +6,11 @@ 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'])
10
+ if control.is_a? Array and alternatives.length.zero?
11
+ control, alternatives = control.first, control[1..-1]
12
+ end
13
+
9
14
  begin
10
15
  experiment_name_with_version, goals = normalize_experiment(metric_descriptor)
11
16
  experiment_name = experiment_name_with_version.to_s.split(':')[0]
@@ -6,7 +6,7 @@ module Split
6
6
  def initialize(attrs = {})
7
7
  self.experiment = attrs[:experiment] if !attrs[:experiment].nil?
8
8
  self.alternative = attrs[:alternative] if !attrs[:alternative].nil?
9
- self.goals = attrs[:goals] if !attrs[:goals].nil?
9
+ self.goals = attrs[:goals].nil? ? [] : attrs[:goals]
10
10
  end
11
11
 
12
12
  def alternative
@@ -1,6 +1,6 @@
1
1
  module Split
2
2
  MAJOR = 0
3
3
  MINOR = 6
4
- PATCH = 0
4
+ PATCH = 1
5
5
  VERSION = [MAJOR, MINOR, PATCH].join('.')
6
6
  end
@@ -120,20 +120,44 @@ describe Split::Configuration do
120
120
  end
121
121
 
122
122
  context "as symbols" do
123
- before do
124
- experiments_yaml = <<-eos
125
- :my_experiment:
126
- :alternatives:
127
- - Control Opt
128
- - Alt One
129
- - Alt Two
130
- :resettable: false
131
- eos
132
- @config.experiments = YAML.load(experiments_yaml)
123
+
124
+ context "with valid YAML" do
125
+ before do
126
+ experiments_yaml = <<-eos
127
+ :my_experiment:
128
+ :alternatives:
129
+ - Control Opt
130
+ - Alt One
131
+ - Alt Two
132
+ :resettable: false
133
+ eos
134
+ @config.experiments = YAML.load(experiments_yaml)
135
+ end
136
+
137
+ it "should normalize experiments" do
138
+ @config.normalized_experiments.should == {:my_experiment=>{:alternatives=>["Control Opt", ["Alt One", "Alt Two"]]}}
139
+ end
133
140
  end
134
141
 
135
- it "should normalize experiments" do
136
- @config.normalized_experiments.should == {:my_experiment=>{:alternatives=>["Control Opt", ["Alt One", "Alt Two"]]}}
142
+ context "with invalid YAML" do
143
+
144
+ let(:yaml) { YAML.load(input) }
145
+
146
+ context "with an empty string" do
147
+ let(:input) { '' }
148
+
149
+ it "should raise an error" do
150
+ expect { @config.experiments = yaml }.to raise_error(/Experiments must be a Hash/)
151
+ end
152
+ end
153
+
154
+ context "with just the YAML header" do
155
+ let(:input) { '---' }
156
+
157
+ it "should raise an error" do
158
+ expect { @config.experiments = yaml }.to raise_error(/Experiments must be a Hash/)
159
+ end
160
+ end
137
161
  end
138
162
  end
139
163
  end
@@ -139,6 +139,15 @@ describe Split::Experiment do
139
139
  e.should == experiment
140
140
  e.algorithm.should == Split::Algorithms::Whiplash
141
141
  end
142
+
143
+ it "should persist a new experiment in redis, that does not exist in the configuration file" do
144
+ experiment = Split::Experiment.new('foobar', :alternatives => ['tra', 'la'], :algorithm => Split::Algorithms::Whiplash)
145
+ experiment.save
146
+
147
+ e = Split::Experiment.find('foobar')
148
+ e.should == experiment
149
+ e.alternatives.collect{|a| a.name}.should == ['tra', 'la']
150
+ end
142
151
  end
143
152
 
144
153
  describe 'deleting' do
@@ -230,7 +239,8 @@ describe Split::Experiment do
230
239
  end
231
240
 
232
241
  it "should use the specified algorithm if a winner does not exist" do
233
- Split.configuration.algorithm.should_receive(:choose_alternative).and_return(Split::Alternative.new('green', 'link_color'))
242
+ experiment.algorithm = Split::Algorithms::Whiplash
243
+ experiment.algorithm.should_receive(:choose_alternative).and_return(Split::Alternative.new('green', 'link_color'))
234
244
  experiment.next_alternative.name.should eql('green')
235
245
  end
236
246
  end
@@ -15,6 +15,10 @@ describe Split::Helper do
15
15
  lambda { ab_test('xyz', '1', '2', '3') }.should_not raise_error
16
16
  end
17
17
 
18
+ it "should not raise an error when passed an array for alternatives" do
19
+ lambda { ab_test('xyz', ['1', '2', '3']) }.should_not raise_error
20
+ end
21
+
18
22
  it "should raise the appropriate error when passed integers for alternatives" do
19
23
  lambda { ab_test('xyz', 1, 2, 3) }.should raise_error(ArgumentError)
20
24
  end
@@ -801,8 +805,7 @@ describe Split::Helper do
801
805
  end
802
806
 
803
807
  it "fails gracefully if config is missing" do
804
- Split.configuration.experiments = nil
805
- lambda { ab_test :my_experiment }.should raise_error(/not found/i)
808
+ lambda { Split.configuration.experiments = nil }.should raise_error(/Experiments must be a Hash/)
806
809
  end
807
810
 
808
811
  it "fails gracefully if config is missing alternatives" do
@@ -8,6 +8,7 @@ describe Split::Trial do
8
8
  trial = Split::Trial.new(:experiment => experiment, :alternative => alternative)
9
9
  trial.experiment.should == experiment
10
10
  trial.alternative.should == alternative
11
+ trial.goals.should == []
11
12
  end
12
13
 
13
14
  describe "alternative" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: split
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-04 00:00:00.000000000 Z
12
+ date: 2013-05-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
16
- requirement: &70363788679740 !ruby/object:Gem::Requirement
16
+ requirement: &70334741839320 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70363788679740
24
+ version_requirements: *70334741839320
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: redis-namespace
27
- requirement: &70363788691860 !ruby/object:Gem::Requirement
27
+ requirement: &70334741851360 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.1.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70363788691860
35
+ version_requirements: *70334741851360
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sinatra
38
- requirement: &70363788708940 !ruby/object:Gem::Requirement
38
+ requirement: &70334741849020 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.2.6
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70363788708940
46
+ version_requirements: *70334741849020
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: simple-random
49
- requirement: &70363788722360 !ruby/object:Gem::Requirement
49
+ requirement: &70334741846920 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70363788722360
57
+ version_requirements: *70334741846920
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rake
60
- requirement: &70363788720900 !ruby/object:Gem::Requirement
60
+ requirement: &70334741860380 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70363788720900
68
+ version_requirements: *70334741860380
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: bundler
71
- requirement: &70363788716120 !ruby/object:Gem::Requirement
71
+ requirement: &70334741859480 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '1.0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70363788716120
79
+ version_requirements: *70334741859480
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rspec
82
- requirement: &70363788729360 !ruby/object:Gem::Requirement
82
+ requirement: &70334741857740 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '2.12'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70363788729360
90
+ version_requirements: *70334741857740
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rack-test
93
- requirement: &70363788751640 !ruby/object:Gem::Requirement
93
+ requirement: &70334741856560 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ~>
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '0.6'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70363788751640
101
+ version_requirements: *70334741856560
102
102
  description:
103
103
  email:
104
104
  - andrewnez@gmail.com