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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e624cf4ffac44db90b2d87b2142487870e8d8313
4
- data.tar.gz: d2f59e2b24bf57977ac7e6494416b5a8eab08c6b
3
+ metadata.gz: a239324275cb444df1667c84c26134d4cea832af
4
+ data.tar.gz: 3640a24cc2166d34f1211f629a6d5334bbcc1021
5
5
  SHA512:
6
- metadata.gz: 4f7713ef88ad77be3d31a16767ea3e5bf5cc5bb50478208aa3bb506e8f316eec26d1fb3e1752b7b6cae73c918a98cf5ad1dae78513e9e6e7c5bb20e329569861
7
- data.tar.gz: 3ff7a9e8f111e3aa83c56bcd7d4c3c834c76c9e0118ce6cacca8921362f78d1016c8c21555fd43f4a42f972557dac32e673becab80c1aacedfa2526fd00daba8
6
+ metadata.gz: b12cf807e8c67be3090a3d96bed156c8237dd8c127c3498a93c5ffc46ac9ebb70435cbf87a7b82f61f293b013fb10f5c4f0dd773e3fa4e9fb81364f90cbae817
7
+ data.tar.gz: 820e507831da2346e52f9589b1494cd30d6a83c3f508b6f5b67631e3f48ad5313e050ff04d5b09823116103ccc040d35e327a5b6412e4b975927d77ddb1d07e1
data/.travis.yml CHANGED
@@ -11,7 +11,6 @@ services:
11
11
  - mongodb
12
12
  - redis-server
13
13
  rvm:
14
- - 2.0.0
15
14
  - 2.1.0
16
15
  - 2.2.0
17
16
  - 2.3.0
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.1.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 (1.6.0)
24
- bson (1.6.0-java)
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 (1.6.0)
80
- bson (= 1.6.0)
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 "bson_ext"
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
@@ -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.1.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 (1.6.0)
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 (1.6.0)
125
- bson (= 1.6.0)
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)
@@ -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.1.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 (1.10.0)
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 (1.10.0)
123
- bson (~> 1.10.0)
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)
@@ -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.1.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 (1.10.0)
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 (1.10.0)
137
- bson (~> 1.10.0)
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.1.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 (1.12.5)
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 (1.12.5)
109
- bson (= 1.12.5)
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
- @mongo.connected?
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
- @mongo ||= setup_connection(@options)
54
- @mongo.connect
55
- database = @mongo.db(@options[:database])
56
- database.authenticate @options[:username], @options[:password], true if @options[:username]
57
- @metrics = database.collection("vanity.metrics")
58
- @experiments = database.collection("vanity.experiments")
59
- @participants = database.collection("vanity.participants")
60
- @participants.create_index [[:experiment, 1], [:identity, 1]], :unique=>true
61
- @participants.create_index [[:experiment, 1], [:seen, 1]]
62
- @participants.create_index [[:experiment, 1], [:converted, 1]]
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.find_one(:_id=>metric)
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.update({ :_id=>metric }, { "$inc"=>inc, "$set"=>{ :last_update_at=>Time.now } }, :upsert=>true)
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.find_one(:_id=>metric)
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.remove :_id=>metric
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.find_one({ :_id=>experiment })
107
+ !!@experiments.find(:_id=>experiment).limit(1).first
108
108
  end
109
109
 
110
110
  def set_experiment_created_at(experiment, time)
