ab_panel 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +33 -3
- data/ab_panel.gemspec +2 -2
- data/lib/ab_panel.rb +7 -1
- data/lib/ab_panel/config.rb +5 -1
- data/lib/ab_panel/version.rb +1 -1
- data/lib/array.rb +25 -0
- data/spec/ab_panel/config_spec.rb +19 -0
- data/spec/ab_panel_spec.rb +16 -1
- data/spec/array_spec.rb +50 -0
- data/spec/support/files/config/ab_panel.yml +7 -5
- metadata +28 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17de85b0bf79398d4c004cb2a7de53b13347843e
|
4
|
+
data.tar.gz: ec7bbc8ade945df459db9f83b754c2cefab9f9c9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 556d32d1a6b2965db653703aea0a2e02914fc526f80e3a392ceb3bef66374912961b6ab82f7275903e4272fe2aab774d38e123c4b5dbfdc68370fdd69961f474
|
7
|
+
data.tar.gz: 9abeeb2287f2ec6626068d4ec89dc185d21cc8bf1d83c66ef29d7ec067439836a3b3c2e6226928fe8746a44369b348a9418cc9d9f9d43cc5b24f9910a5ce3c0a
|
data/README.md
CHANGED
@@ -18,6 +18,35 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
$ gem install ab_panel
|
20
20
|
|
21
|
+
## Upgrading from 0.2.0 to 0.3.0
|
22
|
+
|
23
|
+
In this new version we've added weights to different conditions/scenarios. This
|
24
|
+
is so that you can rollout certain features slowly. We've also removed the
|
25
|
+
original (control scenario) that is added standard.
|
26
|
+
|
27
|
+
The only thing you need to do to upgrade is update the ``ab_panel.yml``.
|
28
|
+
|
29
|
+
Old:
|
30
|
+
|
31
|
+
```yaml
|
32
|
+
|
33
|
+
foo:
|
34
|
+
- bar1
|
35
|
+
- bar2
|
36
|
+
|
37
|
+
```
|
38
|
+
|
39
|
+
New (if you want to keep original or need original):
|
40
|
+
|
41
|
+
```yaml
|
42
|
+
|
43
|
+
foo:
|
44
|
+
bar1: 2
|
45
|
+
bar2: 2
|
46
|
+
original: 2
|
47
|
+
|
48
|
+
```
|
49
|
+
|
21
50
|
## Usage
|
22
51
|
|
23
52
|
Create a config file with one or more experiments and conditions.
|
@@ -26,13 +55,14 @@ In `config/ab_panel.yml`
|
|
26
55
|
|
27
56
|
```yaml
|
28
57
|
my_experiment:
|
29
|
-
|
30
|
-
|
58
|
+
original: 1
|
59
|
+
condition_b: 1
|
60
|
+
condition_c: 1
|
31
61
|
```
|
32
62
|
|
33
63
|
Note that this will create 3 conditions:
|
34
64
|
|
35
|
-
1. Original condition
|
65
|
+
1. Original condition
|
36
66
|
2. Condition B
|
37
67
|
3. Condition C
|
38
68
|
|
data/ab_panel.gemspec
CHANGED
@@ -6,8 +6,8 @@ require 'ab_panel/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "ab_panel"
|
8
8
|
spec.version = AbPanel::VERSION
|
9
|
-
spec.authors = ["Wouter de Vos", "Mark Mulder"]
|
10
|
-
spec.email = ["wouter@springest.com", "markmulder@gmail.com"]
|
9
|
+
spec.authors = ["Wouter de Vos", "Mark Mulder", "Peter de Ruijter"]
|
10
|
+
spec.email = ["wouter@springest.com", "markmulder@gmail.com", "hello@thisiswho.im"]
|
11
11
|
spec.description = %q{Run A/B test experiments on your Rails 3+ site using Mixpanel as a backend.}
|
12
12
|
spec.summary = %q{Run A/B test experiments on your Rails 3+ site using Mixpanel as a backend.}
|
13
13
|
spec.homepage = "https://github.com/Springest/ab_panel"
|
data/lib/ab_panel.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'set'
|
2
|
+
require_relative './array'
|
3
|
+
|
2
4
|
Dir[File.expand_path(File.join(
|
3
5
|
File.dirname(__FILE__),'ab_panel','**','*.rb'))]
|
4
6
|
.each {|f| require f}
|
@@ -37,6 +39,10 @@ module AbPanel
|
|
37
39
|
config.scenarios experiment
|
38
40
|
end
|
39
41
|
|
42
|
+
def weights(experiment)
|
43
|
+
config.weights experiment
|
44
|
+
end
|
45
|
+
|
40
46
|
def properties
|
41
47
|
@env[:properties]
|
42
48
|
end
|
@@ -83,7 +89,7 @@ module AbPanel
|
|
83
89
|
selected = begin
|
84
90
|
already_assigned.send(experiment).condition
|
85
91
|
rescue
|
86
|
-
scenarios(experiment)
|
92
|
+
scenarios(experiment).weighted_sample(weights(experiment))
|
87
93
|
end
|
88
94
|
|
89
95
|
cs[experiment]["#{selected}?"] = true
|
data/lib/ab_panel/config.rb
CHANGED
@@ -12,9 +12,13 @@ module AbPanel
|
|
12
12
|
|
13
13
|
def scenarios(experiment)
|
14
14
|
raise ArgumentError.new( "Fatal: Experiment config not found for #{experiment}" ) unless experiments.include? experiment.to_sym
|
15
|
-
( settings[experiment.to_sym].map(&:to_sym)
|
15
|
+
( settings[experiment.to_sym].keys.map(&:to_sym)).uniq
|
16
16
|
end
|
17
17
|
|
18
|
+
def weights(experiment)
|
19
|
+
raise ArgumentError.new( "Fatal: Experiment config not found for #{experiment}" ) unless experiments.include? experiment.to_sym
|
20
|
+
settings[experiment.to_sym].map { |s| s[1] }
|
21
|
+
end
|
18
22
|
|
19
23
|
def settings
|
20
24
|
@settings ||= YAML.load(
|
data/lib/ab_panel/version.rb
CHANGED
data/lib/array.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class Array
|
2
|
+
def weighted_sample(weights=nil)
|
3
|
+
weights = Array.new(length, 1.0) if weights.nil? || weights.sum == 0
|
4
|
+
total = weights.sum
|
5
|
+
|
6
|
+
# The total sum of weights is multiplied by a random number
|
7
|
+
trigger = Kernel::rand * total
|
8
|
+
|
9
|
+
subtotal = 0
|
10
|
+
result = nil
|
11
|
+
|
12
|
+
# The subtotal is checked agains the trigger. The higher the sum, the higher
|
13
|
+
# the probability of triggering a result.
|
14
|
+
weights.each_with_index do |weight, index|
|
15
|
+
subtotal += weight
|
16
|
+
|
17
|
+
if subtotal > trigger
|
18
|
+
result = self[index]
|
19
|
+
break
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# Returns self.last from current array if result is nil
|
23
|
+
result || last
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AbPanel::Config do
|
4
|
+
let(:config) { AbPanel::Config.new }
|
5
|
+
before do
|
6
|
+
AbPanel::Config.any_instance.stub(:settings) { { exp1: { scenario1: 25, scenario2: 75 } } }
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '.experiments' do
|
10
|
+
subject { config.experiments }
|
11
|
+
it { should =~ [:exp1] }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '.weights' do
|
15
|
+
subject { config.weights('exp1') }
|
16
|
+
|
17
|
+
it { should =~ [75.0, 25.0] }
|
18
|
+
end
|
19
|
+
end
|
data/spec/ab_panel_spec.rb
CHANGED
@@ -7,6 +7,21 @@ describe AbPanel do
|
|
7
7
|
it { should =~ %w(experiment1 experiment2).map(&:to_sym) }
|
8
8
|
end
|
9
9
|
|
10
|
+
describe ".weights" do
|
11
|
+
let(:experiment) { AbPanel.experiments.first }
|
12
|
+
subject { AbPanel.weights(experiment) }
|
13
|
+
|
14
|
+
it { should == [25, 25, 25, 25] }
|
15
|
+
|
16
|
+
describe "With a nonexistent experiment" do
|
17
|
+
let(:experiment) { :does_not_exist }
|
18
|
+
|
19
|
+
it 'should throw an ArgumentError' do
|
20
|
+
expect { subject }.to raise_exception ArgumentError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
10
25
|
describe ".scenarios" do
|
11
26
|
subject { AbPanel.scenarios(experiment) }
|
12
27
|
|
@@ -14,7 +29,7 @@ describe AbPanel do
|
|
14
29
|
|
15
30
|
it { should =~ %w( scenario1 scenario2 scenario3 original ).map(&:to_sym) }
|
16
31
|
|
17
|
-
describe "With an
|
32
|
+
describe "With an nonexistent experiment" do
|
18
33
|
let(:experiment) { :does_not_exist }
|
19
34
|
|
20
35
|
it 'should throw an ArgumentError' do
|
data/spec/array_spec.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Array do
|
4
|
+
describe '.weighted_sample' do
|
5
|
+
before do
|
6
|
+
Kernel.stub(:rand) { 0.5 }
|
7
|
+
end
|
8
|
+
|
9
|
+
context "Stub test" do
|
10
|
+
subject { Kernel.rand }
|
11
|
+
it { should eq 0.5 }
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:array) { [1, 2, 3, 4] }
|
15
|
+
subject { array.weighted_sample }
|
16
|
+
|
17
|
+
it { should eq 3 }
|
18
|
+
|
19
|
+
context "different random" do
|
20
|
+
before do
|
21
|
+
Kernel.stub(:rand) { 0 }
|
22
|
+
end
|
23
|
+
|
24
|
+
it { should eq 1 }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "different random" do
|
28
|
+
before do
|
29
|
+
Kernel.stub(:rand) { 1 }
|
30
|
+
end
|
31
|
+
|
32
|
+
it { should eq 4 }
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with weights" do
|
36
|
+
subject { array.weighted_sample([1, 0, 0, 0]) }
|
37
|
+
it { should eq 1 }
|
38
|
+
end
|
39
|
+
|
40
|
+
context "all the same weights" do
|
41
|
+
before { Kernel.stub(:rand) { 1 } }
|
42
|
+
subject { array.weighted_sample([0, 0, 0, 0]) }
|
43
|
+
it { should eq 4 }
|
44
|
+
context "random 0" do
|
45
|
+
before { Kernel.stub(:rand) { 0 } }
|
46
|
+
it { should eq 1 }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
CHANGED
@@ -1,124 +1,126 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ab_panel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter de Vos
|
8
8
|
- Mark Mulder
|
9
|
+
- Peter de Ruijter
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date:
|
13
|
+
date: 2014-03-18 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: bundler
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
18
|
requirements:
|
18
|
-
- - ~>
|
19
|
+
- - "~>"
|
19
20
|
- !ruby/object:Gem::Version
|
20
21
|
version: '1.3'
|
21
22
|
type: :development
|
22
23
|
prerelease: false
|
23
24
|
version_requirements: !ruby/object:Gem::Requirement
|
24
25
|
requirements:
|
25
|
-
- - ~>
|
26
|
+
- - "~>"
|
26
27
|
- !ruby/object:Gem::Version
|
27
28
|
version: '1.3'
|
28
29
|
- !ruby/object:Gem::Dependency
|
29
30
|
name: rails
|
30
31
|
requirement: !ruby/object:Gem::Requirement
|
31
32
|
requirements:
|
32
|
-
- - ~>
|
33
|
+
- - "~>"
|
33
34
|
- !ruby/object:Gem::Version
|
34
35
|
version: '3.2'
|
35
36
|
type: :development
|
36
37
|
prerelease: false
|
37
38
|
version_requirements: !ruby/object:Gem::Requirement
|
38
39
|
requirements:
|
39
|
-
- - ~>
|
40
|
+
- - "~>"
|
40
41
|
- !ruby/object:Gem::Version
|
41
42
|
version: '3.2'
|
42
43
|
- !ruby/object:Gem::Dependency
|
43
44
|
name: rake
|
44
45
|
requirement: !ruby/object:Gem::Requirement
|
45
46
|
requirements:
|
46
|
-
- -
|
47
|
+
- - ">="
|
47
48
|
- !ruby/object:Gem::Version
|
48
49
|
version: '0'
|
49
50
|
type: :development
|
50
51
|
prerelease: false
|
51
52
|
version_requirements: !ruby/object:Gem::Requirement
|
52
53
|
requirements:
|
53
|
-
- -
|
54
|
+
- - ">="
|
54
55
|
- !ruby/object:Gem::Version
|
55
56
|
version: '0'
|
56
57
|
- !ruby/object:Gem::Dependency
|
57
58
|
name: fakeweb
|
58
59
|
requirement: !ruby/object:Gem::Requirement
|
59
60
|
requirements:
|
60
|
-
- -
|
61
|
+
- - ">="
|
61
62
|
- !ruby/object:Gem::Version
|
62
63
|
version: '0'
|
63
64
|
type: :development
|
64
65
|
prerelease: false
|
65
66
|
version_requirements: !ruby/object:Gem::Requirement
|
66
67
|
requirements:
|
67
|
-
- -
|
68
|
+
- - ">="
|
68
69
|
- !ruby/object:Gem::Version
|
69
70
|
version: '0'
|
70
71
|
- !ruby/object:Gem::Dependency
|
71
72
|
name: rspec
|
72
73
|
requirement: !ruby/object:Gem::Requirement
|
73
74
|
requirements:
|
74
|
-
- -
|
75
|
+
- - ">="
|
75
76
|
- !ruby/object:Gem::Version
|
76
77
|
version: '0'
|
77
78
|
type: :development
|
78
79
|
prerelease: false
|
79
80
|
version_requirements: !ruby/object:Gem::Requirement
|
80
81
|
requirements:
|
81
|
-
- -
|
82
|
+
- - ">="
|
82
83
|
- !ruby/object:Gem::Version
|
83
84
|
version: '0'
|
84
85
|
- !ruby/object:Gem::Dependency
|
85
86
|
name: debugger
|
86
87
|
requirement: !ruby/object:Gem::Requirement
|
87
88
|
requirements:
|
88
|
-
- -
|
89
|
+
- - ">="
|
89
90
|
- !ruby/object:Gem::Version
|
90
91
|
version: '0'
|
91
92
|
type: :development
|
92
93
|
prerelease: false
|
93
94
|
version_requirements: !ruby/object:Gem::Requirement
|
94
95
|
requirements:
|
95
|
-
- -
|
96
|
+
- - ">="
|
96
97
|
- !ruby/object:Gem::Version
|
97
98
|
version: '0'
|
98
99
|
- !ruby/object:Gem::Dependency
|
99
100
|
name: mixpanel
|
100
101
|
requirement: !ruby/object:Gem::Requirement
|
101
102
|
requirements:
|
102
|
-
- -
|
103
|
+
- - ">="
|
103
104
|
- !ruby/object:Gem::Version
|
104
105
|
version: '0'
|
105
106
|
type: :runtime
|
106
107
|
prerelease: false
|
107
108
|
version_requirements: !ruby/object:Gem::Requirement
|
108
109
|
requirements:
|
109
|
-
- -
|
110
|
+
- - ">="
|
110
111
|
- !ruby/object:Gem::Version
|
111
112
|
version: '0'
|
112
113
|
description: Run A/B test experiments on your Rails 3+ site using Mixpanel as a backend.
|
113
114
|
email:
|
114
115
|
- wouter@springest.com
|
115
116
|
- markmulder@gmail.com
|
117
|
+
- hello@thisiswho.im
|
116
118
|
executables: []
|
117
119
|
extensions: []
|
118
120
|
extra_rdoc_files: []
|
119
121
|
files:
|
120
|
-
- .gitignore
|
121
|
-
- .travis.yml
|
122
|
+
- ".gitignore"
|
123
|
+
- ".travis.yml"
|
122
124
|
- Gemfile
|
123
125
|
- LICENSE.txt
|
124
126
|
- README.md
|
@@ -196,9 +198,12 @@ files:
|
|
196
198
|
- lib/ab_panel/javascript.rb
|
197
199
|
- lib/ab_panel/mixpanel.rb
|
198
200
|
- lib/ab_panel/version.rb
|
201
|
+
- lib/array.rb
|
202
|
+
- spec/ab_panel/config_spec.rb
|
199
203
|
- spec/ab_panel/controller_additions_spec.rb
|
200
204
|
- spec/ab_panel/javascript_spec.rb
|
201
205
|
- spec/ab_panel_spec.rb
|
206
|
+
- spec/array_spec.rb
|
202
207
|
- spec/spec_helper.rb
|
203
208
|
- spec/support/fakeweb.rb
|
204
209
|
- spec/support/files/config/ab_panel.yml
|
@@ -213,17 +218,17 @@ require_paths:
|
|
213
218
|
- lib
|
214
219
|
required_ruby_version: !ruby/object:Gem::Requirement
|
215
220
|
requirements:
|
216
|
-
- -
|
221
|
+
- - ">="
|
217
222
|
- !ruby/object:Gem::Version
|
218
223
|
version: '0'
|
219
224
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
220
225
|
requirements:
|
221
|
-
- -
|
226
|
+
- - ">="
|
222
227
|
- !ruby/object:Gem::Version
|
223
228
|
version: '0'
|
224
229
|
requirements: []
|
225
230
|
rubyforge_project:
|
226
|
-
rubygems_version: 2.0
|
231
|
+
rubygems_version: 2.2.0
|
227
232
|
signing_key:
|
228
233
|
specification_version: 4
|
229
234
|
summary: Run A/B test experiments on your Rails 3+ site using Mixpanel as a backend.
|
@@ -294,9 +299,11 @@ test_files:
|
|
294
299
|
- example/vendor/assets/javascripts/.gitkeep
|
295
300
|
- example/vendor/assets/stylesheets/.gitkeep
|
296
301
|
- example/vendor/plugins/.gitkeep
|
302
|
+
- spec/ab_panel/config_spec.rb
|
297
303
|
- spec/ab_panel/controller_additions_spec.rb
|
298
304
|
- spec/ab_panel/javascript_spec.rb
|
299
305
|
- spec/ab_panel_spec.rb
|
306
|
+
- spec/array_spec.rb
|
300
307
|
- spec/spec_helper.rb
|
301
308
|
- spec/support/fakeweb.rb
|
302
309
|
- spec/support/files/config/ab_panel.yml
|