vanity 3.1.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/linting.yml +28 -0
- data/.github/workflows/test.yml +3 -6
- data/.rubocop.yml +114 -0
- data/.rubocop_todo.yml +67 -0
- data/Appraisals +9 -31
- data/CHANGELOG +5 -0
- data/Gemfile +7 -3
- data/Gemfile.lock +31 -3
- data/README.md +4 -9
- data/Rakefile +25 -24
- data/bin/vanity +1 -1
- data/doc/configuring.textile +1 -0
- data/gemfiles/rails52.gemfile +6 -3
- data/gemfiles/rails52.gemfile.lock +34 -9
- data/gemfiles/rails60.gemfile +6 -3
- data/gemfiles/rails60.gemfile.lock +34 -9
- data/gemfiles/rails61.gemfile +6 -3
- data/gemfiles/rails61.gemfile.lock +34 -9
- data/lib/generators/vanity/migration_generator.rb +5 -7
- data/lib/vanity/adapters/abstract_adapter.rb +43 -45
- data/lib/vanity/adapters/active_record_adapter.rb +30 -30
- data/lib/vanity/adapters/mock_adapter.rb +14 -18
- data/lib/vanity/adapters/mongodb_adapter.rb +73 -69
- data/lib/vanity/adapters/redis_adapter.rb +19 -27
- data/lib/vanity/adapters.rb +1 -1
- data/lib/vanity/autoconnect.rb +6 -7
- data/lib/vanity/commands/list.rb +7 -7
- data/lib/vanity/commands/report.rb +18 -22
- data/lib/vanity/configuration.rb +19 -19
- data/lib/vanity/connection.rb +12 -14
- data/lib/vanity/experiment/ab_test.rb +82 -70
- data/lib/vanity/experiment/alternative.rb +3 -5
- data/lib/vanity/experiment/base.rb +24 -19
- data/lib/vanity/experiment/bayesian_bandit_score.rb +7 -13
- data/lib/vanity/experiment/definition.rb +6 -6
- data/lib/vanity/frameworks/rails.rb +39 -39
- data/lib/vanity/frameworks.rb +2 -2
- data/lib/vanity/helpers.rb +1 -1
- data/lib/vanity/metric/active_record.rb +21 -19
- data/lib/vanity/metric/base.rb +22 -23
- data/lib/vanity/metric/google_analytics.rb +6 -9
- data/lib/vanity/metric/remote.rb +3 -5
- data/lib/vanity/playground.rb +3 -6
- data/lib/vanity/vanity.rb +8 -12
- data/lib/vanity/version.rb +1 -1
- data/test/adapters/active_record_adapter_test.rb +1 -5
- data/test/adapters/mock_adapter_test.rb +0 -2
- data/test/adapters/mongodb_adapter_test.rb +1 -5
- data/test/adapters/redis_adapter_test.rb +2 -3
- data/test/adapters/shared_tests.rb +9 -12
- data/test/autoconnect_test.rb +3 -3
- data/test/cli_test.rb +0 -1
- data/test/configuration_test.rb +18 -34
- data/test/connection_test.rb +3 -3
- data/test/dummy/Rakefile +1 -1
- data/test/dummy/app/controllers/use_vanity_controller.rb +12 -8
- data/test/dummy/app/mailers/vanity_mailer.rb +3 -3
- data/test/dummy/config/application.rb +1 -1
- data/test/dummy/config/boot.rb +3 -3
- data/test/dummy/config/environment.rb +1 -1
- data/test/dummy/config/environments/development.rb +0 -1
- data/test/dummy/config/environments/test.rb +1 -1
- data/test/dummy/config/initializers/session_store.rb +1 -1
- data/test/dummy/config.ru +1 -1
- data/test/dummy/script/rails +2 -2
- data/test/experiment/ab_test.rb +148 -154
- data/test/experiment/base_test.rb +48 -32
- data/test/frameworks/rails/action_controller_test.rb +25 -25
- data/test/frameworks/rails/action_mailer_test.rb +2 -2
- data/test/frameworks/rails/action_view_test.rb +5 -6
- data/test/frameworks/rails/rails_test.rb +147 -181
- data/test/helper_test.rb +2 -2
- data/test/metric/active_record_test.rb +174 -212
- data/test/metric/base_test.rb +21 -46
- data/test/metric/google_analytics_test.rb +17 -25
- data/test/metric/remote_test.rb +7 -10
- data/test/playground_test.rb +7 -14
- data/test/templates_test.rb +16 -20
- data/test/test_helper.rb +28 -29
- data/test/vanity_test.rb +4 -10
- data/test/web/rails/dashboard_test.rb +21 -21
- data/vanity.gemspec +8 -7
- metadata +28 -30
- data/gemfiles/rails42.gemfile +0 -33
- data/gemfiles/rails42.gemfile.lock +0 -265
- data/gemfiles/rails42_protected_attributes.gemfile +0 -34
- data/gemfiles/rails42_protected_attributes.gemfile.lock +0 -264
- data/gemfiles/rails51.gemfile +0 -33
- data/gemfiles/rails51.gemfile.lock +0 -285
@@ -27,7 +27,7 @@ module Vanity
|
|
27
27
|
send :"find_or_create_by_#{method}", value
|
28
28
|
end
|
29
29
|
rescue ActiveRecord::RecordNotUnique
|
30
|
-
if retried
|
30
|
+
if retried # rubocop:todo Style/GuardClause
|
31
31
|
raise
|
32
32
|
else
|
33
33
|
retried = true
|
@@ -71,7 +71,7 @@ module Vanity
|
|
71
71
|
# Experiment model
|
72
72
|
class VanityExperiment < VanityRecord
|
73
73
|
self.table_name = :vanity_experiments
|
74
|
-
has_many :vanity_conversions, :
|
74
|
+
has_many :vanity_conversions, dependent: :destroy
|
75
75
|
attr_accessible :experiment_id if needs_attr_accessible?
|
76
76
|
|
77
77
|
# Finds or creates the experiment
|
@@ -104,14 +104,14 @@ module Vanity
|
|
104
104
|
def self.retrieve(experiment, identity, create = true, update_with = nil)
|
105
105
|
retried = false
|
106
106
|
begin
|
107
|
-
if record = VanityParticipant.where(:
|
107
|
+
if record = VanityParticipant.where(experiment_id: experiment.to_s, identity: identity.to_s).first # rubocop:todo Lint/AssignmentInCondition
|
108
108
|
record.update(update_with) if update_with
|
109
109
|
elsif create
|
110
|
-
record = VanityParticipant.create({ :
|
110
|
+
record = VanityParticipant.create({ experiment_id: experiment.to_s, identity: identity.to_s }.merge(update_with || {}))
|
111
111
|
end
|
112
112
|
record
|
113
113
|
rescue ActiveRecord::RecordNotUnique => e
|
114
|
-
if retried
|
114
|
+
if retried # rubocop:todo Style/GuardClause
|
115
115
|
raise e
|
116
116
|
else
|
117
117
|
retried = true
|
@@ -121,9 +121,11 @@ module Vanity
|
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
def initialize(options)
|
125
|
-
@options = options.
|
126
|
-
|
124
|
+
def initialize(options) # rubocop:todo Lint/MissingSuper
|
125
|
+
@options = options.each_with_object({}) do |kv, h|
|
126
|
+
h[kv.first.to_s] = kv.last
|
127
|
+
end
|
128
|
+
if @options["active_record_adapter"] && (@options["active_record_adapter"] != "default") # rubocop:todo Style/GuardClause
|
127
129
|
@options["adapter"] = @options["active_record_adapter"]
|
128
130
|
VanityRecord.establish_connection(@options)
|
129
131
|
end
|
@@ -147,7 +149,6 @@ module Vanity
|
|
147
149
|
end
|
148
150
|
end
|
149
151
|
|
150
|
-
|
151
152
|
# -- Metrics --
|
152
153
|
|
153
154
|
def get_metric_last_update_at(metric)
|
@@ -155,11 +156,11 @@ module Vanity
|
|
155
156
|
record && record.updated_at
|
156
157
|
end
|
157
158
|
|
158
|
-
def metric_track(metric, timestamp,
|
159
|
+
def metric_track(metric, timestamp, _identity, values)
|
159
160
|
record = VanityMetric.retrieve(metric)
|
160
161
|
|
161
162
|
values.each_with_index do |value, index|
|
162
|
-
record.vanity_metric_values.create(:
|
163
|
+
record.vanity_metric_values.create(date: timestamp.to_date.to_s, index: index, value: value)
|
163
164
|
end
|
164
165
|
|
165
166
|
record.touch_with_grace_period
|
@@ -171,14 +172,14 @@ module Vanity
|
|
171
172
|
record = VanityMetric.retrieve(metric)
|
172
173
|
dates = (from.to_date..to.to_date).map(&:to_s)
|
173
174
|
conditions = [connection.quote_column_name('date') + ' BETWEEN ? AND ?', from.to_date, to.to_date]
|
174
|
-
order =
|
175
|
+
order = connection.quote_column_name('date').to_s # rubocop:todo Lint/UselessAssignment
|
175
176
|
select = "sum(#{connection.quote_column_name('value')}) AS value, #{connection.quote_column_name('date')}"
|
176
|
-
group_by =
|
177
|
+
group_by = connection.quote_column_name('date').to_s
|
177
178
|
|
178
179
|
values = record.vanity_metric_values.select(select).where(conditions).group(group_by)
|
179
180
|
|
180
181
|
dates.map do |date|
|
181
|
-
value = values.detect{|v| v.date == date }
|
182
|
+
value = values.detect { |v| v.date == date }
|
182
183
|
[(value && value.value) || 0]
|
183
184
|
end
|
184
185
|
end
|
@@ -188,7 +189,6 @@ module Vanity
|
|
188
189
|
record && record.destroy
|
189
190
|
end
|
190
191
|
|
191
|
-
|
192
192
|
# -- Experiments --
|
193
193
|
|
194
194
|
def experiment_persisted?(experiment)
|
@@ -198,7 +198,7 @@ module Vanity
|
|
198
198
|
# Store when experiment was created (do not write over existing value).
|
199
199
|
def set_experiment_created_at(experiment, time)
|
200
200
|
record = VanityExperiment.find_by_experiment_id(experiment.to_s) ||
|
201
|
-
|
201
|
+
VanityExperiment.new(experiment_id: experiment.to_s)
|
202
202
|
record.created_at ||= time
|
203
203
|
record.save
|
204
204
|
end
|
@@ -218,7 +218,7 @@ module Vanity
|
|
218
218
|
end
|
219
219
|
|
220
220
|
# Returns true if experiment completed.
|
221
|
-
def is_experiment_completed?(experiment)
|
221
|
+
def is_experiment_completed?(experiment) # rubocop:todo Naming/PredicateName
|
222
222
|
!!VanityExperiment.retrieve(experiment).completed_at
|
223
223
|
end
|
224
224
|
|
@@ -226,7 +226,7 @@ module Vanity
|
|
226
226
|
VanityExperiment.retrieve(experiment).update_attribute(:enabled, enabled)
|
227
227
|
end
|
228
228
|
|
229
|
-
def is_experiment_enabled?(experiment)
|
229
|
+
def is_experiment_enabled?(experiment) # rubocop:todo Naming/PredicateName
|
230
230
|
record = VanityExperiment.retrieve(experiment)
|
231
231
|
if Vanity.configuration.experiments_start_enabled
|
232
232
|
record.enabled != false
|
@@ -240,21 +240,21 @@ module Vanity
|
|
240
240
|
# :conversions.
|
241
241
|
def ab_counts(experiment, alternative)
|
242
242
|
record = VanityExperiment.retrieve(experiment)
|
243
|
-
participants = VanityParticipant.where(:
|
244
|
-
converted = VanityParticipant.where(:
|
245
|
-
conversions = record.vanity_conversions.where(:
|
243
|
+
participants = VanityParticipant.where(experiment_id: experiment.to_s, seen: alternative).count
|
244
|
+
converted = VanityParticipant.where(experiment_id: experiment.to_s, converted: alternative).count
|
245
|
+
conversions = record.vanity_conversions.where(alternative: alternative).sum(:conversions)
|
246
246
|
|
247
247
|
{
|
248
|
-
:
|
249
|
-
:
|
250
|
-
:
|
248
|
+
participants: participants,
|
249
|
+
converted: converted,
|
250
|
+
conversions: conversions,
|
251
251
|
}
|
252
252
|
end
|
253
253
|
|
254
254
|
# Pick particular alternative (by index) to show to this particular
|
255
255
|
# participant (by identity).
|
256
256
|
def ab_show(experiment, identity, alternative)
|
257
|
-
VanityParticipant.retrieve(experiment, identity, true, :
|
257
|
+
VanityParticipant.retrieve(experiment, identity, true, shown: alternative)
|
258
258
|
end
|
259
259
|
|
260
260
|
# Indicates which alternative to show to this participant. See #ab_show.
|
@@ -266,12 +266,12 @@ module Vanity
|
|
266
266
|
# Cancels previously set association between identity and alternative. See
|
267
267
|
# #ab_show.
|
268
268
|
def ab_not_showing(experiment, identity)
|
269
|
-
VanityParticipant.retrieve(experiment, identity, true, :
|
269
|
+
VanityParticipant.retrieve(experiment, identity, true, shown: nil)
|
270
270
|
end
|
271
271
|
|
272
272
|
# Records a participant in this experiment for the given alternative.
|
273
273
|
def ab_add_participant(experiment, alternative, identity)
|
274
|
-
VanityParticipant.retrieve(experiment, identity, true, :
|
274
|
+
VanityParticipant.retrieve(experiment, identity, true, seen: alternative)
|
275
275
|
end
|
276
276
|
|
277
277
|
# Determines if a participant already has seen this alternative in this experiment.
|
@@ -294,8 +294,8 @@ module Vanity
|
|
294
294
|
# implicit is false (default), only add conversion if participant
|
295
295
|
# previously recorded as participating in this experiment.
|
296
296
|
def ab_add_conversion(experiment, alternative, identity, count = 1, implicit = false)
|
297
|
-
participant = VanityParticipant.retrieve(experiment, identity, false)
|
298
|
-
VanityParticipant.retrieve(experiment, identity, implicit, :
|
297
|
+
participant = VanityParticipant.retrieve(experiment, identity, false) # rubocop:todo Lint/UselessAssignment
|
298
|
+
VanityParticipant.retrieve(experiment, identity, implicit, converted: alternative, seen: alternative)
|
299
299
|
VanityExperiment.retrieve(experiment).increment_conversion(alternative, count)
|
300
300
|
end
|
301
301
|
|
@@ -312,7 +312,7 @@ module Vanity
|
|
312
312
|
|
313
313
|
# Deletes all information about this experiment.
|
314
314
|
def destroy_experiment(experiment)
|
315
|
-
VanityParticipant.where(:
|
315
|
+
VanityParticipant.where(experiment_id: experiment.to_s).delete_all
|
316
316
|
record = VanityExperiment.find_by_experiment_id(experiment.to_s)
|
317
317
|
record && record.destroy
|
318
318
|
end
|
@@ -14,9 +14,9 @@ module Vanity
|
|
14
14
|
#
|
15
15
|
# @since 1.4.0
|
16
16
|
class MockAdapter < AbstractAdapter
|
17
|
-
def initialize(
|
18
|
-
@metrics = @@metrics ||= {}
|
19
|
-
@experiments = @@experiments ||= {}
|
17
|
+
def initialize(_options) # rubocop:todo Lint/MissingSuper
|
18
|
+
@metrics = @@metrics ||= {} # rubocop:todo Style/ClassVars
|
19
|
+
@experiments = @@experiments ||= {} # rubocop:todo Style/ClassVars
|
20
20
|
end
|
21
21
|
|
22
22
|
def active?
|
@@ -42,18 +42,17 @@ module Vanity
|
|
42
42
|
@experiments.clear
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
45
|
# -- Metrics --
|
47
46
|
|
48
47
|
def get_metric_last_update_at(metric)
|
49
48
|
@metrics[metric] && @metrics[metric][:last_update_at]
|
50
49
|
end
|
51
50
|
|
52
|
-
def metric_track(metric, timestamp,
|
51
|
+
def metric_track(metric, timestamp, _identity, values)
|
53
52
|
@metrics[metric] ||= {}
|
54
53
|
current = @metrics[metric][timestamp.to_date] ||= []
|
55
|
-
values.each_with_index do |v,i|
|
56
|
-
current[i] = (current[i] || 0) + v || 0
|
54
|
+
values.each_with_index do |v, i|
|
55
|
+
current[i] = ((current[i] || 0) + v) || 0
|
57
56
|
end
|
58
57
|
@metrics[metric][:last_update_at] = Time.now
|
59
58
|
end
|
@@ -67,7 +66,6 @@ module Vanity
|
|
67
66
|
@metrics.delete metric
|
68
67
|
end
|
69
68
|
|
70
|
-
|
71
69
|
# -- Experiments --
|
72
70
|
|
73
71
|
def experiment_persisted?(experiment)
|
@@ -92,7 +90,7 @@ module Vanity
|
|
92
90
|
@experiments[experiment] && @experiments[experiment][:completed_at]
|
93
91
|
end
|
94
92
|
|
95
|
-
def is_experiment_completed?(experiment)
|
93
|
+
def is_experiment_completed?(experiment) # rubocop:todo Naming/PredicateName
|
96
94
|
@experiments[experiment] && @experiments[experiment][:completed_at]
|
97
95
|
end
|
98
96
|
|
@@ -101,10 +99,10 @@ module Vanity
|
|
101
99
|
@experiments[experiment][:enabled] = enabled
|
102
100
|
end
|
103
101
|
|
104
|
-
def is_experiment_enabled?(experiment)
|
102
|
+
def is_experiment_enabled?(experiment) # rubocop:todo Naming/PredicateName
|
105
103
|
record = @experiments[experiment]
|
106
104
|
if Vanity.configuration.experiments_start_enabled
|
107
|
-
record
|
105
|
+
record.nil? || record[:enabled] != false
|
108
106
|
else
|
109
107
|
record && record[:enabled] == true
|
110
108
|
end
|
@@ -112,9 +110,9 @@ module Vanity
|
|
112
110
|
|
113
111
|
def ab_counts(experiment, alternative)
|
114
112
|
alt = alternative(experiment, alternative)
|
115
|
-
{ :
|
116
|
-
:
|
117
|
-
:
|
113
|
+
{ participants: alt[:participants] ? alt[:participants].size : 0,
|
114
|
+
converted: alt[:converted] ? alt[:converted].size : 0,
|
115
|
+
conversions: alt[:conversions] || 0 }
|
118
116
|
end
|
119
117
|
|
120
118
|
def ab_show(experiment, identity, alternative)
|
@@ -139,9 +137,7 @@ module Vanity
|
|
139
137
|
|
140
138
|
def ab_seen(experiment, identity, alternative_or_id)
|
141
139
|
with_ab_seen_deprecation(experiment, identity, alternative_or_id) do |expt, ident, alt_id|
|
142
|
-
if ab_assigned(expt, ident) == alt_id
|
143
|
-
alt_id
|
144
|
-
end
|
140
|
+
alt_id if ab_assigned(expt, ident) == alt_id
|
145
141
|
end
|
146
142
|
end
|
147
143
|
|
@@ -161,7 +157,7 @@ module Vanity
|
|
161
157
|
if implicit
|
162
158
|
alt[:participants] << identity
|
163
159
|
else
|
164
|
-
|
160
|
+
participating = alt[:participants].include?(identity)
|
165
161
|
end
|
166
162
|
alt[:converted] << identity if implicit || participating
|
167
163
|
alt[:conversions] += count
|
@@ -8,7 +8,7 @@ module Vanity
|
|
8
8
|
require "mongo"
|
9
9
|
MongodbAdapter.new(spec)
|
10
10
|
end
|
11
|
-
alias
|
11
|
+
alias mongodb_connection mongo_connection
|
12
12
|
end
|
13
13
|
|
14
14
|
# MongoDB adapter.
|
@@ -17,7 +17,7 @@ module Vanity
|
|
17
17
|
class MongodbAdapter < AbstractAdapter
|
18
18
|
attr_reader :mongo
|
19
19
|
|
20
|
-
def initialize(options)
|
20
|
+
def initialize(options) # rubocop:todo Lint/MissingSuper
|
21
21
|
@options = options.clone
|
22
22
|
@options[:database] ||= (@options[:path] && @options[:path].split("/")[1]) || "vanity"
|
23
23
|
connect!
|
@@ -28,7 +28,13 @@ module Vanity
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def disconnect!
|
31
|
-
|
31
|
+
if @mongo
|
32
|
+
begin
|
33
|
+
@mongo.close
|
34
|
+
rescue StandardError
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
32
38
|
@metrics, @experiments = nil
|
33
39
|
@mongo = nil
|
34
40
|
end
|
@@ -49,9 +55,9 @@ module Vanity
|
|
49
55
|
@participants = @mongo["vanity.participants"]
|
50
56
|
@participants.create unless @mongo.database.collection_names.include?("vanity.participants")
|
51
57
|
@participants.indexes.create_many(
|
52
|
-
{ :
|
53
|
-
{ :
|
54
|
-
{ :
|
58
|
+
{ key: { experiment: 1, identity: 1 }, unique: true },
|
59
|
+
{ key: { experiment: 1, seen: 1 } },
|
60
|
+
{ key: { experiment: 1, converted: 1 } }
|
55
61
|
)
|
56
62
|
|
57
63
|
@mongo
|
@@ -59,7 +65,7 @@ module Vanity
|
|
59
65
|
|
60
66
|
def to_s
|
61
67
|
userinfo = @options.values_at(:username, :password).join(":") if @options[:username]
|
62
|
-
URI::Generic.build(:
|
68
|
+
URI::Generic.build(scheme: "mongodb", userinfo: userinfo, host: (@mongo.host || @options[:host]), port: (@mongo.port || @options[:port]), path: "/#{@options[:database]}").to_s
|
63
69
|
end
|
64
70
|
|
65
71
|
def flushdb
|
@@ -68,197 +74,195 @@ module Vanity
|
|
68
74
|
@participants.drop
|
69
75
|
end
|
70
76
|
|
71
|
-
|
72
77
|
# -- Metrics --
|
73
78
|
|
74
79
|
def get_metric_last_update_at(metric)
|
75
|
-
record = @metrics.find(:
|
80
|
+
record = @metrics.find(_id: metric).limit(1).first
|
76
81
|
record && record["last_update_at"]
|
77
82
|
end
|
78
83
|
|
79
|
-
def metric_track(metric, timestamp,
|
84
|
+
def metric_track(metric, timestamp, _identity, values)
|
80
85
|
inc = {}
|
81
|
-
values.each_with_index do |v,i|
|
86
|
+
values.each_with_index do |v, i|
|
82
87
|
inc["data.#{timestamp.to_date}.#{i}"] = v
|
83
88
|
end
|
84
|
-
@metrics.find(:
|
89
|
+
@metrics.find(_id: metric).find_one_and_replace(
|
85
90
|
{
|
86
|
-
"$inc"=>inc,
|
87
|
-
"$set"=>{ :
|
91
|
+
"$inc" => inc,
|
92
|
+
"$set" => { last_update_at: Time.now },
|
88
93
|
},
|
89
|
-
:
|
94
|
+
upsert: true
|
90
95
|
)
|
91
96
|
end
|
92
97
|
|
93
98
|
def metric_values(metric, from, to)
|
94
|
-
record = @metrics.find(:
|
95
|
-
data = record && record["data"] || {}
|
99
|
+
record = @metrics.find(_id: metric).limit(1).first
|
100
|
+
data = (record && record["data"]) || {}
|
96
101
|
(from.to_date..to.to_date).map { |date| (data[date.to_s] || {}).values }
|
97
102
|
end
|
98
103
|
|
99
104
|
def destroy_metric(metric)
|
100
|
-
@metrics.find(:
|
105
|
+
@metrics.find(_id: metric).delete_one
|
101
106
|
end
|
102
107
|
|
103
|
-
|
104
108
|
# -- Experiments --
|
105
109
|
|
106
110
|
def experiment_persisted?(experiment)
|
107
|
-
!!@experiments.find(:
|
111
|
+
!!@experiments.find(_id: experiment).limit(1).first
|
108
112
|
end
|
109
113
|
|
110
114
|
def set_experiment_created_at(experiment, time)
|
111
115
|
# @experiments.insert_one(:_id=>experiment, :created_at=>time)
|
112
|
-
@experiments.find(:
|
116
|
+
@experiments.find(_id: experiment).find_one_and_replace(
|
113
117
|
{
|
114
|
-
"$setOnInsert"=>{ :
|
118
|
+
"$setOnInsert" => { created_at: time },
|
115
119
|
},
|
116
|
-
:
|
120
|
+
upsert: true
|
117
121
|
)
|
118
122
|
end
|
119
123
|
|
120
124
|
def get_experiment_created_at(experiment)
|
121
|
-
record = @experiments.find(:
|
125
|
+
record = @experiments.find(_id: experiment).limit(1).projection(created_at: 1).first
|
122
126
|
record && record["created_at"]
|
123
|
-
#Returns nil if either the record or the field doesn't exist
|
127
|
+
# Returns nil if either the record or the field doesn't exist
|
124
128
|
end
|
125
129
|
|
126
130
|
def set_experiment_completed_at(experiment, time)
|
127
|
-
@experiments.find(:
|
131
|
+
@experiments.find(_id: experiment).find_one_and_replace(
|
128
132
|
{
|
129
|
-
"$set"=>{ :
|
133
|
+
"$set" => { completed_at: time },
|
130
134
|
},
|
131
|
-
:
|
135
|
+
upsert: true
|
132
136
|
)
|
133
137
|
end
|
134
138
|
|
135
139
|
def get_experiment_completed_at(experiment)
|
136
|
-
record = @experiments.find(:
|
140
|
+
record = @experiments.find(_id: experiment).limit(1).projection(completed_at: 1).first
|
137
141
|
record && record["completed_at"]
|
138
142
|
end
|
139
143
|
|
140
|
-
def is_experiment_completed?(experiment)
|
141
|
-
!!@experiments.find(:
|
144
|
+
def is_experiment_completed?(experiment) # rubocop:todo Naming/PredicateName
|
145
|
+
!!@experiments.find(_id: experiment, completed_at: { "$exists" => true }).limit(1).first
|
142
146
|
end
|
143
147
|
|
144
148
|
def set_experiment_enabled(experiment, enabled)
|
145
|
-
@experiments.find(:
|
149
|
+
@experiments.find(_id: experiment).find_one_and_replace(
|
146
150
|
{
|
147
|
-
"$set"=>{ :enabled
|
151
|
+
"$set" => { enabled: enabled },
|
148
152
|
},
|
149
|
-
:
|
153
|
+
upsert: true
|
150
154
|
)
|
151
155
|
end
|
152
156
|
|
153
|
-
def is_experiment_enabled?(experiment)
|
154
|
-
record = @experiments.find(:
|
157
|
+
def is_experiment_enabled?(experiment) # rubocop:todo Naming/PredicateName
|
158
|
+
record = @experiments.find(_id: experiment).limit(1).projection(enabled: 1).first
|
155
159
|
if Vanity.configuration.experiments_start_enabled
|
156
|
-
record
|
160
|
+
record.nil? || record["enabled"] != false
|
157
161
|
else
|
158
162
|
record && record["enabled"] == true
|
159
163
|
end
|
160
164
|
end
|
161
165
|
|
162
166
|
def ab_counts(experiment, alternative)
|
163
|
-
record = @experiments.find(:
|
167
|
+
record = @experiments.find(_id: experiment).limit(1).projection(conversions: 1).first
|
164
168
|
conversions = record && record["conversions"]
|
165
|
-
{ :
|
166
|
-
:
|
167
|
-
:
|
169
|
+
{ participants: @participants.find({ experiment: experiment, seen: alternative }).count,
|
170
|
+
converted: @participants.find({ experiment: experiment, converted: alternative }).count,
|
171
|
+
conversions: (conversions && conversions[alternative.to_s]) || 0 }
|
168
172
|
end
|
169
173
|
|
170
174
|
def ab_show(experiment, identity, alternative)
|
171
|
-
@participants.find(:experiment
|
175
|
+
@participants.find(experiment: experiment, identity: identity).find_one_and_replace(
|
172
176
|
{
|
173
|
-
"$set"=>{ :
|
177
|
+
"$set" => { show: alternative },
|
174
178
|
},
|
175
|
-
:
|
179
|
+
upsert: true
|
176
180
|
)
|
177
181
|
end
|
178
182
|
|
179
183
|
def ab_showing(experiment, identity)
|
180
|
-
participant = @participants.find(:experiment
|
184
|
+
participant = @participants.find(experiment: experiment, identity: identity).limit(1).projection(show: 1).first
|
181
185
|
participant && participant["show"]
|
182
186
|
end
|
183
187
|
|
184
188
|
def ab_not_showing(experiment, identity)
|
185
|
-
@participants.find(:experiment
|
189
|
+
@participants.find(experiment: experiment, identity: identity).find_one_and_replace(
|
186
190
|
{
|
187
|
-
"$unset"=> { :
|
191
|
+
"$unset" => { show: "" },
|
188
192
|
},
|
189
|
-
:
|
193
|
+
upsert: true
|
190
194
|
)
|
191
195
|
end
|
192
196
|
|
193
197
|
def ab_add_participant(experiment, alternative, identity)
|
194
|
-
@participants.find(:experiment
|
198
|
+
@participants.find(experiment: experiment, identity: identity).find_one_and_replace(
|
195
199
|
{
|
196
|
-
"$push"=>{ :
|
200
|
+
"$push" => { seen: alternative },
|
197
201
|
},
|
198
|
-
:
|
202
|
+
upsert: true
|
199
203
|
)
|
200
204
|
end
|
201
205
|
|
202
206
|
# Determines if a participant already has seen this alternative in this experiment.
|
203
207
|
def ab_seen(experiment, identity, alternative_or_id)
|
204
208
|
with_ab_seen_deprecation(experiment, identity, alternative_or_id) do |expt, ident, alt_id|
|
205
|
-
participant = @participants.find(:
|
209
|
+
participant = @participants.find(experiment: expt, identity: ident).limit(1).projection(seen: 1).first
|
206
210
|
participant && participant["seen"].first == alt_id
|
207
211
|
end
|
208
212
|
end
|
209
213
|
|
210
214
|
# Returns the participant's seen alternative in this experiment, if it exists
|
211
215
|
def ab_assigned(experiment, identity)
|
212
|
-
participant = @participants.find(:experiment
|
216
|
+
participant = @participants.find(experiment: experiment, identity: identity).limit(1).projection(seen: 1).first
|
213
217
|
participant && participant["seen"].first
|
214
218
|
end
|
215
219
|
|
216
220
|
def ab_add_conversion(experiment, alternative, identity, count = 1, implicit = false)
|
217
221
|
if implicit
|
218
|
-
@participants.find(:experiment
|
222
|
+
@participants.find(experiment: experiment, identity: identity).find_one_and_replace(
|
219
223
|
{
|
220
|
-
"$push"=>{ :
|
224
|
+
"$push" => { seen: alternative },
|
221
225
|
},
|
222
|
-
:
|
226
|
+
upsert: true
|
223
227
|
)
|
224
228
|
else
|
225
|
-
participating = @participants.find(:experiment
|
229
|
+
participating = @participants.find(experiment: experiment, identity: identity, seen: alternative).limit(1).first
|
226
230
|
end
|
227
231
|
|
228
232
|
if implicit || participating
|
229
|
-
@participants.find(:experiment
|
233
|
+
@participants.find(experiment: experiment, identity: identity).find_one_and_replace(
|
230
234
|
{
|
231
|
-
"$push"=>{ :
|
235
|
+
"$push" => { converted: alternative },
|
232
236
|
},
|
233
|
-
:
|
237
|
+
upsert: true
|
234
238
|
)
|
235
239
|
end
|
236
240
|
|
237
|
-
@experiments.find(:
|
241
|
+
@experiments.find(_id: experiment).find_one_and_replace(
|
238
242
|
{
|
239
|
-
"$inc"=>{ "conversions.#{alternative}"=>count }
|
243
|
+
"$inc" => { "conversions.#{alternative}" => count },
|
240
244
|
},
|
241
|
-
:
|
245
|
+
upsert: true
|
242
246
|
)
|
243
247
|
end
|
244
248
|
|
245
249
|
def ab_get_outcome(experiment)
|
246
|
-
experiment = @experiments.find(:
|
250
|
+
experiment = @experiments.find(_id: experiment).limit(1).projection(outcome: 1).first
|
247
251
|
experiment && experiment["outcome"]
|
248
252
|
end
|
249
253
|
|
250
254
|
def ab_set_outcome(experiment, alternative = 0)
|
251
|
-
@experiments.find(:
|
255
|
+
@experiments.find(_id: experiment).find_one_and_replace(
|
252
256
|
{
|
253
|
-
"$set"=>{ :
|
257
|
+
"$set" => { outcome: alternative },
|
254
258
|
},
|
255
|
-
:
|
259
|
+
upsert: true
|
256
260
|
)
|
257
261
|
end
|
258
262
|
|
259
263
|
def destroy_experiment(experiment)
|
260
|
-
@experiments.find(:
|
261
|
-
@participants.find(:experiment
|
264
|
+
@experiments.find(_id: experiment).delete_one
|
265
|
+
@participants.find(experiment: experiment).delete_many
|
262
266
|
end
|
263
267
|
|
264
268
|
private
|