111
- @experiments.insert(:_id=>experiment, :created_at=>time)
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.find_one({ :_id=>experiment }, { :fields=>[:created_at] })
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.update({ :_id=>experiment }, { "$set"=>{ :completed_at=>time } }, :upsert=>true)
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.find_one({ :_id=>experiment }, { :fields=>[:completed_at] })
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.find_one(:_id=>experiment, :completed_at=>{ "$exists"=>true })
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.update({ :_id=>experiment }, { "$set"=>{ :enabled=>enabled } }, :upsert=>true)
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.find_one({ :_id=>experiment}, { :fields=>[:enabled] })
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.find_one({ :_id=>experiment }, { :fields=>[:conversions] })
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.update({ :experiment=>experiment, :identity=>identity }, { "$set"=>{ :show=>alternative } }, :upsert=>true)
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.find_one({ :experiment=>experiment, :identity=>identity }, { :fields=>[:show] })
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.update({ :experiment=>experiment, :identity=>identity }, { "$unset"=>:show })
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.update({ :experiment=>experiment, :identity=>identity }, { "$push"=>{ :seen=>alternative } }, :upsert=>true)
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.find_one({ :experiment=>experiment, :identity=>identity }, { :fields=>[:seen] })
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.find_one({ :experiment=>experiment, :identity=>identity }, { :fields=>[:seen] })
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.update({ :experiment=>experiment, :identity=>identity }, { "$push"=>{ :seen=>alternative } }, :upsert=>true)
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.find_one(:experiment=>experiment, :identity=>identity, :seen=>alternative)
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
- @participants.update({ :experiment=>experiment, :identity=>identity }, { "$push"=>{ :converted=>alternative } }, :upsert=>true) if implicit || participating
190
- @experiments.update({ :_id=>experiment }, { "$inc"=>{ "conversions.#{alternative}"=>count } }, :upsert=>true)
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.find_one({ :_id=>experiment }, { :fields=>[:outcome] })
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.update({ :_id=>experiment }, { "$set"=>{ :outcome=>alternative } }, :upsert=>true)
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.remove :_id=>experiment
204
- @participants.remove :experiment=>experiment
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
@@ -44,7 +44,7 @@ module Vanity
44
44
  begin
45
45
  redis.client.disconnect
46
46
  rescue Exception => e
47
- warn("Error while disconnecting from redis: #{e.message}")
47
+ Vanity.logger.warn("Error while disconnecting from redis: #{e.message}")
48
48
  end
49
49
  end
50
50
  @redis = nil
@@ -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 'DB has no created_at for this experiment! This most likely means' +
74
- 'you didn\'t call #save before calling enabled=, which you should.'
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 "to use bayes_bandit_score, install integration and rubystats gems"
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 "for better integration performance, install gsl gem"
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 "Error in AbTest#complete!: #{$!}"
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 "Experiment #{name} has already been saved"
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 "No default alternative specified; choosing #{@default} as default."
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 "Attempted to set unknown alternative #{@default} as default! Using #{@alternatives.first} instead."
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 "Please use metrics method to explicitly state which metric you are measuring against."
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 &method(:track!)
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 "Error in Vanity::Experiment::Base: #{$!}"
194
+ rescue => e
195
+ Vanity.logger.warn("Error in Vanity::Experiment::Base: #{e}")
196
196
  end
197
197
  end
198
198
  end
@@ -106,7 +106,7 @@ module Vanity
106
106
  identity ||= if @ar_identity_block
107
107
  @ar_identity_block.call(record)
108
108
  end
109
-
109
+
110
110
  call_hooks record.send(@ar_timestamp), identity, [count] if count > 0 && @ar_scoped.exists?(record.id)
111
111
  end
112
112
  end
@@ -1,5 +1,5 @@
1
1
  module Vanity
2
- VERSION = "2.1.2"
2
+ VERSION = "2.2.0"
3
3
 
4
4
  module Version
5
5
  version = VERSION.to_s.split(".").map { |i| i.to_i }
@@ -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
- redis_adapter.expects(:warn).with("Error while disconnecting from redis: RuntimeError")
17
+ Vanity.logger.expects(:warn).with("Error while disconnecting from redis: RuntimeError")
18
18
  redis_adapter.disconnect!
19
19
  end
20
20
  end
@@ -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
- e.expects(:warn)
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
- e.expects(:warn)
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.1.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-02-23 00:00:00.000000000 Z
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.1.2
216
+ - Vanity 2.2.0
217
217
  - --main
218
218
  - README.md
219
219
  - --webcvs