split 0.4.6 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.travis.yml +11 -3
  2. data/CHANGELOG.mdown +22 -1
  3. data/CONTRIBUTING.md +10 -0
  4. data/LICENSE +1 -1
  5. data/README.mdown +235 -60
  6. data/lib/split.rb +8 -9
  7. data/lib/split/algorithms.rb +3 -0
  8. data/lib/split/algorithms/weighted_sample.rb +17 -0
  9. data/lib/split/algorithms/whiplash.rb +35 -0
  10. data/lib/split/alternative.rb +12 -4
  11. data/lib/split/configuration.rb +91 -1
  12. data/lib/split/dashboard/helpers.rb +3 -3
  13. data/lib/split/dashboard/views/_experiment.erb +1 -1
  14. data/lib/split/exceptions.rb +4 -0
  15. data/lib/split/experiment.rb +112 -24
  16. data/lib/split/extensions.rb +3 -0
  17. data/lib/split/extensions/array.rb +4 -0
  18. data/lib/split/extensions/string.rb +15 -0
  19. data/lib/split/helper.rb +87 -55
  20. data/lib/split/metric.rb +68 -0
  21. data/lib/split/persistence.rb +28 -0
  22. data/lib/split/persistence/cookie_adapter.rb +44 -0
  23. data/lib/split/persistence/session_adapter.rb +28 -0
  24. data/lib/split/trial.rb +43 -0
  25. data/lib/split/version.rb +3 -3
  26. data/spec/algorithms/weighted_sample_spec.rb +18 -0
  27. data/spec/algorithms/whiplash_spec.rb +23 -0
  28. data/spec/alternative_spec.rb +81 -9
  29. data/spec/configuration_spec.rb +61 -9
  30. data/spec/dashboard_helpers_spec.rb +2 -5
  31. data/spec/dashboard_spec.rb +0 -2
  32. data/spec/experiment_spec.rb +144 -74
  33. data/spec/helper_spec.rb +234 -29
  34. data/spec/metric_spec.rb +30 -0
  35. data/spec/persistence/cookie_adapter_spec.rb +31 -0
  36. data/spec/persistence/session_adapter_spec.rb +31 -0
  37. data/spec/persistence_spec.rb +33 -0
  38. data/spec/spec_helper.rb +12 -0
  39. data/spec/support/cookies_mock.rb +19 -0
  40. data/spec/trial_spec.rb +59 -0
  41. data/split.gemspec +7 -3
  42. metadata +58 -29
  43. data/Guardfile +0 -5
@@ -0,0 +1,31 @@
1
+ require "spec_helper"
2
+
3
+ describe Split::Persistence::SessionAdapter do
4
+
5
+ let(:context) { mock(:session => {}) }
6
+ subject { Split::Persistence::SessionAdapter.new(context) }
7
+
8
+ describe "#[] and #[]=" do
9
+ it "should set and return the value for given key" do
10
+ subject["my_key"] = "my_value"
11
+ subject["my_key"].should eq("my_value")
12
+ end
13
+ end
14
+
15
+ describe "#delete" do
16
+ it "should delete the given key" do
17
+ subject["my_key"] = "my_value"
18
+ subject.delete("my_key")
19
+ subject["my_key"].should be_nil
20
+ end
21
+ end
22
+
23
+ describe "#keys" do
24
+ it "should return an array of the session's stored keys" do
25
+ subject["my_key"] = "my_value"
26
+ subject["my_second_key"] = "my_second_value"
27
+ subject.keys.should =~ ["my_key", "my_second_key"]
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,33 @@
1
+ require "spec_helper"
2
+
3
+ describe Split::Persistence do
4
+
5
+ subject { Split::Persistence }
6
+
7
+ describe ".adapter" do
8
+ context "when the persistence config is a symbol" do
9
+ it "should return the appropriate adapter for the symbol" do
10
+ Split.configuration.stub(:persistence).and_return(:cookie)
11
+ subject.adapter.should eq(Split::Persistence::CookieAdapter)
12
+ end
13
+
14
+ it "should return an adapter whose class is present in Split::Persistence::ADAPTERS" do
15
+ Split.configuration.stub(:persistence).and_return(:cookie)
16
+ Split::Persistence::ADAPTERS.values.should include(subject.adapter)
17
+ end
18
+
19
+ it "should raise if the adapter cannot be found" do
20
+ Split.configuration.stub(:persistence).and_return(:something_weird)
21
+ expect { subject.adapter }.to raise_error(Split::InvalidPersistenceAdapterError)
22
+ end
23
+ end
24
+ context "when the persistence config is a class" do
25
+ let(:custom_adapter_class) { MyCustomAdapterClass = Class.new }
26
+ it "should return that class" do
27
+ Split.configuration.stub(:persistence).and_return(custom_adapter_class)
28
+ subject.adapter.should eq(MyCustomAdapterClass)
29
+ end
30
+ end
31
+ end
32
+
33
+ end
@@ -6,6 +6,18 @@ require 'split'
6
6
  require 'ostruct'
