split 0.6.4 → 0.6.5
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.
- data/CHANGELOG.mdown +10 -0
- data/README.mdown +31 -4
- data/lib/split.rb +11 -1
- data/lib/split/persistence.rb +2 -2
- data/lib/split/persistence/redis_adapter.rb +52 -0
- data/lib/split/version.rb +1 -1
- data/spec/persistence/redis_adapter_spec.rb +81 -0
- data/split.gemspec +1 -0
- metadata +7 -3
data/CHANGELOG.mdown
CHANGED
data/README.mdown
CHANGED
@@ -191,11 +191,11 @@ end
|
|
191
191
|
|
192
192
|
### Experiment Persistence
|
193
193
|
|
194
|
-
Split comes with
|
194
|
+
Split comes with three built-in persistence adapters for storing users and the alternatives they've been given for each experiment.
|
195
195
|
|
196
196
|
By default Split will store the tests for each user in the session.
|
197
197
|
|
198
|
-
You can optionally configure Split to use a cookie or any custom adapter of your choosing.
|
198
|
+
You can optionally configure Split to use a cookie, Redis, or any custom adapter of your choosing.
|
199
199
|
|
200
200
|
#### Cookies
|
201
201
|
|
@@ -207,6 +207,22 @@ end
|
|
207
207
|
|
208
208
|
__Note:__ Using cookies depends on `ActionDispatch::Cookies` or any identical API
|
209
209
|
|
210
|
+
#### Redis
|
211
|
+
|
212
|
+
Using Redis will allow ab_users to persist across sessions or machines.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
Split.configure do |config|
|
216
|
+
config.persistence = Split::Persistence::RedisAdapter.with_config(:lookup_by => proc { |context| context.current_user_id }
|
217
|
+
# Equivalent
|
218
|
+
# config.persistence = Split::Persistence::RedisAdapter.with_config(:lookup_by => :current_user_id }
|
219
|
+
end
|
220
|
+
```
|
221
|
+
|
222
|
+
Options:
|
223
|
+
* `lookup_by`: method to invoke per request for uniquely identifying ab_users (mandatory configuration)
|
224
|
+
* `namespace`: separate namespace to store these persisted values (default "persistence")
|
225
|
+
|
210
226
|
#### Custom Adapter
|
211
227
|
|
212
228
|
Your custom adapter needs to implement the same API as existing adapters.
|
@@ -554,12 +570,23 @@ end
|
|
554
570
|
|
555
571
|
## Algorithms
|
556
572
|
|
557
|
-
By default, Split ships with
|
573
|
+
By default, Split ships with `Split::Algorithms::WeightedSample` that randomly selects from possible alternatives for a traditional a/b test.
|
574
|
+
It is possible to specify static weights to favor certain alternatives.
|
558
575
|
|
559
|
-
|
576
|
+
`Split::Algorithms::Whiplash` is an implementation of a [multi-armed bandit algorithm](http://stevehanov.ca/blog/index.php?id=132).
|
577
|
+
This algorithm will automatically weight the alternatives based on their relative performance,
|
578
|
+
choosing the better-performing ones more often as trials are completed.
|
560
579
|
|
561
580
|
Users may also write their own algorithms. The default algorithm may be specified globally in the configuration file, or on a per experiment basis using the experiments hash of the configuration file.
|
562
581
|
|
582
|
+
To change the algorithm globally for all experiments, use the following in your initializer:
|
583
|
+
|
584
|
+
```ruby
|
585
|
+
Split.configure do |config|
|
586
|
+
config.algorithm = Split::Algorithms::Whiplash
|
587
|
+
end
|
588
|
+
```
|
589
|
+
|
563
590
|
## Extensions
|
564
591
|
|
565
592
|
- [Split::Export](http://github.com/andrew/split-export) - easily export ab test data out of Split
|
data/lib/split.rb
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
%w[algorithms
|
1
|
+
%w[algorithms
|
2
|
+
alternative
|
3
|
+
configuration
|
4
|
+
exceptions
|
5
|
+
experiment
|
6
|
+
extensions
|
7
|
+
helper
|
8
|
+
metric
|
9
|
+
persistence
|
10
|
+
trial
|
11
|
+
version].each do |f|
|
2
12
|
require "split/#{f}"
|
3
13
|
end
|
4
14
|
|
data/lib/split/persistence.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Split
|
2
|
+
module Persistence
|
3
|
+
class RedisAdapter
|
4
|
+
DEFAULT_CONFIG = {:namespace => 'persistence'}.freeze
|
5
|
+
|
6
|
+
attr_reader :redis_key
|
7
|
+
|
8
|
+
def initialize(context)
|
9
|
+
if lookup_by = self.class.config[:lookup_by]
|
10
|
+
if lookup_by.respond_to?(:call)
|
11
|
+
key_frag = lookup_by.call(context)
|
12
|
+
else
|
13
|
+
key_frag = context.send(lookup_by)
|
14
|
+
end
|
15
|
+
@redis_key = "#{self.class.config[:namespace]}:#{key_frag}"
|
16
|
+
else
|
17
|
+
raise "Please configure lookup_by"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](field)
|
22
|
+
Split.redis.hget(redis_key, field)
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(field, value)
|
26
|
+
Split.redis.hset(redis_key, field, value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(field)
|
30
|
+
Split.redis.hdel(redis_key, field)
|
31
|
+
end
|
32
|
+
|
33
|
+
def keys
|
34
|
+
Split.redis.hkeys(redis_key)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.with_config(options={})
|
38
|
+
self.config.merge!(options)
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.config
|
43
|
+
@config ||= DEFAULT_CONFIG.dup
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.reset_config!
|
47
|
+
@config = DEFAULT_CONFIG.dup
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/split/version.rb
CHANGED
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Split::Persistence::RedisAdapter do
|
4
|
+
|
5
|
+
let(:context) { double(:lookup => 'blah') }
|
6
|
+
|
7
|
+
subject { Split::Persistence::RedisAdapter.new(context) }
|
8
|
+
|
9
|
+
describe '#redis_key' do
|
10
|
+
before { Split::Persistence::RedisAdapter.reset_config! }
|
11
|
+
|
12
|
+
context 'default' do
|
13
|
+
it 'should raise error with prompt to set lookup_by' do
|
14
|
+
expect{Split::Persistence::RedisAdapter.new(context)
|
15
|
+
}.to raise_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'config with lookup_by = proc { "block" }' do
|
20
|
+
before { Split::Persistence::RedisAdapter.with_config(:lookup_by => proc{'block'}) }
|
21
|
+
|
22
|
+
it 'should be "persistence:block"' do
|
23
|
+
subject.redis_key.should == 'persistence:block'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'config with lookup_by = proc { |context| context.test }' do
|
28
|
+
before { Split::Persistence::RedisAdapter.with_config(:lookup_by => proc{'block'}) }
|
29
|
+
let(:context) { double(:test => 'block') }
|
30
|
+
|
31
|
+
it 'should be "persistence:block"' do
|
32
|
+
subject.redis_key.should == 'persistence:block'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'config with lookup_by = "method_name"' do
|
37
|
+
before { Split::Persistence::RedisAdapter.with_config(:lookup_by => 'method_name') }
|
38
|
+
let(:context) { double(:method_name => 'val') }
|
39
|
+
|
40
|
+
it 'should be "persistence:bar"' do
|
41
|
+
subject.redis_key.should == 'persistence:val'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'config with namespace and lookup_by' do
|
46
|
+
before { Split::Persistence::RedisAdapter.with_config(:lookup_by => proc{'frag'}, :namespace => 'namer') }
|
47
|
+
|
48
|
+
it 'should be "namer"' do
|
49
|
+
subject.redis_key.should == 'namer:frag'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'functional tests' do
|
55
|
+
before { Split::Persistence::RedisAdapter.with_config(:lookup_by => 'lookup') }
|
56
|
+
|
57
|
+
describe "#[] and #[]=" do
|
58
|
+
it "should set and return the value for given key" do
|
59
|
+
subject["my_key"] = "my_value"
|
60
|
+
subject["my_key"].should eq("my_value")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#delete" do
|
65
|
+
it "should delete the given key" do
|
66
|
+
subject["my_key"] = "my_value"
|
67
|
+
subject.delete("my_key")
|
68
|
+
subject["my_key"].should be_nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#keys" do
|
73
|
+
it "should return an array of the user's stored keys" do
|
74
|
+
subject["my_key"] = "my_value"
|
75
|
+
subject["my_second_key"] = "my_second_value"
|
76
|
+
subject.keys.should =~ ["my_key", "my_second_key"]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/split.gemspec
CHANGED
@@ -7,6 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Split::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Andrew Nesbitt"]
|
10
|
+
s.licenses = ['MIT']
|
10
11
|
s.email = ["andrewnez@gmail.com"]
|
11
12
|
s.homepage = "https://github.com/andrew/split"
|
12
13
|
s.summary = %q{Rack based split testing framework}
|
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.
|
4
|
+
version: 0.6.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-08-
|
12
|
+
date: 2013-08-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- lib/split/metric.rb
|
205
205
|
- lib/split/persistence.rb
|
206
206
|
- lib/split/persistence/cookie_adapter.rb
|
207
|
+
- lib/split/persistence/redis_adapter.rb
|
207
208
|
- lib/split/persistence/session_adapter.rb
|
208
209
|
- lib/split/trial.rb
|
209
210
|
- lib/split/version.rb
|
@@ -217,6 +218,7 @@ files:
|
|
217
218
|
- spec/helper_spec.rb
|
218
219
|
- spec/metric_spec.rb
|
219
220
|
- spec/persistence/cookie_adapter_spec.rb
|
221
|
+
- spec/persistence/redis_adapter_spec.rb
|
220
222
|
- spec/persistence/session_adapter_spec.rb
|
221
223
|
- spec/persistence_spec.rb
|
222
224
|
- spec/spec_helper.rb
|
@@ -224,7 +226,8 @@ files:
|
|
224
226
|
- spec/trial_spec.rb
|
225
227
|
- split.gemspec
|
226
228
|
homepage: https://github.com/andrew/split
|
227
|
-
licenses:
|
229
|
+
licenses:
|
230
|
+
- MIT
|
228
231
|
post_install_message:
|
229
232
|
rdoc_options: []
|
230
233
|
require_paths:
|
@@ -258,6 +261,7 @@ test_files:
|
|
258
261
|
- spec/helper_spec.rb
|
259
262
|
- spec/metric_spec.rb
|
260
263
|
- spec/persistence/cookie_adapter_spec.rb
|
264
|
+
- spec/persistence/redis_adapter_spec.rb
|
261
265
|
- spec/persistence/session_adapter_spec.rb
|
262
266
|
- spec/persistence_spec.rb
|
263
267
|
- spec/spec_helper.rb
|