split 0.4.6 → 0.5.0

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 (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