vanity 2.1.2 → 2.2.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/.travis.yml +0 -1
- data/CHANGELOG +4 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +6 -9
- data/README.md +1 -5
- data/gemfiles/rails32.gemfile +1 -2
- data/gemfiles/rails32.gemfile.lock +5 -9
- data/gemfiles/rails41.gemfile +1 -2
- data/gemfiles/rails41.gemfile.lock +5 -11
- data/gemfiles/rails42.gemfile +1 -2
- data/gemfiles/rails42.gemfile.lock +5 -11
- data/gemfiles/rails42_protected_attributes.gemfile +1 -2
- data/gemfiles/rails42_protected_attributes.gemfile.lock +5 -8
- data/lib/vanity/adapters/abstract_adapter.rb +1 -1
- data/lib/vanity/adapters/mock_adapter.rb +1 -1
- data/lib/vanity/adapters/mongodb_adapter.rb +118 -49
- data/lib/vanity/adapters/redis_adapter.rb +1 -1
- data/lib/vanity/configuration.rb +3 -3
- data/lib/vanity/experiment/ab_test.rb +24 -22
- data/lib/vanity/experiment/base.rb +3 -3
- data/lib/vanity/metric/active_record.rb +1 -1
- data/lib/vanity/version.rb +1 -1
- data/test/adapters/redis_adapter_test.rb +1 -1
- data/test/experiment/ab_test.rb +38 -38
- data/test/experiment/base_test.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a239324275cb444df1667c84c26134d4cea832af
|
|
4
|
+
data.tar.gz: 3640a24cc2166d34f1211f629a6d5334bbcc1021
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b12cf807e8c67be3090a3d96bed156c8237dd8c127c3498a93c5ffc46ac9ebb70435cbf87a7b82f61f293b013fb10f5c4f0dd773e3fa4e9fb81364f90cbae817
|
|
7
|
+
data.tar.gz: 820e507831da2346e52f9589b1494cd30d6a83c3f508b6f5b67631e3f48ad5313e050ff04d5b09823116103ccc040d35e327a5b6412e4b975927d77ddb1d07e1
|
data/.travis.yml
CHANGED
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
== 2.2.0 (2016-3-26)
|
|
2
|
+
* Update mongo integration for mongo ruby driver 2.x. (@phillbaker)
|
|
3
|
+
* Centralize warnings to use `Vanity.logger` (@phillbaker)
|
|
4
|
+
|
|
1
5
|
== 2.1.2 (2016-2-20)
|
|
2
6
|
|
|
3
7
|
* Fix infinite loop on loading Vanity when using Redis and Redis fails to connect. #292 (@phillbaker)
|
data/Gemfile
CHANGED
|
@@ -7,7 +7,7 @@ gem "rack"
|
|
|
7
7
|
# Persistence
|
|
8
8
|
gem "redis", ">= 2.1"
|
|
9
9
|
gem "redis-namespace", ">= 1.1.0"
|
|
10
|
-
gem "mongo"
|
|
10
|
+
gem "mongo", "~> 2.1"
|
|
11
11
|
|
|
12
12
|
# Math libraries
|
|
13
13
|
gem "integration", "<= 0.1.0"
|
|
@@ -22,7 +22,7 @@ gem "webmock", :require=>false
|
|
|
22
22
|
gem "fakefs", :require => "fakefs/safe"
|
|
23
23
|
|
|
24
24
|
platform :ruby do
|
|
25
|
-
gem "bson_ext"
|
|
25
|
+
# gem "bson_ext"
|
|
26
26
|
gem "sqlite3", "~> 1.3.10"
|
|
27
27
|
end
|
|
28
28
|
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
vanity (2.
|
|
4
|
+
vanity (2.2.0)
|
|
5
5
|
i18n
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -20,10 +20,8 @@ GEM
|
|
|
20
20
|
rake
|
|
21
21
|
thor (>= 0.14.0)
|
|
22
22
|
blankslate (2.1.2.4)
|
|
23
|
-
bson (
|
|
24
|
-
bson (
|
|
25
|
-
bson_ext (1.6.0)
|
|
26
|
-
bson (= 1.6.0)
|
|
23
|
+
bson (4.0.2)
|
|
24
|
+
bson (4.0.2-java)
|
|
27
25
|
celluloid (0.16.0)
|
|
28
26
|
timers (~> 4.0.0)
|
|
29
27
|
classifier-reborn (2.0.3)
|
|
@@ -76,8 +74,8 @@ GEM
|
|
|
76
74
|
rb-inotify (>= 0.9)
|
|
77
75
|
mercenary (0.3.5)
|
|
78
76
|
minitest (4.7.5)
|
|
79
|
-
mongo (
|
|
80
|
-
bson (
|
|
77
|
+
mongo (2.2.3)
|
|
78
|
+
bson (~> 4.0)
|
|
81
79
|
parslet (1.5.0)
|
|
82
80
|
blankslate (~> 2.0)
|
|
83
81
|
posix-spawn (0.3.11)
|
|
@@ -117,7 +115,6 @@ DEPENDENCIES
|
|
|
117
115
|
RedCloth
|
|
118
116
|
activerecord-jdbc-adapter
|
|
119
117
|
appraisal (~> 1.0.2)
|
|
120
|
-
bson_ext
|
|
121
118
|
bundler (>= 1.8.0)
|
|
122
119
|
fakefs
|
|
123
120
|
garb (< 0.9.2)
|
|
@@ -125,7 +122,7 @@ DEPENDENCIES
|
|
|
125
122
|
jdbc-sqlite3
|
|
126
123
|
jekyll
|
|
127
124
|
minitest (>= 4.2)
|
|
128
|
-
mongo
|
|
125
|
+
mongo (~> 2.1)
|
|
129
126
|
rack
|
|
130
127
|
rake
|
|
131
128
|
redis (>= 2.1)
|
data/README.md
CHANGED
|
@@ -65,8 +65,7 @@ test:
|
|
|
65
65
|
Add to your Gemfile:
|
|
66
66
|
|
|
67
67
|
```ruby
|
|
68
|
-
gem "
|
|
69
|
-
gem "mongo"
|
|
68
|
+
gem "mongo", "~> 2.0" # For Mongo 1.x support see Vanity versions 2.1 and below.
|
|
70
69
|
```
|
|
71
70
|
|
|
72
71
|
A sample `config/vanity.yml` might look like:
|
|
@@ -249,9 +248,6 @@ your view no participants will be recorded.
|
|
|
249
248
|
|
|
250
249
|
Here's what's tested and known to work:
|
|
251
250
|
|
|
252
|
-
Ruby 2.0
|
|
253
|
-
Persistence: Redis, Mongo, ActiveRecord
|
|
254
|
-
Rails: 3.2, 4.1, 4.2
|
|
255
251
|
Ruby 2.1
|
|
256
252
|
Persistence: Redis, Mongo, ActiveRecord
|
|
257
253
|
Rails: 3.2, 4.1, 4.2
|
data/gemfiles/rails32.gemfile
CHANGED
|
@@ -5,7 +5,7 @@ source "https://rubygems.org"
|
|
|
5
5
|
gem "rack"
|
|
6
6
|
gem "redis", ">= 2.1"
|
|
7
7
|
gem "redis-namespace", ">= 1.1.0"
|
|
8
|
-
gem "mongo"
|
|
8
|
+
gem "mongo", "~> 2.1"
|
|
9
9
|
gem "integration", "<= 0.1.0"
|
|
10
10
|
gem "rubystats", ">= 0.2.4"
|
|
11
11
|
gem "garb", "< 0.9.2", :require => false
|
|
@@ -29,7 +29,6 @@ group :development do
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
platforms :ruby do
|
|
32
|
-
gem "bson_ext"
|
|
33
32
|
gem "sqlite3", "~> 1.3.10"
|
|
34
33
|
end
|
|
35
34
|
|
|
@@ -7,7 +7,7 @@ GIT
|
|
|
7
7
|
PATH
|
|
8
8
|
remote: ..
|
|
9
9
|
specs:
|
|
10
|
-
vanity (2.
|
|
10
|
+
vanity (2.2.0)
|
|
11
11
|
i18n
|
|
12
12
|
|
|
13
13
|
GEM
|
|
@@ -51,10 +51,7 @@ GEM
|
|
|
51
51
|
thor (>= 0.14.0)
|
|
52
52
|
arel (3.0.3)
|
|
53
53
|
blankslate (2.1.2.4)
|
|
54
|
-
bson (
|
|
55
|
-
bson (1.6.0-java)
|
|
56
|
-
bson_ext (1.6.0)
|
|
57
|
-
bson (= 1.6.0)
|
|
54
|
+
bson (4.0.2)
|
|
58
55
|
builder (3.0.4)
|
|
59
56
|
celluloid (0.16.0)
|
|
60
57
|
timers (~> 4.0.0)
|
|
@@ -121,8 +118,8 @@ GEM
|
|
|
121
118
|
minitest (4.2.0)
|
|
122
119
|
mocha (1.1.0)
|
|
123
120
|
metaclass (~> 0.0.1)
|
|
124
|
-
mongo (
|
|
125
|
-
bson (
|
|
121
|
+
mongo (2.2.3)
|
|
122
|
+
bson (~> 4.0)
|
|
126
123
|
multi_json (1.11.1)
|
|
127
124
|
parslet (1.5.0)
|
|
128
125
|
blankslate (~> 2.0)
|
|
@@ -205,7 +202,6 @@ DEPENDENCIES
|
|
|
205
202
|
RedCloth
|
|
206
203
|
activerecord-jdbc-adapter
|
|
207
204
|
appraisal (~> 1.0.2)
|
|
208
|
-
bson_ext
|
|
209
205
|
bundler (>= 1.8.0)
|
|
210
206
|
fakefs
|
|
211
207
|
fastthread!
|
|
@@ -216,7 +212,7 @@ DEPENDENCIES
|
|
|
216
212
|
minitest (~> 4.2.0)
|
|
217
213
|
minitest_tu_shim (~> 1.3.3)
|
|
218
214
|
mocha (~> 1.0)
|
|
219
|
-
mongo
|
|
215
|
+
mongo (~> 2.1)
|
|
220
216
|
passenger (~> 3.0)
|
|
221
217
|
rack
|
|
222
218
|
rails (= 3.2.22)
|
data/gemfiles/rails41.gemfile
CHANGED
|
@@ -5,7 +5,7 @@ source "https://rubygems.org"
|
|
|
5
5
|
gem "rack"
|
|
6
6
|
gem "redis", ">= 2.1"
|
|
7
7
|
gem "redis-namespace", ">= 1.1.0"
|
|
8
|
-
gem "mongo"
|
|
8
|
+
gem "mongo", "~> 2.1"
|
|
9
9
|
gem "integration", "<= 0.1.0"
|
|
10
10
|
gem "rubystats", ">= 0.2.4"
|
|
11
11
|
gem "garb", "< 0.9.2", :require => false
|
|
@@ -26,7 +26,6 @@ group :development do
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
platforms :ruby do
|
|
29
|
-
gem "bson_ext"
|
|
30
29
|
gem "sqlite3", "~> 1.3.10"
|
|
31
30
|
end
|
|
32
31
|
|
|
@@ -7,7 +7,7 @@ GIT
|
|
|
7
7
|
PATH
|
|
8
8
|
remote: .././
|
|
9
9
|
specs:
|
|
10
|
-
vanity (2.
|
|
10
|
+
vanity (2.2.0)
|
|
11
11
|
i18n
|
|
12
12
|
|
|
13
13
|
GEM
|
|
@@ -50,10 +50,7 @@ GEM
|
|
|
50
50
|
thor (>= 0.14.0)
|
|
51
51
|
arel (5.0.1.20140414130214)
|
|
52
52
|
blankslate (2.1.2.4)
|
|
53
|
-
bson (
|
|
54
|
-
bson (1.10.0-java)
|
|
55
|
-
bson_ext (1.10.0)
|
|
56
|
-
bson (~> 1.10.0)
|
|
53
|
+
bson (4.0.2)
|
|
57
54
|
builder (3.2.2)
|
|
58
55
|
celluloid (0.16.0)
|
|
59
56
|
timers (~> 4.0.0)
|
|
@@ -119,10 +116,8 @@ GEM
|
|
|
119
116
|
minitest (5.5.1)
|
|
120
117
|
mocha (1.1.0)
|
|
121
118
|
metaclass (~> 0.0.1)
|
|
122
|
-
mongo (
|
|
123
|
-
bson (~>
|
|
124
|
-
mongo (1.10.0-java)
|
|
125
|
-
bson (~> 1.10.0)
|
|
119
|
+
mongo (2.2.3)
|
|
120
|
+
bson (~> 4.0)
|
|
126
121
|
multi_json (1.10.1)
|
|
127
122
|
parslet (1.5.0)
|
|
128
123
|
blankslate (~> 2.0)
|
|
@@ -199,7 +194,6 @@ DEPENDENCIES
|
|
|
199
194
|
RedCloth
|
|
200
195
|
activerecord-jdbc-adapter
|
|
201
196
|
appraisal (~> 1.0.2)
|
|
202
|
-
bson_ext
|
|
203
197
|
bundler (>= 1.8.0)
|
|
204
198
|
fakefs
|
|
205
199
|
fastthread!
|
|
@@ -209,7 +203,7 @@ DEPENDENCIES
|
|
|
209
203
|
jekyll
|
|
210
204
|
minitest (>= 4.2)
|
|
211
205
|
mocha (~> 1.0)
|
|
212
|
-
mongo
|
|
206
|
+
mongo (~> 2.1)
|
|
213
207
|
passenger (~> 3.0)
|
|
214
208
|
rack
|
|
215
209
|
rails (= 4.1.9)
|
data/gemfiles/rails42.gemfile
CHANGED
|
@@ -5,7 +5,7 @@ source "https://rubygems.org"
|
|
|
5
5
|
gem "rack"
|
|
6
6
|
gem "redis", ">= 2.1"
|
|
7
7
|
gem "redis-namespace", ">= 1.1.0"
|
|
8
|
-
gem "mongo"
|
|
8
|
+
gem "mongo", "~> 2.1"
|
|
9
9
|
gem "integration", "<= 0.1.0"
|
|
10
10
|
gem "rubystats", ">= 0.2.4"
|
|
11
11
|
gem "garb", "< 0.9.2", :require => false
|
|
@@ -26,7 +26,6 @@ group :development do
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
platforms :ruby do
|
|
29
|
-
gem "bson_ext"
|
|
30
29
|
gem "sqlite3", "~> 1.3.10"
|
|
31
30
|
end
|
|
32
31
|
|
|
@@ -7,7 +7,7 @@ GIT
|
|
|
7
7
|
PATH
|
|
8
8
|
remote: ..
|
|
9
9
|
specs:
|
|
10
|
-
vanity (2.
|
|
10
|
+
vanity (2.2.0)
|
|
11
11
|
i18n
|
|
12
12
|
|
|
13
13
|
GEM
|
|
@@ -59,10 +59,7 @@ GEM
|
|
|
59
59
|
thor (>= 0.14.0)
|
|
60
60
|
arel (6.0.0)
|
|
61
61
|
blankslate (2.1.2.4)
|
|
62
|
-
bson (
|
|
63
|
-
bson (1.10.0-java)
|
|
64
|
-
bson_ext (1.10.0)
|
|
65
|
-
bson (~> 1.10.0)
|
|
62
|
+
bson (4.0.2)
|
|
66
63
|
builder (3.2.2)
|
|
67
64
|
celluloid (0.16.0)
|
|
68
65
|
timers (~> 4.0.0)
|
|
@@ -133,10 +130,8 @@ GEM
|
|
|
133
130
|
minitest (5.5.1)
|
|
134
131
|
mocha (1.1.0)
|
|
135
132
|
metaclass (~> 0.0.1)
|
|
136
|
-
mongo (
|
|
137
|
-
bson (~>
|
|
138
|
-
mongo (1.10.0-java)
|
|
139
|
-
bson (~> 1.10.0)
|
|
133
|
+
mongo (2.2.3)
|
|
134
|
+
bson (~> 4.0)
|
|
140
135
|
multi_json (1.10.1)
|
|
141
136
|
nokogiri (1.6.5)
|
|
142
137
|
mini_portile (~> 0.6.0)
|
|
@@ -225,7 +220,6 @@ DEPENDENCIES
|
|
|
225
220
|
RedCloth
|
|
226
221
|
activerecord-jdbc-adapter
|
|
227
222
|
appraisal (~> 1.0.2)
|
|
228
|
-
bson_ext
|
|
229
223
|
bundler (>= 1.8.0)
|
|
230
224
|
fakefs
|
|
231
225
|
fastthread!
|
|
@@ -235,7 +229,7 @@ DEPENDENCIES
|
|
|
235
229
|
jekyll
|
|
236
230
|
minitest (>= 4.2)
|
|
237
231
|
mocha (~> 1.0)
|
|
238
|
-
mongo
|
|
232
|
+
mongo (~> 2.1)
|
|
239
233
|
passenger (~> 3.0)
|
|
240
234
|
rack
|
|
241
235
|
rails (= 4.2.0)
|
|
@@ -5,7 +5,7 @@ source "https://rubygems.org"
|
|
|
5
5
|
gem "rack"
|
|
6
6
|
gem "redis", ">= 2.1"
|
|
7
7
|
gem "redis-namespace", ">= 1.1.0"
|
|
8
|
-
gem "mongo"
|
|
8
|
+
gem "mongo", "~> 2.1"
|
|
9
9
|
gem "integration", "<= 0.1.0"
|
|
10
10
|
gem "rubystats", ">= 0.2.4"
|
|
11
11
|
gem "garb", "< 0.9.2", :require => false
|
|
@@ -27,7 +27,6 @@ group :development do
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
platforms :ruby do
|
|
30
|
-
gem "bson_ext"
|
|
31
30
|
gem "sqlite3", "~> 1.3.10"
|
|
32
31
|
end
|
|
33
32
|
|
|
@@ -7,7 +7,7 @@ GIT
|
|
|
7
7
|
PATH
|
|
8
8
|
remote: ../
|
|
9
9
|
specs:
|
|
10
|
-
vanity (2.
|
|
10
|
+
vanity (2.2.0)
|
|
11
11
|
i18n
|
|
12
12
|
|
|
13
13
|
GEM
|
|
@@ -55,9 +55,7 @@ GEM
|
|
|
55
55
|
rake
|
|
56
56
|
thor (>= 0.14.0)
|
|
57
57
|
arel (6.0.3)
|
|
58
|
-
bson (
|
|
59
|
-
bson_ext (1.12.5)
|
|
60
|
-
bson (~> 1.12.5)
|
|
58
|
+
bson (4.0.2)
|
|
61
59
|
builder (3.2.2)
|
|
62
60
|
colorator (0.1)
|
|
63
61
|
concurrent-ruby (1.0.0)
|
|
@@ -105,8 +103,8 @@ GEM
|
|
|
105
103
|
minitest (5.8.3)
|
|
106
104
|
mocha (1.1.0)
|
|
107
105
|
metaclass (~> 0.0.1)
|
|
108
|
-
mongo (
|
|
109
|
-
bson (
|
|
106
|
+
mongo (2.2.3)
|
|
107
|
+
bson (~> 4.0)
|
|
110
108
|
nokogiri (1.6.7.1)
|
|
111
109
|
mini_portile2 (~> 2.0.0.rc2)
|
|
112
110
|
passenger (3.0.21)
|
|
@@ -180,7 +178,6 @@ DEPENDENCIES
|
|
|
180
178
|
RedCloth
|
|
181
179
|
activerecord-jdbc-adapter
|
|
182
180
|
appraisal (~> 1.0.2)
|
|
183
|
-
bson_ext
|
|
184
181
|
bundler (>= 1.8.0)
|
|
185
182
|
fakefs
|
|
186
183
|
fastthread!
|
|
@@ -190,7 +187,7 @@ DEPENDENCIES
|
|
|
190
187
|
jekyll
|
|
191
188
|
minitest (>= 4.2)
|
|
192
189
|
mocha (~> 1.0)
|
|
193
|
-
mongo
|
|
190
|
+
mongo (~> 2.1)
|
|
194
191
|
passenger (~> 3.0)
|
|
195
192
|
protected_attributes (= 1.1.0)
|
|
196
193
|
rack
|
|
@@ -71,7 +71,7 @@ module Vanity
|
|
|
71
71
|
def set_experiment_enabled(experiment, enabled)
|
|
72
72
|
fail "Not implemented"
|
|
73
73
|
end
|
|
74
|
-
|
|
74
|
+
|
|
75
75
|
# Returns true if experiment is enabled, the default (Vanity.configuration.experiments_start_enabled) is true.
|
|
76
76
|
# (*except for mock_adapter, where default is true for testing)
|
|
77
77
|
def is_experiment_enabled?(experiment)
|
|
@@ -95,7 +95,7 @@ module Vanity
|
|
|
95
95
|
def is_experiment_completed?(experiment)
|
|
96
96
|
@experiments[experiment] && @experiments[experiment][:completed_at]
|
|
97
97
|
end
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
def set_experiment_enabled(experiment, enabled)
|
|
100
100
|
@experiments[experiment] ||= {}
|
|
101
101
|
@experiments[experiment][:enabled] = enabled
|
|
@@ -18,24 +18,13 @@ module Vanity
|
|
|
18
18
|
attr_reader :mongo
|
|
19
19
|
|
|
20
20
|
def initialize(options)
|
|
21
|
-
setup_connection(options)
|
|
22
21
|
@options = options.clone
|
|
23
22
|
@options[:database] ||= (@options[:path] && @options[:path].split("/")[1]) || "vanity"
|
|
24
23
|
connect!
|
|
25
24
|
end
|
|
26
25
|
|
|
27
|
-
def setup_connection(options = {})
|
|
28
|
-
if options[:hosts]
|
|
29
|
-
args = (options[:hosts].map{|host| [host, options[:port]] } << {:connect => false})
|
|
30
|
-
@mongo = Mongo::ReplSetConnection.new(*args)
|
|
31
|
-
else
|
|
32
|
-
@mongo = Mongo::Connection.new(options[:host], options[:port], :connect => false)
|
|
33
|
-
end
|
|
34
|
-
@mongo
|
|
35
|
-
end
|
|
36
|
-
|
|
37
26
|
def active?
|
|
38
|
-
|
|
27
|
+
!!@mongo
|
|
39
28
|
end
|
|
40
29
|
|
|
41
30
|
def disconnect!
|
|
@@ -50,16 +39,21 @@ module Vanity
|
|
|
50
39
|
end
|
|
51
40
|
|
|
52
41
|
def connect!
|
|
53
|
-
|
|
54
|
-
@
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@metrics
|
|
58
|
-
@experiments =
|
|
59
|
-
@
|
|
60
|
-
@participants
|
|
61
|
-
@participants.
|
|
62
|
-
@participants.
|
|
42
|
+
Mongo::Logger.logger = Vanity.logger
|
|
43
|
+
setup_connection(@options)
|
|
44
|
+
|
|
45
|
+
@metrics = @mongo["vanity.metrics"]
|
|
46
|
+
@metrics.create unless @mongo.database.collection_names.include?("vanity.metrics")
|
|
47
|
+
@experiments = @mongo["vanity.experiments"]
|
|
48
|
+
@experiments.create unless @mongo.database.collection_names.include?("vanity.experiments")
|
|
49
|
+
@participants = @mongo["vanity.participants"]
|
|
50
|
+
@participants.create unless @mongo.database.collection_names.include?("vanity.participants")
|
|
51
|
+
@participants.indexes.create_many(
|
|
52
|
+
{ :key => { :experiment => 1, :identity => 1 }, :unique=>true },
|
|
53
|
+
{ :key => { :experiment => 1, :seen => 1 } },
|
|
54
|
+
{ :key => { :experiment => 1, :converted => 1 } }
|
|
55
|
+
)
|
|
56
|
+
|
|
63
57
|
@mongo
|
|
64
58
|
end
|
|
65
59
|
|
|
@@ -78,7 +72,7 @@ module Vanity
|
|
|
78
72
|
# -- Metrics --
|
|
79
73
|
|
|
80
74
|
def get_metric_last_update_at(metric)
|
|
81
|
-
record = @metrics.
|
|
75
|
+
record = @metrics.find(:_id=>metric).limit(1).first
|
|
82
76
|
record && record["last_update_at"]
|
|
83
77
|
end
|
|
84
78
|
|
|
@@ -87,55 +81,77 @@ module Vanity
|
|
|
87
81
|
values.each_with_index do |v,i|
|
|
88
82
|
inc["data.#{timestamp.to_date}.#{i}"] = v
|
|
89
83
|
end
|
|
90
|
-
@metrics.
|
|
84
|
+
@metrics.find(:_id=>metric).find_one_and_replace(
|
|
85
|
+
{
|
|
86
|
+
"$inc"=>inc,
|
|
87
|
+
"$set"=>{ :last_update_at=>Time.now }
|
|
88
|
+
},
|
|
89
|
+
:upsert=>true
|
|
90
|
+
)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def metric_values(metric, from, to)
|
|
94
|
-
record = @metrics.
|
|
94
|
+
record = @metrics.find(:_id=>metric).limit(1).first
|
|
95
95
|
data = record && record["data"] || {}
|
|
96
96
|
(from.to_date..to.to_date).map { |date| (data[date.to_s] || {}).values }
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
def destroy_metric(metric)
|
|
100
|
-
@metrics.
|
|
100
|
+
@metrics.find(:_id=>metric).delete_one
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
# -- Experiments --
|
|
105
105
|
|
|
106
106
|
def experiment_persisted?(experiment)
|
|
107
|
-
!!@experiments.
|
|
107
|
+
!!@experiments.find(:_id=>experiment).limit(1).first
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
def set_experiment_created_at(experiment, time)
|
|
111
|
-
@experiments.
|
|
111
|
+
# @experiments.insert_one(:_id=>experiment, :created_at=>time)
|
|
112
|
+
@experiments.find(:_id=>experiment).find_one_and_replace(
|
|
113
|
+
{
|
|
114
|
+
"$setOnInsert"=>{ :created_at=>time }
|
|
115
|
+
},
|
|
116
|
+
:upsert=>true
|
|
117
|
+
)
|
|
112
118
|
end
|
|
113
119
|
|
|
114
120
|
def get_experiment_created_at(experiment)
|
|
115
|
-
record = @experiments.
|
|
121
|
+
record = @experiments.find(:_id=>experiment).limit(1).projection(:created_at=>1).first
|
|
116
122
|
record && record["created_at"]
|
|
117
123
|
#Returns nil if either the record or the field doesn't exist
|
|
118
124
|
end
|
|
119
125
|
|
|
120
126
|
def set_experiment_completed_at(experiment, time)
|
|
121
|
-
@experiments.
|
|
127
|
+
@experiments.find(:_id=>experiment).find_one_and_replace(
|
|
128
|
+
{
|
|
129
|
+
"$set"=>{ :completed_at=>time }
|
|
130
|
+
},
|
|
131
|
+
:upsert=>true
|
|
132
|
+
)
|
|
122
133
|
end
|
|
123
134
|
|
|
124
135
|
def get_experiment_completed_at(experiment)
|
|
125
|
-
record = @experiments.
|
|
136
|
+
record = @experiments.find(:_id=>experiment).limit(1).projection(:completed_at=>1).first
|
|
126
137
|
record && record["completed_at"]
|
|
127
138
|
end
|
|
128
139
|
|
|
129
140
|
def is_experiment_completed?(experiment)
|
|
130
|
-
!!@experiments.
|
|
141
|
+
!!@experiments.find(:_id=>experiment, :completed_at=>{ "$exists"=>true }).limit(1).first
|
|
131
142
|
end
|
|
132
143
|
|
|
133
144
|
def set_experiment_enabled(experiment, enabled)
|
|
134
|
-
@experiments.
|
|
145
|
+
@experiments.find(:_id=>experiment).find_one_and_replace(
|
|
146
|
+
{
|
|
147
|
+
"$set"=>{ :enabled=>enabled }
|
|
148
|
+
},
|
|
149
|
+
:upsert=>true
|
|
150
|
+
)
|
|
135
151
|
end
|
|
136
152
|
|
|
137
153
|
def is_experiment_enabled?(experiment)
|
|
138
|
-
record = @experiments.
|
|
154
|
+
record = @experiments.find(:_id=>experiment).limit(1).projection(:enabled=>1).first
|
|
139
155
|
if Vanity.configuration.experiments_start_enabled
|
|
140
156
|
record == nil || record["enabled"] != false
|
|
141
157
|
else
|
|
@@ -144,7 +160,7 @@ module Vanity
|
|
|
144
160
|
end
|
|
145
161
|
|
|
146
162
|
def ab_counts(experiment, alternative)
|
|
147
|
-
record = @experiments.
|
|
163
|
+
record = @experiments.find(:_id=>experiment ).limit(1).projection(:conversions=>1).first
|
|
148
164
|
conversions = record && record["conversions"]
|
|
149
165
|
{ :participants => @participants.find({ :experiment=>experiment, :seen=>alternative }).count,
|
|
150
166
|
:converted => @participants.find({ :experiment=>experiment, :converted=>alternative }).count,
|
|
@@ -152,56 +168,109 @@ module Vanity
|
|
|
152
168
|
end
|
|
153
169
|
|
|
154
170
|
def ab_show(experiment, identity, alternative)
|
|
155
|
-
@participants.
|
|
171
|
+
@participants.find(:experiment=>experiment, :identity=>identity).find_one_and_replace(
|
|
172
|
+
{
|
|
173
|
+
"$set"=>{ :show=>alternative }
|
|
174
|
+
},
|
|
175
|
+
:upsert=>true
|
|
176
|
+
)
|
|
156
177
|
end
|
|
157
178
|
|
|
158
179
|
def ab_showing(experiment, identity)
|
|
159
|
-
participant = @participants.
|
|
180
|
+
participant = @participants.find(:experiment=>experiment, :identity=>identity).limit(1).projection(:show=>1).first
|
|
160
181
|
participant && participant["show"]
|
|
161
182
|
end
|
|
162
183
|
|
|
163
184
|
def ab_not_showing(experiment, identity)
|
|
164
|
-
@participants.
|
|
185
|
+
@participants.find(:experiment=>experiment, :identity=>identity).find_one_and_replace(
|
|
186
|
+
{
|
|
187
|
+
"$unset"=> { :show => "" }
|
|
188
|
+
},
|
|
189
|
+
:upsert=>true
|
|
190
|
+
)
|
|
165
191
|
end
|
|
166
192
|
|
|
167
193
|
def ab_add_participant(experiment, alternative, identity)
|
|
168
|
-
@participants.
|
|
194
|
+
@participants.find(:experiment=>experiment, :identity=>identity).find_one_and_replace(
|
|
195
|
+
{
|
|
196
|
+
"$push"=>{ :seen=>alternative }
|
|
197
|
+
},
|
|
198
|
+
:upsert=>true
|
|
199
|
+
)
|
|
169
200
|
end
|
|
170
201
|
|
|
171
202
|
# Determines if a participant already has seen this alternative in this experiment.
|
|
172
203
|
def ab_seen(experiment, identity, alternative)
|
|
173
|
-
participant = @participants.
|
|
204
|
+
participant = @participants.find(:experiment=>experiment, :identity=>identity).limit(1).projection(:seen=>1).first
|
|
174
205
|
participant && participant["seen"].first == alternative.id
|
|
175
206
|
end
|
|
176
207
|
|
|
177
208
|
# Returns the participant's seen alternative in this experiment, if it exists
|
|
178
209
|
def ab_assigned(experiment, identity)
|
|
179
|
-
participant = @participants.
|
|
210
|
+
participant = @participants.find(:experiment=>experiment, :identity=>identity).limit(1).projection(:seen=>1).first
|
|
180
211
|
participant && participant["seen"].first
|
|
181
212
|
end
|
|
182
213
|
|
|
183
214
|
def ab_add_conversion(experiment, alternative, identity, count = 1, implicit = false)
|
|
184
215
|
if implicit
|
|
185
|
-
@participants.
|
|
216
|
+
@participants.find(:experiment=>experiment, :identity=>identity).find_one_and_replace(
|
|
217
|
+
{
|
|
218
|
+
"$push"=>{ :seen=>alternative }
|
|
219
|
+
},
|
|
220
|
+
:upsert=>true
|
|
221
|
+
)
|
|
186
222
|
else
|
|
187
|
-
participating = @participants.
|
|
223
|
+
participating = @participants.find(:experiment=>experiment, :identity=>identity, :seen=>alternative).limit(1).first
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
if implicit || participating
|
|
227
|
+
@participants.find(:experiment=>experiment, :identity=>identity).find_one_and_replace(
|
|
228
|
+
{
|
|
229
|
+
"$push"=>{ :converted=>alternative }
|
|
230
|
+
},
|
|
231
|
+
:upsert=>true
|
|
232
|
+
)
|
|
188
233
|
end
|
|
189
|
-
|
|
190
|
-
@experiments.
|
|
234
|
+
|
|
235
|
+
@experiments.find(:_id=>experiment).find_one_and_replace(
|
|
236
|
+
{
|
|
237
|
+
"$inc"=>{ "conversions.#{alternative}"=>count }
|
|
238
|
+
},
|
|
239
|
+
:upsert=>true
|
|
240
|
+
)
|
|
191
241
|
end
|
|
192
242
|
|
|
193
243
|
def ab_get_outcome(experiment)
|
|
194
|
-
experiment = @experiments.
|
|
244
|
+
experiment = @experiments.find(:_id=>experiment).limit(1).projection(:outcome=>1).first
|
|
195
245
|
experiment && experiment["outcome"]
|
|
196
246
|
end
|
|
197
247
|
|
|
198
248
|
def ab_set_outcome(experiment, alternative = 0)
|
|
199
|
-
@experiments.
|
|
249
|
+
@experiments.find(:_id=>experiment).find_one_and_replace(
|
|
250
|
+
{
|
|
251
|
+
"$set"=>{ :outcome=>alternative }
|
|
252
|
+
},
|
|
253
|
+
:upsert=>true
|
|
254
|
+
)
|
|
200
255
|
end
|
|
201
256
|
|
|
202
257
|
def destroy_experiment(experiment)
|
|
203
|
-
@experiments.
|
|
204
|
-
@participants.
|
|
258
|
+
@experiments.find(:_id=>experiment).delete_one
|
|
259
|
+
@participants.find(:experiment=>experiment).delete_many
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
private
|
|
263
|
+
|
|
264
|
+
def setup_connection(options)
|
|
265
|
+
options[:user] = options[:username] if options[:username]
|
|
266
|
+
|
|
267
|
+
hosts = options.delete(:hosts) || [options.delete(:host)]
|
|
268
|
+
hosts.map! { |host| "#{host}:#{options.delete(:port)}" }
|
|
269
|
+
|
|
270
|
+
@mongo = Mongo::Client.new(
|
|
271
|
+
hosts,
|
|
272
|
+
options
|
|
273
|
+
)
|
|
205
274
|
end
|
|
206
275
|
end
|
|
207
276
|
end
|
data/lib/vanity/configuration.rb
CHANGED
|
@@ -25,9 +25,9 @@ module Vanity
|
|
|
25
25
|
nil
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
# Filter all User-Agents that have 'bot', 'crawler', 'spider', URL.
|
|
30
|
-
#
|
|
28
|
+
#
|
|
29
|
+
# Filter all User-Agents that have 'bot', 'crawler', 'spider', URL.
|
|
30
|
+
#
|
|
31
31
|
def default_request_filter(request) # :nodoc:
|
|
32
32
|
request &&
|
|
33
33
|
request.env &&
|
|
@@ -27,10 +27,10 @@ module Vanity
|
|
|
27
27
|
@use_probabilities = nil
|
|
28
28
|
@is_default_set = false
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# -- Default --
|
|
32
32
|
|
|
33
|
-
# Call this method once to set a default alternative. Call without
|
|
33
|
+
# Call this method once to set a default alternative. Call without
|
|
34
34
|
# arguments to obtain the current default. If default is not specified,
|
|
35
35
|
# the first alternative is used.
|
|
36
36
|
#
|
|
@@ -55,12 +55,12 @@ module Vanity
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# -- Enabled --
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
# Returns true if experiment is enabled, false if disabled.
|
|
60
60
|
def enabled?
|
|
61
61
|
!@playground.collecting? || (active? && connection.is_experiment_enabled?(@id))
|
|
62
62
|
end
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
# Enable or disable the experiment. Only works if the playground is collecting
|
|
65
65
|
# and this experiment is enabled.
|
|
66
66
|
#
|
|
@@ -70,8 +70,10 @@ module Vanity
|
|
|
70
70
|
def enabled=(bool)
|
|
71
71
|
return unless @playground.collecting? && active?
|
|
72
72
|
if created_at.nil?
|
|
73
|
-
warn
|
|
74
|
-
|
|
73
|
+
Vanity.logger.warn(
|
|
74
|
+
'DB has no created_at for this experiment! This most likely means' +
|
|
75
|
+
'you didn\'t call #save before calling enabled=, which you should.'
|
|
76
|
+
)
|
|
75
77
|
else
|
|
76
78
|
connection.set_experiment_enabled(@id, bool)
|
|
77
79
|
end
|
|
@@ -187,9 +189,9 @@ module Vanity
|
|
|
187
189
|
if @playground.collecting?
|
|
188
190
|
if active?
|
|
189
191
|
if enabled?
|
|
190
|
-
return assignment_for_identity(request)
|
|
192
|
+
return assignment_for_identity(request)
|
|
191
193
|
else
|
|
192
|
-
# Show the default if experiment is disabled.
|
|
194
|
+
# Show the default if experiment is disabled.
|
|
193
195
|
return default
|
|
194
196
|
end
|
|
195
197
|
else
|
|
@@ -340,13 +342,13 @@ module Vanity
|
|
|
340
342
|
require "integration"
|
|
341
343
|
require "rubystats"
|
|
342
344
|
rescue LoadError
|
|
343
|
-
fail
|
|
345
|
+
fail("to use bayes_bandit_score, install integration and rubystats gems")
|
|
344
346
|
end
|
|
345
347
|
|
|
346
348
|
begin
|
|
347
349
|
require "gsl"
|
|
348
350
|
rescue LoadError
|
|
349
|
-
warn
|
|
351
|
+
Vanity.logger.warn("for better integration performance, install gsl gem")
|
|
350
352
|
end
|
|
351
353
|
|
|
352
354
|
BayesianBanditScore.new(alternatives, outcome).calculate!
|
|
@@ -480,8 +482,8 @@ module Vanity
|
|
|
480
482
|
begin
|
|
481
483
|
result = @outcome_is.call
|
|
482
484
|
outcome = result.id if Alternative === result && result.experiment == self
|
|
483
|
-
rescue
|
|
484
|
-
warn
|
|
485
|
+
rescue => e
|
|
486
|
+
Vanity.logger.warn("Error in AbTest#complete!: #{e}")
|
|
485
487
|
end
|
|
486
488
|
else
|
|
487
489
|
best = score.best
|
|
@@ -499,7 +501,7 @@ module Vanity
|
|
|
499
501
|
connection.destroy_experiment(@id)
|
|
500
502
|
super
|
|
501
503
|
end
|
|
502
|
-
|
|
504
|
+
|
|
503
505
|
# clears all collected data for the experiment
|
|
504
506
|
def reset
|
|
505
507
|
return unless @playground.collecting?
|
|
@@ -524,7 +526,7 @@ module Vanity
|
|
|
524
526
|
# and declared.
|
|
525
527
|
def save
|
|
526
528
|
if @saved
|
|
527
|
-
warn
|
|
529
|
+
Vanity.logger.warn("Experiment #{name} has already been saved")
|
|
528
530
|
return
|
|
529
531
|
end
|
|
530
532
|
@saved = true
|
|
@@ -532,21 +534,21 @@ module Vanity
|
|
|
532
534
|
fail "Experiment #{name} needs at least two alternatives" unless @alternatives.size >= 2
|
|
533
535
|
if !@is_default_set
|
|
534
536
|
default(@alternatives.first)
|
|
535
|
-
warn
|
|
537
|
+
Vanity.logger.warn("No default alternative specified; choosing #{@default} as default.")
|
|
536
538
|
elsif alternative(@default).nil?
|
|
537
|
-
#Specified a default that wasn't listed as an alternative; warn and override.
|
|
538
|
-
warn
|
|
539
|
-
#Set the instance variable directly since default(value) is no longer defined
|
|
539
|
+
# Specified a default that wasn't listed as an alternative; warn and override.
|
|
540
|
+
Vanity.logger.warn("Attempted to set unknown alternative #{@default} as default! Using #{@alternatives.first} instead.")
|
|
541
|
+
# Set the instance variable directly since default(value) is no longer defined
|
|
540
542
|
@default = @alternatives.first
|
|
541
543
|
end
|
|
542
544
|
super
|
|
543
545
|
if @metrics.nil? || @metrics.empty?
|
|
544
|
-
warn
|
|
546
|
+
Vanity.logger.warn("Please use metrics method to explicitly state which metric you are measuring against.")
|
|
545
547
|
metric = @playground.metrics[id] ||= Vanity::Metric.new(@playground, name)
|
|
546
548
|
@metrics = [metric]
|
|
547
549
|
end
|
|
548
550
|
@metrics.each do |metric|
|
|
549
|
-
metric.hook
|
|
551
|
+
metric.hook(&method(:track!))
|
|
550
552
|
end
|
|
551
553
|
end
|
|
552
554
|
|
|
@@ -587,7 +589,7 @@ module Vanity
|
|
|
587
589
|
end
|
|
588
590
|
|
|
589
591
|
# Returns the assigned alternative, previously chosen alternative, or
|
|
590
|
-
# alternative_for for a given identity.
|
|
592
|
+
# alternative_for for a given identity.
|
|
591
593
|
def assignment_for_identity(request)
|
|
592
594
|
identity = identity()
|
|
593
595
|
if filter_visitor?(request, identity)
|
|
@@ -633,7 +635,7 @@ module Vanity
|
|
|
633
635
|
end
|
|
634
636
|
|
|
635
637
|
def filter_visitor?(request, identity)
|
|
636
|
-
@playground.request_filter.call(request) ||
|
|
638
|
+
@playground.request_filter.call(request) ||
|
|
637
639
|
(@request_filter_block && @request_filter_block.call(request, identity))
|
|
638
640
|
end
|
|
639
641
|
|
|
@@ -158,7 +158,7 @@ module Vanity
|
|
|
158
158
|
return unless @playground.collecting?
|
|
159
159
|
connection.set_experiment_created_at @id, Time.now
|
|
160
160
|
end
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
# -- Filtering Particpants --
|
|
163
163
|
|
|
164
164
|
# Define an experiment specific request filter. For example:
|
|
@@ -191,8 +191,8 @@ module Vanity
|
|
|
191
191
|
if @complete_block
|
|
192
192
|
begin
|
|
193
193
|
complete! if @complete_block.call
|
|
194
|
-
rescue
|
|
195
|
-
warn
|
|
194
|
+
rescue => e
|
|
195
|
+
Vanity.logger.warn("Error in Vanity::Experiment::Base: #{e}")
|
|
196
196
|
end
|
|
197
197
|
end
|
|
198
198
|
end
|
data/lib/vanity/version.rb
CHANGED
|
@@ -14,7 +14,7 @@ describe Vanity::Adapters::RedisAdapter do
|
|
|
14
14
|
mocked_redis.expects(:client).raises(RuntimeError)
|
|
15
15
|
redis_adapter = Vanity::Adapters::RedisAdapter.new({})
|
|
16
16
|
redis_adapter.stubs(:redis).returns(mocked_redis)
|
|
17
|
-
|
|
17
|
+
Vanity.logger.expects(:warn).with("Error while disconnecting from redis: RuntimeError")
|
|
18
18
|
redis_adapter.disconnect!
|
|
19
19
|
end
|
|
20
20
|
end
|
data/test/experiment/ab_test.rb
CHANGED
|
@@ -181,28 +181,28 @@ class AbTestTest < ActionController::TestCase
|
|
|
181
181
|
exp = experiment(:nil_default_default)
|
|
182
182
|
assert_equal exp.default, exp.alternative(nil)
|
|
183
183
|
end
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
|
|
185
|
+
|
|
186
186
|
# -- Experiment Enabled/disabled --
|
|
187
|
-
|
|
187
|
+
|
|
188
188
|
# @example new test should be enabled regardless of collecting?
|
|
189
189
|
# regardless_of "Vanity.playground.collecting" do
|
|
190
190
|
# assert (new_ab_test :test).enabled?
|
|
191
191
|
# end
|
|
192
192
|
def regardless_of(attr_name, &block)
|
|
193
193
|
prev_val = eval "#{attr_name}?"
|
|
194
|
-
|
|
194
|
+
|
|
195
195
|
eval "#{attr_name}=true"
|
|
196
196
|
block.call(eval "#{attr_name}?")
|
|
197
197
|
nuke_playground
|
|
198
|
-
|
|
198
|
+
|
|
199
199
|
eval "#{attr_name}=false"
|
|
200
200
|
block.call(eval "#{attr_name}?")
|
|
201
201
|
nuke_playground
|
|
202
|
-
|
|
202
|
+
|
|
203
203
|
eval "#{attr_name}=prev_val"
|
|
204
204
|
end
|
|
205
|
-
|
|
205
|
+
|
|
206
206
|
def test_new_test_is_disabled_when_experiments_start_enabled_is_false
|
|
207
207
|
Vanity.configuration.experiments_start_enabled = false
|
|
208
208
|
exp = new_ab_test :test, enable: false do
|
|
@@ -220,7 +220,7 @@ class AbTestTest < ActionController::TestCase
|
|
|
220
220
|
end
|
|
221
221
|
assert exp.enabled?
|
|
222
222
|
end
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
def test_complete_sets_enabled_false
|
|
225
225
|
Vanity.playground.collecting = true
|
|
226
226
|
exp = new_ab_test :test do
|
|
@@ -248,14 +248,14 @@ class AbTestTest < ActionController::TestCase
|
|
|
248
248
|
metrics :coolness
|
|
249
249
|
default false
|
|
250
250
|
end
|
|
251
|
-
|
|
251
|
+
|
|
252
252
|
exp.enabled = true
|
|
253
253
|
assert exp.enabled?
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
exp.enabled = false
|
|
256
256
|
assert !exp.enabled?
|
|
257
257
|
end
|
|
258
|
-
|
|
258
|
+
|
|
259
259
|
def test_cannot_set_enabled_for_inactive
|
|
260
260
|
Vanity.playground.collecting = true
|
|
261
261
|
exp = new_ab_test :test do
|
|
@@ -278,19 +278,19 @@ class AbTestTest < ActionController::TestCase
|
|
|
278
278
|
exp.enabled = false
|
|
279
279
|
assert exp.enabled?
|
|
280
280
|
end
|
|
281
|
-
|
|
281
|
+
|
|
282
282
|
def test_enabled_persists_across_definitions
|
|
283
283
|
Vanity.configuration.experiments_start_enabled = false
|
|
284
284
|
Vanity.playground.collecting = true
|
|
285
|
-
new_ab_test :test, :enable => false do
|
|
285
|
+
new_ab_test :test, :enable => false do
|
|
286
286
|
metrics :coolness
|
|
287
287
|
default false
|
|
288
288
|
end
|
|
289
289
|
assert !experiment(:test).enabled? #starts off false
|
|
290
|
-
|
|
290
|
+
|
|
291
291
|
new_playground
|
|
292
292
|
metric "Coolness"
|
|
293
|
-
|
|
293
|
+
|
|
294
294
|
new_ab_test :test, :enable => false do
|
|
295
295
|
metrics :coolness
|
|
296
296
|
default false
|
|
@@ -298,10 +298,10 @@ class AbTestTest < ActionController::TestCase
|
|
|
298
298
|
assert !experiment(:test).enabled? #still false
|
|
299
299
|
experiment(:test).enabled = true
|
|
300
300
|
assert experiment(:test).enabled? #now true
|
|
301
|
-
|
|
301
|
+
|
|
302
302
|
new_playground
|
|
303
303
|
metric "Coolness"
|
|
304
|
-
|
|
304
|
+
|
|
305
305
|
new_ab_test :test, :enable => false do
|
|
306
306
|
metrics :coolness
|
|
307
307
|
default false
|
|
@@ -314,15 +314,15 @@ class AbTestTest < ActionController::TestCase
|
|
|
314
314
|
def test_enabled_persists_across_definitions_when_starting_enabled
|
|
315
315
|
Vanity.configuration.experiments_start_enabled = true
|
|
316
316
|
Vanity.playground.collecting = true
|
|
317
|
-
new_ab_test :test, :enable => false do
|
|
317
|
+
new_ab_test :test, :enable => false do
|
|
318
318
|
metrics :coolness
|
|
319
319
|
default false
|
|
320
320
|
end
|
|
321
321
|
assert experiment(:test).enabled? #starts off true
|
|
322
|
-
|
|
322
|
+
|
|
323
323
|
new_playground
|
|
324
324
|
metric "Coolness"
|
|
325
|
-
|
|
325
|
+
|
|
326
326
|
new_ab_test :test, :enable => false do
|
|
327
327
|
metrics :coolness
|
|
328
328
|
default false
|
|
@@ -330,10 +330,10 @@ class AbTestTest < ActionController::TestCase
|
|
|
330
330
|
assert experiment(:test).enabled? #still true
|
|
331
331
|
experiment(:test).enabled = false
|
|
332
332
|
assert !experiment(:test).enabled? #now false
|
|
333
|
-
|
|
333
|
+
|
|
334
334
|
new_playground
|
|
335
335
|
metric "Coolness"
|
|
336
|
-
|
|
336
|
+
|
|
337
337
|
new_ab_test :test, :enable => false do
|
|
338
338
|
metrics :coolness
|
|
339
339
|
default false
|
|
@@ -342,12 +342,12 @@ class AbTestTest < ActionController::TestCase
|
|
|
342
342
|
experiment(:test).enabled = true
|
|
343
343
|
assert experiment(:test).enabled? #now true again
|
|
344
344
|
end
|
|
345
|
-
|
|
345
|
+
|
|
346
346
|
def test_choose_random_when_enabled
|
|
347
347
|
regardless_of "Vanity.playground.collecting" do
|
|
348
348
|
metric "Coolness"
|
|
349
349
|
|
|
350
|
-
exp = new_ab_test :test do
|
|
350
|
+
exp = new_ab_test :test do
|
|
351
351
|
metrics :coolness
|
|
352
352
|
true_false
|
|
353
353
|
default false
|
|
@@ -360,20 +360,20 @@ class AbTestTest < ActionController::TestCase
|
|
|
360
360
|
assert_equal results, [true, false].to_set
|
|
361
361
|
end
|
|
362
362
|
end
|
|
363
|
-
|
|
363
|
+
|
|
364
364
|
def test_choose_default_when_disabled
|
|
365
365
|
exp = new_ab_test :test do
|
|
366
366
|
metrics :coolness
|
|
367
367
|
alternatives 0, 1, 2, 3, 4, 5
|
|
368
368
|
default 3
|
|
369
369
|
end
|
|
370
|
-
|
|
370
|
+
|
|
371
371
|
exp.enabled = false
|
|
372
372
|
100.times.each do
|
|
373
373
|
assert_equal 3, exp.choose.value
|
|
374
374
|
end
|
|
375
375
|
end
|
|
376
|
-
|
|
376
|
+
|
|
377
377
|
def test_choose_outcome_when_finished
|
|
378
378
|
exp = new_ab_test :test do
|
|
379
379
|
metrics :coolness
|
|
@@ -386,7 +386,7 @@ class AbTestTest < ActionController::TestCase
|
|
|
386
386
|
assert_equal 5, exp.choose.value
|
|
387
387
|
end
|
|
388
388
|
end
|
|
389
|
-
|
|
389
|
+
|
|
390
390
|
# -- Experiment metric --
|
|
391
391
|
|
|
392
392
|
def test_explicit_metric
|
|
@@ -1247,7 +1247,7 @@ This experiment did not run long enough to find a clear winner.
|
|
|
1247
1247
|
default false
|
|
1248
1248
|
end
|
|
1249
1249
|
e = experiment(:quick)
|
|
1250
|
-
|
|
1250
|
+
Vanity.logger.expects(:warn)
|
|
1251
1251
|
assert_nothing_raised do
|
|
1252
1252
|
e.complete!
|
|
1253
1253
|
end
|
|
@@ -1448,9 +1448,9 @@ This experiment did not run long enough to find a clear winner.
|
|
|
1448
1448
|
end
|
|
1449
1449
|
assert_equal results, [:b].to_set
|
|
1450
1450
|
end
|
|
1451
|
-
|
|
1451
|
+
|
|
1452
1452
|
# -- Reset --
|
|
1453
|
-
|
|
1453
|
+
|
|
1454
1454
|
def test_reset_clears_participants
|
|
1455
1455
|
new_ab_test :simple do
|
|
1456
1456
|
alternatives :a, :b, :c
|
|
@@ -1462,20 +1462,20 @@ This experiment did not run long enough to find a clear winner.
|
|
|
1462
1462
|
experiment(:simple).reset
|
|
1463
1463
|
assert_equal experiment(:simple).alternatives[1].participants, 0
|
|
1464
1464
|
end
|
|
1465
|
-
|
|
1465
|
+
|
|
1466
1466
|
def test_clears_outcome_and_completed_at
|
|
1467
1467
|
new_ab_test :simple do
|
|
1468
1468
|
alternatives :a, :b, :c
|
|
1469
1469
|
default :a
|
|
1470
|
-
metrics :coolness
|
|
1471
|
-
end
|
|
1472
|
-
experiment(:simple).reset
|
|
1473
|
-
assert_nil experiment(:simple).outcome
|
|
1470
|
+
metrics :coolness
|
|
1471
|
+
end
|
|
1472
|
+
experiment(:simple).reset
|
|
1473
|
+
assert_nil experiment(:simple).outcome
|
|
1474
1474
|
assert_nil experiment(:simple).completed_at
|
|
1475
1475
|
end
|
|
1476
|
-
|
|
1476
|
+
|
|
1477
1477
|
# -- Pick Winner --
|
|
1478
|
-
|
|
1478
|
+
|
|
1479
1479
|
def test_complete_with_argument_sets_outcome_and_completes
|
|
1480
1480
|
new_ab_test :simple do
|
|
1481
1481
|
alternatives :a, :b, :c
|
|
@@ -146,7 +146,7 @@ describe Vanity::Experiment::Base do
|
|
|
146
146
|
e = experiment(:ab)
|
|
147
147
|
e.complete_if { true }
|
|
148
148
|
e.stubs(:complete!).raises(RuntimeError, "A forced error")
|
|
149
|
-
|
|
149
|
+
Vanity.logger.expects(:warn)
|
|
150
150
|
e.stubs(:identity).returns(:b)
|
|
151
151
|
e.track!(:a, Time.now, 10)
|
|
152
152
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vanity
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Assaf Arkin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-03-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: i18n
|
|
@@ -213,7 +213,7 @@ metadata: {}
|
|
|
213
213
|
post_install_message: To get started run vanity --help
|
|
214
214
|
rdoc_options:
|
|
215
215
|
- --title
|
|
216
|
-
- Vanity 2.
|
|
216
|
+
- Vanity 2.2.0
|
|
217
217
|
- --main
|
|
218
218
|
- README.md
|
|
219
219
|
- --webcvs
|