vanity 1.8.4 → 1.9.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -2
- data/CHANGELOG +12 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +12 -10
- data/README.rdoc +45 -16
- data/Rakefile +14 -9
- data/doc/_layouts/page.html +4 -6
- data/doc/ab_testing.textile +1 -1
- data/doc/configuring.textile +2 -4
- data/doc/email.textile +1 -3
- data/doc/index.textile +3 -63
- data/doc/rails.textile +34 -8
- data/gemfiles/rails3.gemfile +12 -3
- data/gemfiles/rails3.gemfile.lock +37 -11
- data/gemfiles/rails31.gemfile +12 -3
- data/gemfiles/rails31.gemfile.lock +37 -11
- data/gemfiles/rails32.gemfile +12 -3
- data/gemfiles/rails32.gemfile.lock +37 -11
- data/gemfiles/rails4.gemfile +12 -3
- data/gemfiles/rails4.gemfile.lock +37 -11
- data/lib/vanity/adapters/abstract_adapter.rb +4 -0
- data/lib/vanity/adapters/active_record_adapter.rb +18 -10
- data/lib/vanity/adapters/mock_adapter.rb +8 -4
- data/lib/vanity/adapters/mongodb_adapter.rb +11 -7
- data/lib/vanity/adapters/redis_adapter.rb +88 -37
- data/lib/vanity/commands/report.rb +9 -9
- data/lib/vanity/experiment/ab_test.rb +120 -101
- data/lib/vanity/experiment/alternative.rb +21 -21
- data/lib/vanity/experiment/base.rb +5 -5
- data/lib/vanity/experiment/bayesian_bandit_score.rb +51 -51
- data/lib/vanity/experiment/definition.rb +10 -10
- data/lib/vanity/frameworks/rails.rb +39 -36
- data/lib/vanity/helpers.rb +6 -4
- data/lib/vanity/metric/active_record.rb +1 -1
- data/lib/vanity/metric/base.rb +23 -24
- data/lib/vanity/metric/google_analytics.rb +5 -5
- data/lib/vanity/playground.rb +118 -24
- data/lib/vanity/templates/_report.erb +20 -6
- data/lib/vanity/templates/vanity.css +2 -0
- data/lib/vanity/version.rb +1 -1
- data/test/adapters/redis_adapter_test.rb +106 -1
- data/test/dummy/config/database.yml +21 -4
- data/test/dummy/config/routes.rb +1 -1
- data/test/experiment/ab_test.rb +93 -13
- data/test/metric/active_record_test.rb +9 -4
- data/test/passenger_test.rb +43 -42
- data/test/playground_test.rb +50 -1
- data/test/rails_dashboard_test.rb +38 -1
- data/test/rails_helper_test.rb +5 -0
- data/test/rails_test.rb +66 -15
- data/test/test_helper.rb +24 -2
- data/vanity.gemspec +0 -2
- metadata +45 -57
data/lib/vanity/helpers.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
module Vanity
|
1
|
+
module Vanity
|
2
2
|
# Helper methods available on Object.
|
3
3
|
#
|
4
4
|
# @example From ERB template
|
@@ -17,7 +17,7 @@ module Vanity
|
|
17
17
|
# end
|
18
18
|
# end
|
19
19
|
module Helpers
|
20
|
-
|
20
|
+
|
21
21
|
# This method returns one of the alternative values in the named A/B test.
|
22
22
|
#
|
23
23
|
# @example A/B two alternatives for a page
|
@@ -34,12 +34,14 @@ module Vanity
|
|
34
34
|
# end
|
35
35
|
# @since 1.2.0
|
36
36
|
def ab_test(name, &block)
|
37
|
+
# TODO refactor with Vanity::Rails::Helpers#ab_test
|
38
|
+
request = respond_to?(:request) ? self.request : nil
|
37
39
|
if Vanity.playground.using_js?
|
38
40
|
@_vanity_experiments ||= {}
|
39
|
-
@_vanity_experiments[name] ||= Vanity.playground.experiment(name).choose
|
41
|
+
@_vanity_experiments[name] ||= Vanity.playground.experiment(name).choose(request)
|
40
42
|
value = @_vanity_experiments[name].value
|
41
43
|
else
|
42
|
-
value = Vanity.playground.experiment(name).choose.value
|
44
|
+
value = Vanity.playground.experiment(name).choose(request).value
|
43
45
|
end
|
44
46
|
|
45
47
|
if block
|
@@ -3,7 +3,7 @@ module Vanity
|
|
3
3
|
|
4
4
|
AGGREGATES = [:average, :minimum, :maximum, :sum]
|
5
5
|
|
6
|
-
# Use an ActiveRecord model to get metric data from database table.
|
6
|
+
# Use an ActiveRecord model to get metric data from database table. Also
|
7
7
|
# forwards +after_create+ callbacks to hooks (updating experiments).
|
8
8
|
#
|
9
9
|
# Supported options:
|
data/lib/vanity/metric/base.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Vanity
|
2
2
|
|
3
|
-
# A metric is an object that implements two methods: +name+ and +values+.
|
3
|
+
# A metric is an object that implements two methods: +name+ and +values+. It
|
4
4
|
# can also respond to addition methods (+track!+, +bounds+, etc), these are
|
5
5
|
# optional.
|
6
6
|
#
|
7
7
|
# This class implements a basic metric that tracks data and stores it in the
|
8
|
-
# database.
|
8
|
+
# database. You can use this as the basis for your metric, or as reference
|
9
9
|
# for the methods your metric must and can implement.
|
10
10
|
#
|
11
11
|
# @since 1.1.0
|
@@ -20,7 +20,7 @@ module Vanity
|
|
20
20
|
# description "Most boring metric ever"
|
21
21
|
# end
|
22
22
|
module Definition
|
23
|
-
|
23
|
+
|
24
24
|
attr_reader :playground
|
25
25
|
|
26
26
|
# Defines a new metric, using the class Vanity::Metric.
|
@@ -37,7 +37,7 @@ module Vanity
|
|
37
37
|
end
|
38
38
|
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Startup metrics for pirates. AARRR stands for:
|
42
42
|
# * Acquisition
|
43
43
|
# * Activation
|
@@ -45,15 +45,14 @@ module Vanity
|
|
45
45
|
# * Referral
|
46
46
|
# * Revenue
|
47
47
|
# Read more: http://500hats.typepad.com/500blogs/2007/09/startup-metrics.html
|
48
|
-
|
49
48
|
class << self
|
50
49
|
|
51
50
|
# Helper method to return description for a metric.
|
52
51
|
#
|
53
52
|
# A metric object may have a +description+ method that returns a detailed
|
54
|
-
# description.
|
53
|
+
# description. It may also have no description, or no +description+
|
55
54
|
# method, in which case return +nil+.
|
56
|
-
#
|
55
|
+
#
|
57
56
|
# @example
|
58
57
|
# puts Vanity::Metric.description(metric)
|
59
58
|
def description(metric)
|
@@ -63,25 +62,25 @@ module Vanity
|
|
63
62
|
# Helper method to return bounds for a metric.
|
64
63
|
#
|
65
64
|
# A metric object may have a +bounds+ method that returns lower and upper
|
66
|
-
# bounds.
|
65
|
+
# bounds. It may also have no bounds, or no +bounds+ # method, in which
|
67
66
|
# case we return +[nil, nil]+.
|
68
|
-
#
|
67
|
+
#
|
69
68
|
# @example
|
70
69
|
# upper = Vanity::Metric.bounds(metric).last
|
71
70
|
def bounds(metric)
|
72
71
|
metric.respond_to?(:bounds) && metric.bounds || [nil, nil]
|
73
72
|
end
|
74
73
|
|
75
|
-
# Returns data set for a given date range.
|
74
|
+
# Returns data set for a given date range. The data set is an array of
|
76
75
|
# date, value pairs.
|
77
76
|
#
|
78
|
-
# First argument is the metric.
|
79
|
-
# number of days to go back in history, defaults to 90 days.
|
77
|
+
# First argument is the metric. Second argument is the start date, or
|
78
|
+
# number of days to go back in history, defaults to 90 days. Third
|
80
79
|
# argument is end date, defaults to today.
|
81
80
|
#
|
82
81
|
# @example These are all equivalent:
|
83
|
-
# Vanity::Metric.data(my_metric)
|
84
|
-
# Vanity::Metric.data(my_metric, 90)
|
82
|
+
# Vanity::Metric.data(my_metric)
|
83
|
+
# Vanity::Metric.data(my_metric, 90)
|
85
84
|
# Vanity::Metric.data(my_metric, Date.today - 89)
|
86
85
|
# Vanity::Metric.data(my_metric, Date.today - 89, Date.today)
|
87
86
|
def data(metric, *args)
|
@@ -158,7 +157,7 @@ module Vanity
|
|
158
157
|
end
|
159
158
|
protected :track_args
|
160
159
|
|
161
|
-
# Metric definitions use this to introduce tracking hook.
|
160
|
+
# Metric definitions use this to introduce tracking hook. The hook is
|
162
161
|
# called with metric identifier, timestamp, count and possibly additional
|
163
162
|
# arguments.
|
164
163
|
#
|
@@ -171,27 +170,27 @@ module Vanity
|
|
171
170
|
end
|
172
171
|
|
173
172
|
# This method returns the acceptable bounds of a metric as an array with
|
174
|
-
# two values: low and high.
|
173
|
+
# two values: low and high. Use nil for unbounded.
|
175
174
|
#
|
176
|
-
# Alerts are created when metric values exceed their bounds.
|
175
|
+
# Alerts are created when metric values exceed their bounds. For example,
|
177
176
|
# a metric of user registration can use historical data to calculate
|
178
|
-
# expected range of new registration for the next day.
|
177
|
+
# expected range of new registration for the next day. If actual metric
|
179
178
|
# falls below the expected range, it could indicate registration process is
|
180
|
-
# broken.
|
179
|
+
# broken. Going above higher bound could trigger opening a Champagne
|
181
180
|
# bottle.
|
182
181
|
#
|
183
182
|
# The default implementation returns +nil+.
|
184
183
|
def bounds
|
185
184
|
end
|
186
|
-
|
185
|
+
|
187
186
|
|
188
187
|
# -- Reporting --
|
189
|
-
|
190
|
-
# Human readable metric name.
|
188
|
+
|
189
|
+
# Human readable metric name. All metrics must implement this method.
|
191
190
|
attr_reader :name
|
192
191
|
alias :to_s :name
|
193
192
|
|
194
|
-
# Human readable description.
|
193
|
+
# Human readable description. Use two newlines to break paragraphs.
|
195
194
|
attr_accessor :description
|
196
195
|
|
197
196
|
# Sets or returns description. For example
|
@@ -206,7 +205,7 @@ module Vanity
|
|
206
205
|
end
|
207
206
|
|
208
207
|
# Given two arguments, a start date and an end date (inclusive), returns an
|
209
|
-
# array of measurements.
|
208
|
+
# array of measurements. All metrics must implement this method.
|
210
209
|
def values(from, to)
|
211
210
|
values = connection.metric_values(@id, from, to)
|
212
211
|
values.map { |row| row.first.to_i }
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Vanity
|
2
2
|
class Metric
|
3
|
-
|
4
|
-
# Use Google Analytics metric.
|
3
|
+
|
4
|
+
# Use Google Analytics metric. Note: you must +require "garb"+ before
|
5
5
|
# vanity.
|
6
|
-
#
|
6
|
+
#
|
7
7
|
# @example Page views
|
8
8
|
# metric "Page views" do
|
9
9
|
# google_analytics "UA-1828623-6"
|
@@ -40,7 +40,7 @@ module Vanity
|
|
40
40
|
end
|
41
41
|
(from..to).map { |day| data[day.strftime('%Y%m%d')] || 0 }
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
# Hooks not supported for GA metrics.
|
45
45
|
def hook
|
46
46
|
fail "Cannot use hooks with Google Analytics methods"
|
@@ -59,7 +59,7 @@ module Vanity
|
|
59
59
|
end
|
60
60
|
|
61
61
|
class Resource
|
62
|
-
# GA profile used for this report.
|
62
|
+
# GA profile used for this report. Populated after calling results.
|
63
63
|
attr_reader :profile
|
64
64
|
|
65
65
|
def initialize(web_property_id, metric)
|
data/lib/vanity/playground.rb
CHANGED
@@ -16,7 +16,7 @@ module Vanity
|
|
16
16
|
# Vanity.playground.
|
17
17
|
#
|
18
18
|
# First argument is connection specification (see #redis=), last argument is
|
19
|
-
# a set of options, both are optional.
|
19
|
+
# a set of options, both are optional. Supported options are:
|
20
20
|
# - connection -- Connection specification
|
21
21
|
# - namespace -- Namespace to use
|
22
22
|
# - load_path -- Path to load experiments/metrics from
|
@@ -26,7 +26,7 @@ module Vanity
|
|
26
26
|
options = Hash === args.last ? args.pop : {}
|
27
27
|
# In the case of Rails, use the Rails logger and collect only for
|
28
28
|
# production environment by default.
|
29
|
-
defaults = options[:rails] ? DEFAULTS.merge(:collecting =>
|
29
|
+
defaults = options[:rails] ? DEFAULTS.merge(:collecting => true, :logger => ::Rails.logger) : DEFAULTS
|
30
30
|
if config_file_exists?
|
31
31
|
env = ENV["RACK_ENV"] || ENV["RAILS_ENV"] || "development"
|
32
32
|
config = load_config_file[env]
|
@@ -52,6 +52,7 @@ module Vanity
|
|
52
52
|
|
53
53
|
@loading = []
|
54
54
|
@use_js = false
|
55
|
+
@failover_on_datastore_error = false
|
55
56
|
self.add_participant_path = DEFAULT_ADD_PARTICIPANT_PATH
|
56
57
|
@collecting = !!@options[:collecting]
|
57
58
|
end
|
@@ -65,9 +66,13 @@ module Vanity
|
|
65
66
|
# Logger.
|
66
67
|
attr_accessor :logger
|
67
68
|
|
68
|
-
# Path to the add_participant action
|
69
|
+
# Path to the add_participant action.
|
69
70
|
attr_accessor :add_participant_path
|
70
71
|
|
72
|
+
attr_accessor :on_datastore_error
|
73
|
+
|
74
|
+
attr_accessor :request_filter
|
75
|
+
|
71
76
|
# Defines a new experiment. Generally, do not call this directly,
|
72
77
|
# use one of the definition methods (ab_test, measure, etc).
|
73
78
|
#
|
@@ -87,7 +92,6 @@ module Vanity
|
|
87
92
|
# an exception if it cannot load the experiment's definition.
|
88
93
|
#
|
89
94
|
# @see Vanity::Experiment
|
90
|
-
|
91
95
|
def experiment(name)
|
92
96
|
id = name.to_s.downcase.gsub(/\W/, "_").to_sym
|
93
97
|
warn "Deprecated: pleae call experiment method with experiment identifier (a Ruby symbol)" unless id == name
|
@@ -102,33 +106,35 @@ module Vanity
|
|
102
106
|
# when converted to_s (so could be used for caching, for example)
|
103
107
|
def participant_info(participant_id)
|
104
108
|
participant_array = []
|
105
|
-
experiments.values.sort_by
|
109
|
+
experiments.values.sort_by(&:name).each do |e|
|
106
110
|
index = connection.ab_assigned(e.id, participant_id)
|
107
111
|
if index
|
108
112
|
participant_array << [e, e.alternatives[index.to_i]]
|
109
113
|
end
|
110
114
|
end
|
111
|
-
|
115
|
+
participant_array
|
112
116
|
end
|
113
117
|
|
118
|
+
|
114
119
|
# -- Robot Detection --
|
115
120
|
|
116
|
-
# Call to indicate that participants should be added via js
|
117
|
-
#
|
118
|
-
# and skewing results.
|
121
|
+
# Call to indicate that participants should be added via js. This helps
|
122
|
+
# keep robots from participating in the A/B test and skewing results.
|
119
123
|
#
|
120
|
-
# If you use this
|
121
|
-
# - Set Vanity.playground.add_participant_path = '/path/to/vanity/action',
|
122
|
-
# this should point to the add_participant path that is added with
|
123
|
-
# Vanity::Rails::Dashboard, make sure that this action is available
|
124
|
-
# to all users
|
124
|
+
# If you want to use this:
|
125
125
|
# - Add <%= vanity_js %> to any page that needs uses an ab_test. vanity_js
|
126
126
|
# needs to be included after your call to ab_test so that it knows which
|
127
|
-
# version of the experiment the participant is a member of.
|
127
|
+
# version of the experiment the participant is a member of. The helper
|
128
128
|
# will render nothing if the there are no ab_tests running on the current
|
129
129
|
# page, so adding vanity_js to the bottom of your layouts is a good
|
130
|
-
# option.
|
130
|
+
# option. Keep in mind that if you call use_js! and don't include
|
131
131
|
# vanity_js in your view no participants will be recorded.
|
132
|
+
#
|
133
|
+
# Note that a custom JS callback path can be set using:
|
134
|
+
# - Set Vanity.playground.add_participant_path = '/path/to/vanity/action',
|
135
|
+
# this should point to the add_participant path that is added with
|
136
|
+
# Vanity::Rails::Dashboard, make sure that this action is available
|
137
|
+
# to all users.
|
132
138
|
def use_js!
|
133
139
|
@use_js = true
|
134
140
|
end
|
@@ -138,7 +144,91 @@ module Vanity
|
|
138
144
|
end
|
139
145
|
|
140
146
|
|
141
|
-
#
|
147
|
+
# -- Datastore graceful failover --
|
148
|
+
|
149
|
+
# Turns on passing of errors to the Proc returned by #on_datastore_error.
|
150
|
+
# Call Vanity.playground.failover_on_datastore_error! to turn this on.
|
151
|
+
#
|
152
|
+
# @since 1.9.0
|
153
|
+
def failover_on_datastore_error!
|
154
|
+
@failover_on_datastore_error = true
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns whether to failover on an error raise by the datastore adapter.
|
158
|
+
#
|
159
|
+
# @since 1.9.0
|
160
|
+
def failover_on_datastore_error?
|
161
|
+
@failover_on_datastore_error
|
162
|
+
end
|
163
|
+
|
164
|
+
# Must return a Proc that accepts as parameters: the thrown error, the
|
165
|
+
# calling Class, the calling method, and an array of arguments passed to
|
166
|
+
# the calling method. The return value is ignored.
|
167
|
+
#
|
168
|
+
# Proc.new do |error, klass, method, arguments|
|
169
|
+
# ...
|
170
|
+
# end
|
171
|
+
#
|
172
|
+
# The default implementation logs this information to Playground#logger.
|
173
|
+
#
|
174
|
+
# Set a custom action by calling Vanity.playground.on_datastore_error =
|
175
|
+
# Proc.new { ... }.
|
176
|
+
#
|
177
|
+
# @since 1.9.0
|
178
|
+
def on_datastore_error
|
179
|
+
@on_datastore_error || default_on_datastore_error
|
180
|
+
end
|
181
|
+
|
182
|
+
def default_on_datastore_error # :nodoc:
|
183
|
+
Proc.new do |error, klass, method, arguments|
|
184
|
+
log = "[#{Time.now.iso8601}]"
|
185
|
+
log << " [vanity #{klass} #{method}]"
|
186
|
+
log << " [#{error.message}]"
|
187
|
+
log << " [#{arguments.join(' ')}]"
|
188
|
+
@logger.error(log)
|
189
|
+
nil
|
190
|
+
end
|
191
|
+
end
|
192
|
+
protected :default_on_datastore_error
|
193
|
+
|
194
|
+
|
195
|
+
# -- Blocking or ignoring visitors --
|
196
|
+
|
197
|
+
# Must return a Proc that accepts as a parameter the request object, if
|
198
|
+
# made available by the implement framework. The return value should be a
|
199
|
+
# boolean whether to ignore the request. This is called only for the JS
|
200
|
+
# callback action.
|
201
|
+
#
|
202
|
+
# Proc.new do |request|
|
203
|
+
# ...
|
204
|
+
# end
|
205
|
+
#
|
206
|
+
# The default implementation does a simple test of whether the request's
|
207
|
+
# HTTP_USER_AGENT header contains a URI, since well behaved bots typically
|
208
|
+
# include a reference URI in their user agent strings. (Original idea:
|
209
|
+
# http://stackoverflow.com/a/9285889.)
|
210
|
+
#
|
211
|
+
# Alternatively, one could filter an explicit list of IPs, add additional
|
212
|
+
# user agent strings to filter, or any custom test. Set a custom filter
|
213
|
+
# by calling Vanity.playground.request_filter = Proc.new { ... }.
|
214
|
+
#
|
215
|
+
# @since 1.9.0
|
216
|
+
def request_filter
|
217
|
+
@request_filter || default_request_filter
|
218
|
+
end
|
219
|
+
|
220
|
+
def default_request_filter # :nodoc:
|
221
|
+
Proc.new do |request|
|
222
|
+
request &&
|
223
|
+
request.env &&
|
224
|
+
request.env["HTTP_USER_AGENT"] &&
|
225
|
+
request.env["HTTP_USER_AGENT"].match(/\(.*https?:\/\/.*\)/)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
protected :default_request_filter
|
229
|
+
|
230
|
+
# Returns hash of experiments (key is experiment id). This create the
|
231
|
+
# Experiment and persists it to the datastore.
|
142
232
|
#
|
143
233
|
# @see Vanity::Experiment
|
144
234
|
def experiments
|
@@ -153,7 +243,11 @@ module Vanity
|
|
153
243
|
@experiments
|
154
244
|
end
|
155
245
|
|
156
|
-
|
246
|
+
def experiments_persisted?
|
247
|
+
experiments.keys.all? { |id| connection.experiment_persisted?(id) }
|
248
|
+
end
|
249
|
+
|
250
|
+
# Reloads all metrics and experiments. Rails calls this for each request in
|
157
251
|
# development mode.
|
158
252
|
def reload!
|
159
253
|
@experiments = nil
|
@@ -161,7 +255,7 @@ module Vanity
|
|
161
255
|
load!
|
162
256
|
end
|
163
257
|
|
164
|
-
# Loads all metrics and experiments.
|
258
|
+
# Loads all metrics and experiments. Rails calls this during
|
165
259
|
# initialization.
|
166
260
|
def load!
|
167
261
|
experiments
|
@@ -323,9 +417,7 @@ module Vanity
|
|
323
417
|
establish_connection(@spec)
|
324
418
|
end
|
325
419
|
|
326
|
-
# Deprecated. Use Vanity.playground.collecting = true/false instead.
|
327
|
-
# Rails, collecting is true in production environment, false in all other
|
328
|
-
# environments, which is exactly what you want.
|
420
|
+
# Deprecated. Use Vanity.playground.collecting = true/false instead.
|
329
421
|
def test!
|
330
422
|
warn "Deprecated: use collecting = false instead"
|
331
423
|
self.collecting = false
|
@@ -364,6 +456,8 @@ module Vanity
|
|
364
456
|
if connection_spec
|
365
457
|
connection_spec = "redis://" + connection_spec unless connection_spec[/^\w+:/]
|
366
458
|
establish_connection connection_spec
|
459
|
+
else
|
460
|
+
establish_connection
|
367
461
|
end
|
368
462
|
end
|
369
463
|
end
|
@@ -384,13 +478,13 @@ module Vanity
|
|
384
478
|
@playground ||= Playground.new(:rails=>defined?(::Rails))
|
385
479
|
end
|
386
480
|
|
387
|
-
# Returns the Vanity context.
|
481
|
+
# Returns the Vanity context. For example, when using Rails this would be
|
388
482
|
# the current controller, which can be used to get/set the vanity identity.
|
389
483
|
def context
|
390
484
|
Thread.current[:vanity_context]
|
391
485
|
end
|
392
486
|
|
393
|
-
# Sets the Vanity context.
|
487
|
+
# Sets the Vanity context. For example, when using Rails this would be
|
394
488
|
# set by the set_vanity_context before filter (via Vanity::Rails#use_vanity).
|
395
489
|
def context=(context)
|
396
490
|
Thread.current[:vanity_context] = context
|
@@ -13,14 +13,28 @@
|
|
13
13
|
</head>
|
14
14
|
<body>
|
15
15
|
<div class="vanity">
|
16
|
-
<%
|
17
|
-
<
|
18
|
-
|
16
|
+
<% unless Vanity.playground.collecting? %>
|
17
|
+
<div class="alert">
|
18
|
+
Vanity is currently not collecting data or metrics. To turn on data collection, set <span style='font-family: courier'>Vanity.playground.collecting = true;</span> in <span style='font-family: courier'>config/environments/[environment].rb</span>.
|
19
|
+
</div>
|
19
20
|
<% end %>
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
|
22
|
+
<% if @experiments_persisted %>
|
23
|
+
<% if @experiments.present? %>
|
24
|
+
<h2>Experiments</h2>
|
25
|
+
<%= render :file=>Vanity.template("_experiments"), :locals=>{:experiments=>@experiments} %>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
<% unless @metrics.empty? %>
|
29
|
+
<h2>Metrics</h2>
|
30
|
+
<%= render :file=>Vanity.template("_metrics"), :locals=>{:metrics=>@metrics, :experiments=>@experiments} %>
|
31
|
+
<% end %>
|
32
|
+
<% else %>
|
33
|
+
<div class="alert">
|
34
|
+
Vanity's cached experiments are out of sync with those on the filesystem and/or those in the datastore. Please restart your server and/or turn on collecting.
|
35
|
+
</div>
|
23
36
|
<% end %>
|
37
|
+
|
24
38
|
<p class="footer">Generated by <a href="http://vanity.labnotes.org">Vanity</a></p>
|
25
39
|
</div>
|
26
40
|
</body>
|