vanity 1.8.4 → 1.9.0.beta
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.
- 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>
|