7
7
  require 'complex' if RUBY_VERSION.match(/1\.8/)
8
8
 
9
+ Dir['./spec/support/*.rb'].each { |f| require f }
10
+
11
+ RSpec.configure do |config|
12
+ config.order = 'random'
13
+ config.before(:each) do
14
+ Split.configuration = Split::Configuration.new
15
+ Split.redis.flushall
16
+ @ab_user = {}
17
+ params = nil
18
+ end
19
+ end
20
+
9
21
  def session
10
22
  @session ||= {}
11
23
  end
@@ -0,0 +1,19 @@
1
+ class CookiesMock
2
+
3
+ def initialize
4
+ @cookies = {}
5
+ end
6
+
7
+ def []=(key, value)
8
+ @cookies[key] = value[:value]
9
+ end
10
+
11
+ def [](key)
12
+ @cookies[key]
13
+ end
14
+
15
+ def delete(key)
16
+ @cookies.delete(key)
17
+ end
18
+
19
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+ require 'split/trial'
3
+
4
+ describe Split::Trial do
5
+ it "should be initializeable" do
6
+ experiment = mock('experiment')
7
+ alternative = mock('alternative')
8
+ trial = Split::Trial.new(:experiment => experiment, :alternative => alternative)
9
+ trial.experiment.should == experiment
10
+ trial.alternative.should == alternative
11
+ end
12
+
13
+ describe "alternative" do
14
+ it "should use the alternative if specified" do
15
+ trial = Split::Trial.new(:experiment => experiment = mock('experiment'), :alternative => alternative = mock('alternative'))
16
+ trial.should_not_receive(:choose)
17
+ trial.alternative.should == alternative
18
+ end
19
+
20
+ it "should populate alternative with a full alternative object after calling choose" do
21
+ experiment = Split::Experiment.new('basket_text', :alternative_names => ['basket', 'cart'])
22
+ experiment.save
23
+ trial = Split::Trial.new(:experiment => experiment)
24
+ trial.choose
25
+ trial.alternative.class.should == Split::Alternative
26
+ ['basket', 'cart'].should include(trial.alternative.name)
27
+ end
28
+
29
+ it "should populate an alternative when only one option is offerred" do
30
+ experiment = Split::Experiment.new('basket_text', :alternative_names => ['basket'])
31
+ experiment.save
32
+ trial = Split::Trial.new(:experiment => experiment)
33
+ trial.choose
34
+ trial.alternative.class.should == Split::Alternative
35
+ trial.alternative.name.should == 'basket'
36
+ end
37
+
38
+
39
+ it "should choose from the available alternatives" do
40
+ trial = Split::Trial.new(:experiment => experiment = mock('experiment'))
41
+ experiment.should_receive(:next_alternative).and_return(alternative = mock('alternative'))
42
+ alternative.should_receive(:increment_participation)
43
+ experiment.should_receive(:winner).and_return nil
44
+ trial.choose!
45
+
46
+ trial.alternative.should == alternative
47
+ end
48
+ end
49
+
50
+ describe "alternative_name" do
51
+ it "should load the alternative when the alternative name is set" do
52
+ experiment = Split::Experiment.new('basket_text', :alternative_names => ['basket', "cart"])
53
+ experiment.save
54
+
55
+ trial = Split::Trial.new(:experiment => experiment, :alternative_name => 'basket')
56
+ trial.alternative.name.should == 'basket'
57
+ end
58
+ end
59
+ end
@@ -15,16 +15,20 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.files = `git ls-files`.split("\n")
17
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
18
  s.require_paths = ["lib"]
