mikeg-vanity 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +153 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +83 -0
- data/bin/vanity +53 -0
- data/lib/vanity.rb +38 -0
- data/lib/vanity/backport.rb +43 -0
- data/lib/vanity/commands.rb +2 -0
- data/lib/vanity/commands/list.rb +21 -0
- data/lib/vanity/commands/report.rb +60 -0
- data/lib/vanity/experiment/ab_test.rb +477 -0
- data/lib/vanity/experiment/base.rb +212 -0
- data/lib/vanity/helpers.rb +59 -0
- data/lib/vanity/metric/active_record.rb +77 -0
- data/lib/vanity/metric/base.rb +221 -0
- data/lib/vanity/metric/google_analytics.rb +70 -0
- data/lib/vanity/mock_redis.rb +76 -0
- data/lib/vanity/playground.rb +197 -0
- data/lib/vanity/rails.rb +22 -0
- data/lib/vanity/rails/dashboard.rb +24 -0
- data/lib/vanity/rails/helpers.rb +158 -0
- data/lib/vanity/rails/testing.rb +11 -0
- data/lib/vanity/templates/_ab_test.erb +26 -0
- data/lib/vanity/templates/_experiment.erb +5 -0
- data/lib/vanity/templates/_experiments.erb +7 -0
- data/lib/vanity/templates/_metric.erb +14 -0
- data/lib/vanity/templates/_metrics.erb +13 -0
- data/lib/vanity/templates/_report.erb +27 -0
- data/lib/vanity/templates/flot.min.js +1 -0
- data/lib/vanity/templates/jquery.min.js +19 -0
- data/lib/vanity/templates/vanity.css +26 -0
- data/lib/vanity/templates/vanity.js +82 -0
- data/test/ab_test_test.rb +656 -0
- data/test/experiment_test.rb +136 -0
- data/test/experiments/age_and_zipcode.rb +19 -0
- data/test/experiments/metrics/cheers.rb +3 -0
- data/test/experiments/metrics/signups.rb +2 -0
- data/test/experiments/metrics/yawns.rb +3 -0
- data/test/experiments/null_abc.rb +5 -0
- data/test/metric_test.rb +518 -0
- data/test/playground_test.rb +10 -0
- data/test/rails_test.rb +104 -0
- data/test/test_helper.rb +135 -0
- data/vanity.gemspec +18 -0
- data/vendor/redis-rb/LICENSE +20 -0
- data/vendor/redis-rb/README.markdown +36 -0
- data/vendor/redis-rb/Rakefile +62 -0
- data/vendor/redis-rb/bench.rb +44 -0
- data/vendor/redis-rb/benchmarking/suite.rb +24 -0
- data/vendor/redis-rb/benchmarking/worker.rb +71 -0
- data/vendor/redis-rb/bin/distredis +33 -0
- data/vendor/redis-rb/examples/basic.rb +16 -0
- data/vendor/redis-rb/examples/incr-decr.rb +18 -0
- data/vendor/redis-rb/examples/list.rb +26 -0
- data/vendor/redis-rb/examples/sets.rb +36 -0
- data/vendor/redis-rb/lib/dist_redis.rb +124 -0
- data/vendor/redis-rb/lib/hash_ring.rb +128 -0
- data/vendor/redis-rb/lib/pipeline.rb +21 -0
- data/vendor/redis-rb/lib/redis.rb +370 -0
- data/vendor/redis-rb/lib/redis/raketasks.rb +1 -0
- data/vendor/redis-rb/profile.rb +22 -0
- data/vendor/redis-rb/redis-rb.gemspec +30 -0
- data/vendor/redis-rb/spec/redis_spec.rb +637 -0
- data/vendor/redis-rb/spec/spec_helper.rb +4 -0
- data/vendor/redis-rb/speed.rb +16 -0
- data/vendor/redis-rb/tasks/redis.tasks.rb +140 -0
- metadata +125 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
== 1.3.0
|
2
|
+
This release adds support for Google Analytics and AdWords.
|
3
|
+
|
4
|
+
To view Google Analytics metrics from within Vanity, first make sure you are using Garb. For example, in your Gemfile:
|
5
|
+
|
6
|
+
gem "vanity", "1.3.0"
|
7
|
+
gem "garb", "0.5.0"
|
8
|
+
|
9
|
+
Next, authenticate using your account credentials. For example, in your config/environments/production.rb:
|
10
|
+
|
11
|
+
require "garb"
|
12
|
+
Garb::Session.login('..email..', '..password..', account_type: "GOOGLE") rescue nil
|
13
|
+
|
14
|
+
Last, define Vanity metrics that tap to Google Analytics metrics. For example:
|
15
|
+
|
16
|
+
metric "Acquisition: Visitors" do
|
17
|
+
description "Unique visitors on any given page, as tracked by Google Analytics"
|
18
|
+
google_analytics "UA-10087469-2", :visitors
|
19
|
+
end
|
20
|
+
|
21
|
+
* Added: Support for Google Analytics metrics, thanks to Tony Pitale's Garb and blog post: http://www.viget.com/extend/user-goal-tracking-in-rails-with-vanity-and-google-analytics/
|
22
|
+
* Added: Vanity query parameter that you can use to choose a particular alternative, e.g. to tie an advertisement banner with content of the site.
|
23
|
+
* Added: Command line "vanity list" catalogs all ongoing experiments, their alternatives (and fingerprints) and all metrics.
|
24
|
+
* Changed: Rails integration now separates use_vanity method, filters and helpers.
|
25
|
+
* Changed: Explicit vanity_context_filter and vanity_reload_filter so you can skip them, or order filters relative to them.
|
26
|
+
* Fixed: if metric cannot be loaded (e.g. offline, no db access) show error message for that metric but don't break dashboard.
|
27
|
+
* Removed: Vanity.playground.define is deprecated. Bad choice for a method name. If you need this feature, make a suggestion and let's create a better API.
|
28
|
+
|
29
|
+
== 1.2.0 (2009-12-14)
|
30
|
+
This release introduces metrics backed by ActiveRecord. Use them when your model is already tracking a metric, and you get instant historical data.
|
31
|
+
|
32
|
+
Example, track sign ups using User model:
|
33
|
+
|
34
|
+
metric "Signups" do
|
35
|
+
model Account
|
36
|
+
end
|
37
|
+
|
38
|
+
Example, track satisfaction using Survey model:
|
39
|
+
metric "Satisfaction" do
|
40
|
+
model Survey, :average=>:rating
|
41
|
+
end
|
42
|
+
|
43
|
+
Example, track only high ratings:
|
44
|
+
metric "High ratings" do
|
45
|
+
model Rating, :conditions=>["stars >= 4"]
|
46
|
+
end
|
47
|
+
|
48
|
+
There's no need to call track! on these metrics.
|
49
|
+
|
50
|
+
* Added: Metrics backed by ActiveRecord.
|
51
|
+
* Added: track! and ab_test methods now available from Object (i.e. everywhere).
|
52
|
+
* Added: Playground.load!. Now loading all metrics and experiments from Rails initializer.
|
53
|
+
* Changed: Decoupled metric name from identifier. You can now define a metric with more descriptive name, e.g. "Cheers per second (user satisfaction)" and keep their ID simple. Identifier is matched against the file name (for metrics loaded from experiments/metrics).
|
54
|
+
* Changed: Metrics no longer defined on-demand, i.e. calling playground.metric either returns existing metric or raises exception.
|
55
|
+
* Changed: Playground.experiments returns hash instead of array.
|
56
|
+
* Changed: All dates in report are UTC, since we don't know which locale to use.
|
57
|
+
* Removed: Object.experiment is deprecated, please call Vanity.playground.experiment directly.
|
58
|
+
* Fixed: Playground no longer changes logging level on supplied logger.
|
59
|
+
|
60
|
+
== 1.1.1 (2009-12-4)
|
61
|
+
* Fixed: Binding issue that shows up on 1.8.6/7.
|
62
|
+
|
63
|
+
== 1.1.0 (2009-12-4)
|
64
|
+
This release introduces metrics. Metrics are the gateway drug to better software.
|
65
|
+
|
66
|
+
It’s as simple as defining a metric:
|
67
|
+
|
68
|
+
metric "Cheers" do
|
69
|
+
description "They love us, don't they?"
|
70
|
+
end
|
71
|
+
|
72
|
+
Tracking it from your code:
|
73
|
+
|
74
|
+
track! :cheers
|
75
|
+
|
76
|
+
And watching the graph from the Dashboard.
|
77
|
+
|
78
|
+
You can (should) also use metrics with your A/B tests, for example:
|
79
|
+
|
80
|
+
ab_test "Pricing options" do
|
81
|
+
metrics :signup
|
82
|
+
alternatives 15, 25, 29
|
83
|
+
end
|
84
|
+
|
85
|
+
This new usage may become requirement in a future release.
|
86
|
+
|
87
|
+
Much thanks to Ian Sefferman for fixing issues with Ruby 1.8.7 and Rails support.
|
88
|
+
|
89
|
+
* Added: Metrics.
|
90
|
+
* Added: Use Vanity.playground.mock! when running tests and you'd rather not access a live Redis server.
|
91
|
+
* Changed: A/B tests now using metrics for tracking.
|
92
|
+
* Changed: Now throwing NameError instead of LoadError when failing to load experiment/metric. NameError can be rescued on same line.
|
93
|
+
* Changed: New, easier URL mapping for Dashboard: map.vanity "/vanity", :controller=>:vanity.
|
94
|
+
* Changed: All tests are green on Ruby 1.8.6, 1.8.7 and 1.9.1.
|
95
|
+
* Changed: Switched to redis-rb from http://github.com/ezmobius/redis-rb.
|
96
|
+
* Deprecated: Please call experiment method with experiment identifier (a symbol) and not experiment name.
|
97
|
+
|
98
|
+
== 1.0.0 (2009-11-19)
|
99
|
+
This release changes the way you define a new experiment. You can use a method suitable for the type of experiment you want to define, or call the generic define method (previously: experiment method). For example:
|
100
|
+
|
101
|
+
ab_test "My A/B test" do
|
102
|
+
alternatives :a, :b
|
103
|
+
end
|
104
|
+
|
105
|
+
The experiment method is no longer overloaded: it looks up an experiment (loading its definition if necessary), but does not define an experiment. The ab_test method is overloaded, though this may change in the future.
|
106
|
+
|
107
|
+
In addition, the ab_goal! method is now track!. This method may be used for other tests in the future.
|
108
|
+
|
109
|
+
* Added: A/B test report now showing number of participants.
|
110
|
+
* Added: AbTest.score method accepts minimum probability (default 90), and
|
111
|
+
* Removed: Experiment.reset! method. Destroy and save have the same effect.
|
112
|
+
* Changed: Playground.define now requires an experiment type, ab_test is not the default any more.
|
113
|
+
* Changed: When you run Vanity in development mode (configuration.cache_classes = false), it will reload experiments on each request. You can also Vanity.playground.reload!.
|
114
|
+
* Changed: Fancy AJAX trickery in Rails console.
|
115
|
+
* Changed: You can break long experiment descriptions into multiple paragraphs using two consecutive newlines.
|
116
|
+
* Changed: AbTest confidence becomes probability; only returns choice alternative with probability equal or higher than that.
|
117
|
+
* Changed: ab_goal! becomes track!.
|
118
|
+
* Changed: Console becomes Dashboard, which is less confusing with rails console (script/console).
|
119
|
+
|
120
|
+
== 0.3.1 (2009-11-13)
|
121
|
+
* Changed: Redis 1.0 is now vendored into Vanity. This means one less dependecy ... actually two, since Redis brings with it RSpec.
|
122
|
+
|
123
|
+
== 0.3.0 (2009-11-13)
|
124
|
+
* Added: score now includes least performing alternatives, names and values.
|
125
|
+
* Added: shiny reports.
|
126
|
+
* Added: Rails console shows current experiments status and also allows you to choose which alternative you want to see.
|
127
|
+
* Changed: letters instead of numbers for options (option 1 => option A).
|
128
|
+
* Changed: experiment.alternatives is now an immutable snapshot.
|
129
|
+
* Changed: experiment.score returns populated alternative objects instead of structs.
|
130
|
+
* Changed: experiment.chooses uses Redis to store state, better for (when we get to) browser integration.
|
131
|
+
* Changed: experiment.chooses skips recording participant or conversion.
|
132
|
+
* Changed: to MIT license.
|
133
|
+
|
134
|
+
== 0.2.2 (2009-11-12)
|
135
|
+
* Added: vanity binary, with single command for generating a report.
|
136
|
+
* Added: return alternative by value from experiment.alternative(val) method.
|
137
|
+
* Added: reset an experiment by calling reset!.
|
138
|
+
* Added: experiment alternative name (option 1, option 2, etc).
|
139
|
+
* Added: new scoring algorithm: use experiment.score instead of alternative.z_score/confidence.
|
140
|
+
* Added: experiment.conclusion for plain English results.
|
141
|
+
|
142
|
+
== 0.2.1 (2009-11-11)
|
143
|
+
* Added: z-score and confidence level for A/B test alternatives.
|
144
|
+
* Added: test auto-completion and auto-outcome (complete_it, outcome_is).
|
145
|
+
* Changed: default alternatives are now false/true, so if can't decide outcome, fall back on false.
|
146
|
+
|
147
|
+
== 0.2.0 (2009-11-10)
|
148
|
+
* Added: experiment method on object, used to define and access experiments.
|
149
|
+
* Added: playground configuration (Vanity.playground.namespace = , etc).
|
150
|
+
* Added: use_vanity now accepts block instead of symbol.
|
151
|
+
* Changed: Vanity::Helpers becomes Vanity::Rails.
|
152
|
+
* Changed: A/B test experiments alternatives now handled using Alternatives object.
|
153
|
+
* Removed: A/B test measure method no longer in use.
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2009 Assaf Arkin
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
Vanity is an Experiment Driven Development framework for Rails.
|
2
|
+
|
3
|
+
* All about Vanity: http://vanity.labnotes.org
|
4
|
+
* On github: http://github.com/assaf/vanity
|
5
|
+
* Vanity requires Redis 1.0 or later.
|
6
|
+
|
7
|
+
http://farm3.static.flickr.com/2540/4099665871_497f274f68_o.jpg
|
8
|
+
|
9
|
+
|
10
|
+
== A/B Testing With Rails (In 5 Easy Steps)
|
11
|
+
|
12
|
+
<b>Step 1:</b> Start using Vanity in your Rails application:
|
13
|
+
|
14
|
+
gem.config "vanity"
|
15
|
+
|
16
|
+
And:
|
17
|
+
|
18
|
+
class ApplicationController < ActionController::Base
|
19
|
+
use_vanity :current_user
|
20
|
+
end
|
21
|
+
|
22
|
+
<b>Step 2:</b> Define your first A/B test. This experiment goes in the file <code>experiments/price_options.rb</code>:
|
23
|
+
|
24
|
+
ab_test "Price options" do
|
25
|
+
description "Mirror, mirror on the wall, who's the better price of all?"
|
26
|
+
alternatives 19, 25, 29
|
27
|
+
metrics :signups
|
28
|
+
end
|
29
|
+
|
30
|
+
<b>Step 3:</b> Present the different options to your users:
|
31
|
+
|
32
|
+
<h2>Get started for only $<%= ab_test :price_options %> a month!</h2>
|
33
|
+
|
34
|
+
<b>Step 4:</b> Measure conversion:
|
35
|
+
|
36
|
+
class SignupController < ApplicationController
|
37
|
+
def signup
|
38
|
+
@account = Account.new(params[:account])
|
39
|
+
if @account.save
|
40
|
+
track! :signups
|
41
|
+
redirect_to @acccount
|
42
|
+
else
|
43
|
+
render action: :offer
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
<b>Step 5:</b> Check the report:
|
49
|
+
|
50
|
+
vanity --output vanity.html
|
51
|
+
|
52
|
+
|
53
|
+
== Building From Source
|
54
|
+
|
55
|
+
To run the test suite for the first time:
|
56
|
+
|
57
|
+
$ gem install rails mocha timecop
|
58
|
+
$ rake
|
59
|
+
|
60
|
+
You can also +rake test+ if you insist on being explicit.
|
61
|
+
|
62
|
+
To build the documentation:
|
63
|
+
|
64
|
+
$ gem install yardoc jekyll
|
65
|
+
$ rake docs
|
66
|
+
$ open html/index.html
|
67
|
+
|
68
|
+
To clean up after yourself:
|
69
|
+
|
70
|
+
$ rake clobber
|
71
|
+
|
72
|
+
To package Vanity as a gem and install on your machine:
|
73
|
+
|
74
|
+
$ rake install
|
75
|
+
|
76
|
+
|
77
|
+
== Credits/License
|
78
|
+
|
79
|
+
Original code, copyright of Assaf Arkin, released under the MIT license.
|
80
|
+
|
81
|
+
Documentation available under the Creative Commons Attribution license.
|
82
|
+
|
83
|
+
For full list of credits and licenses: http://vanity.labnotes.org/credits.html.
|
data/bin/vanity
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
path = File.expand_path("../lib", File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift path unless $LOAD_PATH.include?(path)
|
4
|
+
|
5
|
+
require "vanity"
|
6
|
+
require "optparse"
|
7
|
+
|
8
|
+
playground = Vanity.playground
|
9
|
+
options = Struct.new(:output).new
|
10
|
+
opts = OptionParser.new("", 24, " ") do |opts|
|
11
|
+
opts.banner = "Usage: #{File.basename($0)} [options] command\n"
|
12
|
+
opts.banner << "Commands:\n"
|
13
|
+
opts.banner << " report Report on all running experiments/metrics"
|
14
|
+
opts.banner << " list List all experiments and metrics"
|
15
|
+
|
16
|
+
opts.separator ""
|
17
|
+
opts.separator "General options:"
|
18
|
+
opts.on("--path PATH", "Path to experiments directory (default: #{playground.load_path})") { |v| playground.load_path = v }
|
19
|
+
opts.on("--output FILE", "Write report to this file (default: stdout)") { |v| options.output = v }
|
20
|
+
|
21
|
+
opts.separator ""
|
22
|
+
opts.separator "Redis options:"
|
23
|
+
opts.on("--host HOST", "Redis server host (default: #{playground.host})") { |v| playground.host = v }
|
24
|
+
opts.on("--port PORT", "Redis server port (default: #{playground.port})") { |v| playground.port = v }
|
25
|
+
opts.on("--db DB", "Redis database (default: #{playground.db})") { |v| playground.db = v }
|
26
|
+
opts.on("--password PWD", "Redis database password") { |v| playground.password = v }
|
27
|
+
opts.on("--namespace NS", "Redis namespace (default: #{playground.namespace})") { |v| playground.namespace = v }
|
28
|
+
|
29
|
+
opts.separator ""
|
30
|
+
opts.separator "Common options:"
|
31
|
+
opts.on_tail "-h", "-H", "--help", "Show this message" do
|
32
|
+
puts opts.to_s.gsub(/^.*DEPRECATED.*$/s, '')
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
opts.on_tail "-v", "--version", "Show version" do
|
36
|
+
puts "Vanity #{Vanity::Version::STRING}"
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.parse!(ARGV)
|
42
|
+
if ARGV.empty?
|
43
|
+
puts opts.banner
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
|
47
|
+
ARGV.each do |cmd|
|
48
|
+
case cmd
|
49
|
+
when "report"; Vanity::Commands.report options.output
|
50
|
+
when "list"; Vanity::Commands.list
|
51
|
+
else fail "No such command: #{cmd}"
|
52
|
+
end
|
53
|
+
end
|
data/lib/vanity.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "date"
|
2
|
+
require "time"
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), "../vendor/redis-rb/lib")
|
6
|
+
autoload :Redis, "redis"
|
7
|
+
|
8
|
+
# All the cool stuff happens in other places.
|
9
|
+
# @see Vanity::Helper
|
10
|
+
# @see Vanity::Rails
|
11
|
+
# @see Vanity::Playground
|
12
|
+
# @see Vanity::Metric
|
13
|
+
# @see Vanity::Experiment
|
14
|
+
module Vanity
|
15
|
+
# Version number.
|
16
|
+
module Version
|
17
|
+
version = Gem::Specification.load(File.expand_path("../vanity.gemspec", File.dirname(__FILE__))).version.to_s.split(".").map { |i| i.to_i }
|
18
|
+
MAJOR = version[0]
|
19
|
+
MINOR = version[1]
|
20
|
+
PATCH = version[2]
|
21
|
+
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
require "vanity/backport" if RUBY_VERSION < "1.9"
|
26
|
+
# Metrics.
|
27
|
+
require "vanity/metric/base"
|
28
|
+
require "vanity/metric/active_record"
|
29
|
+
require "vanity/metric/google_analytics" if defined?(Garb)
|
30
|
+
# Experiments.
|
31
|
+
require "vanity/experiment/base"
|
32
|
+
require "vanity/experiment/ab_test"
|
33
|
+
# Playground.
|
34
|
+
require "vanity/playground"
|
35
|
+
require "vanity/helpers"
|
36
|
+
Vanity.autoload :MockRedis, "vanity/mock_redis"
|
37
|
+
Vanity.autoload :Commands, "vanity/commands"
|
38
|
+
require "vanity/rails" if defined?(Rails)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Time
|
2
|
+
unless method_defined?(:to_date)
|
3
|
+
# Backported from Ruby 1.9.
|
4
|
+
def to_date
|
5
|
+
jd = Date.__send__(:civil_to_jd, year, mon, mday, Date::ITALY)
|
6
|
+
Date.new!(Date.__send__(:jd_to_ajd, jd, 0, 0), 0, Date::ITALY)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Date
|
12
|
+
unless method_defined?(:to_date)
|
13
|
+
# Backported from Ruby 1.9.
|
14
|
+
def to_date
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
unless method_defined?(:to_time)
|
20
|
+
# Backported from Ruby 1.9.
|
21
|
+
def to_time
|
22
|
+
Time.local(year, mon, mday)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Symbol
|
28
|
+
unless method_defined?(:to_proc)
|
29
|
+
# Backported from Ruby 1.9.
|
30
|
+
def to_proc
|
31
|
+
Proc.new { |*args| args.shift.__send__(self, *args) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Array
|
37
|
+
unless method_defined?(:minmax)
|
38
|
+
# Backported from Ruby 1.9.
|
39
|
+
def minmax
|
40
|
+
[min, max]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Vanity
|
2
|
+
module Commands
|
3
|
+
class << self
|
4
|
+
# Lists all experiments and metrics.
|
5
|
+
def list
|
6
|
+
Vanity.playground.experiments.each do |id, experiment|
|
7
|
+
puts "experiment :%-.20s (%-.40s)" % [id, experiment.name]
|
8
|
+
if experiment.respond_to?(:alternatives)
|
9
|
+
experiment.alternatives.each do |alt|
|
10
|
+
hash = experiment.fingerprint(alt)
|
11
|
+
puts " %s: %-40.40s (%s)" % [alt.name, alt.value, hash]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
Vanity.playground.metrics.each do |id, metric|
|
16
|
+
puts "metric :%-.20s (%-.40s)" % [id, metric.name]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "cgi"
|
3
|
+
|
4
|
+
module Vanity
|
5
|
+
|
6
|
+
# Render method available to templates (when used by Vanity command line,
|
7
|
+
# outside Rails).
|
8
|
+
module Render
|
9
|
+
|
10
|
+
# Render the named template. Used for reporting and the dashboard.
|
11
|
+
def render(path, locals = {})
|
12
|
+
locals[:playground] = self
|
13
|
+
keys = locals.keys
|
14
|
+
struct = Struct.new(*keys)
|
15
|
+
struct.send :include, Render
|
16
|
+
locals = struct.new(*locals.values_at(*keys))
|
17
|
+
dir, base = File.split(path)
|
18
|
+
path = File.join(dir, "_#{base}")
|
19
|
+
erb = ERB.new(File.read(path), nil, '<>')
|
20
|
+
erb.filename = path
|
21
|
+
erb.result(locals.instance_eval { binding })
|
22
|
+
end
|
23
|
+
|
24
|
+
# Escape HTML.
|
25
|
+
def h(html)
|
26
|
+
CGI.escapeHTML(html)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Dumbed down from Rails' simple_format.
|
30
|
+
def simple_format(text, options={})
|
31
|
+
open = "<p #{options.map { |k,v| "#{k}=\"#{CGI.escapeHTML v}\"" }.join(" ")}>"
|
32
|
+
text = open + text.gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
|
33
|
+
gsub(/\n\n+/, "</p>\n\n#{open}"). # 2+ newline -> paragraph
|
34
|
+
gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') + # 1 newline -> br
|
35
|
+
"</p>"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Commands available when running Vanity from the command line (see bin/vanity).
|
40
|
+
module Commands
|
41
|
+
class << self
|
42
|
+
include Render
|
43
|
+
|
44
|
+
# Generate an HTML report. Outputs to the named file, or stdout with no
|
45
|
+
# arguments.
|
46
|
+
def report(output = nil)
|
47
|
+
html = render(Vanity.template("report"))
|
48
|
+
if output
|
49
|
+
File.open output, 'w' do |file|
|
50
|
+
file.write html
|
51
|
+
end
|
52
|
+
puts "New report available in #{output}"
|
53
|
+
else
|
54
|
+
$stdout.write html
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|