vanity 2.1.2 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|