20
19
 
21
20
  s.add_dependency 'redis', '>= 2.1'
22
21
  s.add_dependency 'redis-namespace', '>= 1.1.0'
23
22
  s.add_dependency 'sinatra', '>= 1.2.6'
23
+ s.add_dependency 'simple-random'
24
+
25
+ # Ruby 1.8 doesn't include JSON in the std lib
26
+ if RUBY_VERSION < "1.9"
27
+ s.add_dependency 'json', '>= 1.7.5'
28
+ end
24
29
 
25
30
  s.add_development_dependency 'rake'
26
31
  s.add_development_dependency 'bundler', '~> 1.0'
27
- s.add_development_dependency 'rspec', '~> 2.6'
32
+ s.add_development_dependency 'rspec', '~> 2.12'
28
33
  s.add_development_dependency 'rack-test', '~> 0.6'
29
- s.add_development_dependency 'guard-rspec', '~> 1.2'
30
34
  end
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.4.6
4
+ version: 0.5.0
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: 2012-10-28 00:00:00.000000000 Z
12
+ date: 2013-01-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
16
- requirement: &70093446916440 !ruby/object:Gem::Requirement
16
+ requirement: &70342380332380 !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: *70093446916440
24
+ version_requirements: *70342380332380
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: redis-namespace
27
- requirement: &70093446931800 !ruby/object:Gem::Requirement
27
+ requirement: &70342380331880 !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: *70093446931800
35
+ version_requirements: *70342380331880
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sinatra
38
- requirement: &70093446947900 !ruby/object:Gem::Requirement
38
+ requirement: &70342380331420 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,21 @@ dependencies:
43
43
  version: 1.2.6
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70093446947900
46
+ version_requirements: *70342380331420
47
+ - !ruby/object:Gem::Dependency
48
+ name: simple-random
49
+ requirement: &70342380331040 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70342380331040
47
58
  - !ruby/object:Gem::Dependency
48
59
  name: rake
49
- requirement: &70093446950180 !ruby/object:Gem::Requirement
60
+ requirement: &70342380330560 !ruby/object:Gem::Requirement
50
61
  none: false
51
62
  requirements:
52
63
  - - ! '>='
@@ -54,10 +65,10 @@ dependencies:
54
65
  version: '0'
55
66
  type: :development
56
67
  prerelease: false
57
- version_requirements: *70093446950180
68
+ version_requirements: *70342380330560
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: bundler
60
- requirement: &70093447002840 !ruby/object:Gem::Requirement
71
+ requirement: &70342380330060 !ruby/object:Gem::Requirement
61
72
  none: false
62
73
  requirements:
63
74
  - - ~>
@@ -65,21 +76,21 @@ dependencies:
65
76
  version: '1.0'
66
77
  type: :development
67
78
  prerelease: false
68
- version_requirements: *70093447002840
79
+ version_requirements: *70342380330060
69
80
  - !ruby/object:Gem::Dependency
70
81
  name: rspec
71
- requirement: &70093447013900 !ruby/object:Gem::Requirement
82
+ requirement: &70342380329560 !ruby/object:Gem::Requirement
72
83
  none: false
73
84
  requirements:
74
85
  - - ~>
75
86
  - !ruby/object:Gem::Version
76
- version: '2.6'
87
+ version: '2.12'
77
88
  type: :development
78
89
  prerelease: false
79
- version_requirements: *70093447013900
90
+ version_requirements: *70342380329560
80
91
  - !ruby/object:Gem::Dependency
81
92
  name: rack-test
82
- requirement: &70093447010320 !ruby/object:Gem::Requirement
93
+ requirement: &70342380329100 !ruby/object:Gem::Requirement
83
94
  none: false
84
95
  requirements:
85
96
  - - ~>
@@ -87,18 +98,7 @@ dependencies:
87
98
  version: '0.6'
88
99
  type: :development
