vanity 3.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
|