89
100
  prerelease: false
90
- version_requirements: *70093447010320
91
- - !ruby/object:Gem::Dependency
92
- name: guard-rspec
93
- requirement: &70093447018620 !ruby/object:Gem::Requirement
94
- none: false
95
- requirements:
96
- - - ~>
97
- - !ruby/object:Gem::Version
98
- version: '1.2'
99
- type: :development
100
- prerelease: false
101
- version_requirements: *70093447018620
101
+ version_requirements: *70342380329100
102
102
  description:
103
103
  email:
104
104
  - andrewnez@gmail.com
@@ -109,12 +109,15 @@ files:
109
109
  - .gitignore
110
110
  - .travis.yml
111
111
  - CHANGELOG.mdown
112
+ - CONTRIBUTING.md
112
113
  - Gemfile
113
- - Guardfile
114
114
  - LICENSE
115
115
  - README.mdown
116
116
  - Rakefile
117
117
  - lib/split.rb
118
+ - lib/split/algorithms.rb
119
+ - lib/split/algorithms/weighted_sample.rb
120
+ - lib/split/algorithms/whiplash.rb
118
121
  - lib/split/alternative.rb
119
122
  - lib/split/configuration.rb
120
123
  - lib/split/dashboard.rb
@@ -126,16 +129,33 @@ files:
126
129
  - lib/split/dashboard/views/index.erb
127
130
  - lib/split/dashboard/views/layout.erb
128
131
  - lib/split/engine.rb
132
+ - lib/split/exceptions.rb
129
133
  - lib/split/experiment.rb
134
+ - lib/split/extensions.rb
135
+ - lib/split/extensions/array.rb
136
+ - lib/split/extensions/string.rb
130
137
  - lib/split/helper.rb
138
+ - lib/split/metric.rb
139
+ - lib/split/persistence.rb
140
+ - lib/split/persistence/cookie_adapter.rb
141
+ - lib/split/persistence/session_adapter.rb
142
+ - lib/split/trial.rb
131
143
  - lib/split/version.rb
144
+ - spec/algorithms/weighted_sample_spec.rb
145
+ - spec/algorithms/whiplash_spec.rb
132
146
  - spec/alternative_spec.rb
133
147
  - spec/configuration_spec.rb
134
148
  - spec/dashboard_helpers_spec.rb
135
149
  - spec/dashboard_spec.rb
136
150
  - spec/experiment_spec.rb
137
151
  - spec/helper_spec.rb
152
+ - spec/metric_spec.rb
153
+ - spec/persistence/cookie_adapter_spec.rb
154
+ - spec/persistence/session_adapter_spec.rb
155
+ - spec/persistence_spec.rb
138
156
  - spec/spec_helper.rb
157
+ - spec/support/cookies_mock.rb
158
+ - spec/trial_spec.rb
139
159
  - split.gemspec
140
160
  homepage: https://github.com/andrew/split
141
161
  licenses: []
@@ -162,10 +182,19 @@ signing_key:
162
182
  specification_version: 3
163
183
  summary: Rack based split testing framework
164
184
  test_files:
185
+ - spec/algorithms/weighted_sample_spec.rb
186
+ - spec/algorithms/whiplash_spec.rb
165
187
  - spec/alternative_spec.rb
166
188
  - spec/configuration_spec.rb
167
189
  - spec/dashboard_helpers_spec.rb
168
190
  - spec/dashboard_spec.rb
169
191
  - spec/experiment_spec.rb
170
192
  - spec/helper_spec.rb
193
+ - spec/metric_spec.rb
194
+ - spec/persistence/cookie_adapter_spec.rb
195
+ - spec/persistence/session_adapter_spec.rb
196
+ - spec/persistence_spec.rb
171
197
  - spec/spec_helper.rb
198
+ - spec/support/cookies_mock.rb
199
+ - spec/trial_spec.rb
200
+ has_rdoc:
data/Guardfile DELETED
@@ -1,5 +0,0 @@
1
- guard 'rspec', :version => 2 do
2
- watch(%r{^spec/.+_spec\.rb$})
3
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
- watch('spec/spec_helper.rb') { "spec" }
5
- end