moses-vanity 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/.autotest +22 -0
  2. data/.gitignore +7 -0
  3. data/.rvmrc +3 -0
  4. data/.travis.yml +13 -0
  5. data/CHANGELOG +374 -0
  6. data/Gemfile +28 -0
  7. data/MIT-LICENSE +21 -0
  8. data/README.rdoc +108 -0
  9. data/Rakefile +189 -0
  10. data/bin/vanity +16 -0
  11. data/doc/_config.yml +2 -0
  12. data/doc/_layouts/_header.html +34 -0
  13. data/doc/_layouts/page.html +47 -0
  14. data/doc/_metrics.textile +12 -0
  15. data/doc/ab_testing.textile +210 -0
  16. data/doc/configuring.textile +45 -0
  17. data/doc/contributing.textile +93 -0
  18. data/doc/credits.textile +23 -0
  19. data/doc/css/page.css +83 -0
  20. data/doc/css/print.css +43 -0
  21. data/doc/css/syntax.css +7 -0
  22. data/doc/email.textile +129 -0
  23. data/doc/experimental.textile +31 -0
  24. data/doc/faq.textile +8 -0
  25. data/doc/identity.textile +43 -0
  26. data/doc/images/ab_in_dashboard.png +0 -0
  27. data/doc/images/clear_winner.png +0 -0
  28. data/doc/images/price_options.png +0 -0
  29. data/doc/images/sidebar_test.png +0 -0
  30. data/doc/images/signup_metric.png +0 -0
  31. data/doc/images/vanity.png +0 -0
  32. data/doc/index.textile +91 -0
  33. data/doc/metrics.textile +231 -0
  34. data/doc/rails.textile +89 -0
  35. data/doc/site.js +27 -0
  36. data/generators/templates/vanity_migration.rb +53 -0
  37. data/generators/vanity_generator.rb +8 -0
  38. data/lib/generators/templates/vanity_migration.rb +53 -0
  39. data/lib/generators/vanity_generator.rb +15 -0
  40. data/lib/vanity.rb +36 -0
  41. data/lib/vanity/adapters/abstract_adapter.rb +140 -0
  42. data/lib/vanity/adapters/active_record_adapter.rb +248 -0
  43. data/lib/vanity/adapters/mock_adapter.rb +157 -0
  44. data/lib/vanity/adapters/mongodb_adapter.rb +178 -0
  45. data/lib/vanity/adapters/redis_adapter.rb +160 -0
  46. data/lib/vanity/backport.rb +26 -0
  47. data/lib/vanity/commands/list.rb +21 -0
  48. data/lib/vanity/commands/report.rb +64 -0
  49. data/lib/vanity/commands/upgrade.rb +34 -0
  50. data/lib/vanity/experiment/ab_test.rb +507 -0
  51. data/lib/vanity/experiment/base.rb +214 -0
  52. data/lib/vanity/frameworks.rb +16 -0
  53. data/lib/vanity/frameworks/rails.rb +318 -0
  54. data/lib/vanity/helpers.rb +66 -0
  55. data/lib/vanity/images/x.gif +0 -0
  56. data/lib/vanity/metric/active_record.rb +85 -0
  57. data/lib/vanity/metric/base.rb +244 -0
  58. data/lib/vanity/metric/google_analytics.rb +83 -0
  59. data/lib/vanity/metric/remote.rb +53 -0
  60. data/lib/vanity/playground.rb +396 -0
  61. data/lib/vanity/templates/_ab_test.erb +28 -0
  62. data/lib/vanity/templates/_experiment.erb +5 -0
  63. data/lib/vanity/templates/_experiments.erb +7 -0
  64. data/lib/vanity/templates/_metric.erb +14 -0
  65. data/lib/vanity/templates/_metrics.erb +13 -0
  66. data/lib/vanity/templates/_report.erb +27 -0
  67. data/lib/vanity/templates/_vanity.js.erb +20 -0
  68. data/lib/vanity/templates/flot.min.js +1 -0
  69. data/lib/vanity/templates/jquery.min.js +19 -0
  70. data/lib/vanity/templates/vanity.css +26 -0
  71. data/lib/vanity/templates/vanity.js +82 -0
  72. data/lib/vanity/version.rb +11 -0
  73. data/test/adapters/redis_adapter_test.rb +17 -0
  74. data/test/experiment/ab_test.rb +771 -0
  75. data/test/experiment/base_test.rb +150 -0
  76. data/test/experiments/age_and_zipcode.rb +19 -0
  77. data/test/experiments/metrics/cheers.rb +3 -0
  78. data/test/experiments/metrics/signups.rb +2 -0
  79. data/test/experiments/metrics/yawns.rb +3 -0
  80. data/test/experiments/null_abc.rb +5 -0
  81. data/test/metric/active_record_test.rb +277 -0
  82. data/test/metric/base_test.rb +293 -0
  83. data/test/metric/google_analytics_test.rb +104 -0
  84. data/test/metric/remote_test.rb +109 -0
  85. data/test/myapp/app/controllers/application_controller.rb +2 -0
  86. data/test/myapp/app/controllers/main_controller.rb +7 -0
  87. data/test/myapp/config/boot.rb +110 -0
  88. data/test/myapp/config/environment.rb +10 -0
  89. data/test/myapp/config/environments/production.rb +0 -0
  90. data/test/myapp/config/routes.rb +3 -0
  91. data/test/passenger_test.rb +43 -0
  92. data/test/playground_test.rb +26 -0
  93. data/test/rails_dashboard_test.rb +37 -0
  94. data/test/rails_helper_test.rb +36 -0
  95. data/test/rails_test.rb +389 -0
  96. data/test/test_helper.rb +145 -0
  97. data/vanity.gemspec +26 -0
  98. metadata +202 -0
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2010 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
+
@@ -0,0 +1,108 @@
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
+
6
+ http://farm3.static.flickr.com/2540/4099665871_497f274f68_o.jpg
7
+
8
+
9
+ == A/B Testing With Rails (In 5 Easy Steps)
10
+
11
+ <b>Step 1:</b> Start using Vanity in your Rails application:
12
+
13
+ == Rails 2.x configuration
14
+
15
+ Rails::Initializer.run do |config|
16
+ gem.config "vanity"
17
+
18
+ config.after_initialize do
19
+ require "vanity"
20
+ end
21
+ end
22
+
23
+ == Rails 3 configuration
24
+
25
+ Add to your Gemfile:
26
+
27
+ gem "vanity"
28
+
29
+ If using a relational database, run the generator and migrations to create the database schema:
30
+
31
+ $ rails generate vanity
32
+ $ rake db:migrate
33
+
34
+ Add to your application controller:
35
+
36
+ class ApplicationController < ActionController::Base
37
+ use_vanity :current_user
38
+ end
39
+
40
+ <b>Step 2:</b> Define your first A/B test. This experiment goes in the file <code>experiments/price_options.rb</code>:
41
+
42
+ ab_test "Price options" do
43
+ description "Mirror, mirror on the wall, who's the better price of all?"
44
+ alternatives 19, 25, 29
45
+ metrics :signups
46
+ end
47
+
48
+ NOTE: If using a metric as above ("signups"), there needs to be a corresponding ruby file for that metric. Inside the "experiments" directory create a "metrics" directory with a file called "signup.rb". The contents of the file can describe the signup metric, refer to the "Metrics" Vanity documentation page for an example.
49
+
50
+ <b>Step 3:</b> Present the different options to your users:
51
+
52
+ <h2>Get started for only $<%= ab_test :price_options %> a month!</h2>
53
+
54
+ <b>Step 4:</b> Measure conversion:
55
+
56
+ class SignupController < ApplicationController
57
+ def signup
58
+ @account = Account.new(params[:account])
59
+ if @account.save
60
+ track! :signups
61
+ redirect_to @acccount
62
+ else
63
+ render action: :offer
64
+ end
65
+ end
66
+ end
67
+
68
+ <b>Step 5:</b> Check the report:
69
+
70
+ vanity report --output vanity.html
71
+
72
+
73
+ == Rails 3
74
+
75
+ There is currently an issue with report generation. The vanity-talk Google Group has a couple posts that outline the issue for now. This is one of the posts: http://groups.google.com/group/vanity-talk/browse_thread/thread/343081a72a0cefb6
76
+
77
+ If you are collecting data (in development you need to opt-in to this by setting Vanity.playground.collecting = true in environments/development.rb) you can view experiment results with the vanity dashboard instead of the report.
78
+
79
+ The vanity dashboard setup instructions with Vanity work for Rails 3.x except the route is different. A Rails 3.x-style route would look like this:
80
+
81
+ `match '/vanity(/:action(/:id(.:format)))', :controller=>:vanity`
82
+
83
+
84
+ == Registering participants with Javascript
85
+
86
+ If robots or spiders make up a significant portion of your sites traffic they can affect your conversion rate. Vanity can optionally add participants to the experiments using asynchronous javascript callbacks, which will keep almost all robots out. To set this up simply do the following:
87
+
88
+ * Add Vanity.playground.use_js!
89
+ * Set Vanity.playground.add_participant_path = '/path/to/vanity/action', this should point to the add_participant path that is added with Vanity::Rails::Dashboard, make sure that this action is available to all users
90
+ * Add <%= vanity_js %> to any page that needs uses an ab_test. vanity_js needs to be included after your call to ab_test so that it knows which version of the experiment the participant is a member of. The helper will render nothing if the there are no ab_tests running on the current page, so adding vanity_js to the bottom of your layouts is a good option. Keep in mind that if you call use_js! and don't include vanity_js in your view no participants will be recorded.
91
+
92
+ == Contributing
93
+
94
+ * Fork the project
95
+ * Please use a topic branch to make your changes, it's easier to test them that way
96
+ * Fix, patch, enhance, document, improve, sprinkle pixie dust
97
+ * At minimum run rake test, if possible, please run rake test:all
98
+ * Tests. Please. Run rake test, of if you can, rake test:all
99
+ * Send a pull request on GitHub
100
+
101
+
102
+ == Credits/License
103
+
104
+ Original code, copyright of Assaf Arkin, released under the MIT license.
105
+
106
+ Documentation available under the Creative Commons Attribution license.
107
+
108
+ For full list of credits and licenses: http://vanity.labnotes.org/credits.html.
@@ -0,0 +1,189 @@
1
+ require "rake/testtask"
2
+
3
+ # -- Building stuff --
4
+
5
+ spec = Gem::Specification.load(Dir["*.gemspec"].first)
6
+
7
+ desc "Build the Gem"
8
+ task :build do
9
+ sh "gem build #{spec.name}.gemspec"
10
+ end
11
+
12
+ desc "Install #{spec.name} locally"
13
+ task :install=>:build do
14
+ sudo = "sudo" unless File.writable?( Gem::ConfigMap[:bindir])
15
+ sh "#{sudo} gem install #{spec.name}-#{spec.version}.gem"
16
+ end
17
+
18
+ desc "Push new release to gemcutter and git tag"
19
+ task :push=>["test:all", "build"] do
20
+ sh "git push"
21
+ puts "Tagging version #{spec.version} .."
22
+ sh "git tag v#{spec.version}"
23
+ sh "git push --tag"
24
+ puts "Building and pushing gem .."
25
+ sh "gem push #{spec.name}-#{spec.version}.gem"
26
+ end
27
+
28
+
29
+ # -- Testing stuff --
30
+
31
+ desc "Test everything"
32
+ task "test:all"=>"test:adapters"
33
+
34
+ # Ruby versions we're testing with.
35
+ RUBIES = %w{1.8.7 1.9.2}
36
+
37
+ # Use rake test:rubies to run all combination of tests (see test:adapters) using
38
+ # all the versions of Ruby specified in RUBIES. Or to test a specific version of
39
+ # Ruby, rake test:rubies[1.8.7].
40
+ #
41
+ # This task uses RVM to install all the Ruby versions it needs, and creates a
42
+ # vanity gemset in each one that includes Bundler and all the gems specified in
43
+ # Gemfile. If anything goes south you can always wipe these gemsets or uninstall
44
+ # these Rubies and start over.
45
+ desc "Test using multiple versions of Ruby"
46
+ task "test:rubies", :ruby do |t, args|
47
+ rubies = args.ruby ? [args.ruby] : RUBIES
48
+ rubies.each do |ruby|
49
+ puts "** Setup #{ruby}"
50
+ sh "env rvm_install_on_use_flag=1 rvm_gemset_create_on_use_flag=1 rvm use #{ruby}@vanity"
51
+ sh "rvm #{ruby}@vanity rake test:setup"
52
+ puts
53
+ puts "** Test using #{ruby}"
54
+ sh "rvm #{ruby}@vanity -S bundle exec rake test:adapters #{'--trace' if Rake.application.options.trace}"
55
+ end
56
+ end
57
+
58
+ task "test:setup" do
59
+ # Intended to be used from test:rubies, within specific RVM context.
60
+ begin # Make sure we got Bundler installed.
61
+ require "bundler"
62
+ rescue LoadError
63
+ sh "gem install bundler"
64
+ end
65
+ begin # Make sure we got all the dependencies
66
+ sh "bundle exec ruby -e puts > /dev/null"
67
+ rescue
68
+ sh "bundle install"
69
+ end
70
+ end
71
+
72
+ # These are all the adapters we're going to test with.
73
+ ADAPTERS = %w{redis mongodb mysql}
74
+
75
+ desc "Test using different back-ends"
76
+ task "test:adapters", :adapter do |t, args|
77
+ adapters = args.adapter ? [args.adapter] : ADAPTERS
78
+ adapters.each do |adapter|
79
+ puts "** Testing #{adapter} adapter"
80
+ sh "rake test DB=#{adapter} #{'--trace' if Rake.application.options.trace}"
81
+ end
82
+ end
83
+
84
+ # Run the test suit.
85
+
86
+ task :default=>:test
87
+ desc "Run all tests"
88
+ Rake::TestTask.new do |task|
89
+ task.test_files = FileList['test/**/*_test.rb']
90
+ if Rake.application.options.trace
91
+ #task.warning = true
92
+ task.verbose = true
93
+ elsif Rake.application.options.silent
94
+ task.ruby_opts << "-W0"
95
+ else
96
+ task.verbose = true
97
+ end
98
+ task.ruby_opts << "-I."
99
+ end
100
+
101
+ task(:clobber) { rm_rf "tmp" }
102
+
103
+
104
+ # -- Documenting stuff --
105
+
106
+ begin
107
+ require "yard"
108
+ YARD::Rake::YardocTask.new(:yardoc) do |task|
109
+ task.files = FileList["lib/**/*.rb"].exclude("lib/vanity/backport.rb")
110
+ task.options = "--output", "html/api", "--title", "Vanity #{spec.version}", "--main", "README.rdoc", "--files", "CHANGELOG"
111
+ end
112
+ rescue LoadError
113
+ end
114
+
115
+ desc "Jekyll generates the main documentation (sans API)"
116
+ task(:jekyll) { sh "jekyll", "doc", "html" }
117
+ file "html/vanity.pdf"=>:jekyll do |t|
118
+ pages = %w{index metrics ab_testing rails email identity configuring contributing}.map{ |p| "html/#{p}.html" }
119
+ args = %w{--disable-javascript --outline --title Vanity --header-html doc/_layouts/_header.html --print-media-type}
120
+ args.concat %w{--margin-left 20 --margin-right 20 --margin-top 20 --margin-bottom 20 --header-spacing 5}
121
+ args.concat pages << t.name
122
+ sh "wkhtmltopdf", *args
123
+ end
124
+
125
+ file "html/vanity-api.zip"=>:yardoc do |t|
126
+ Dir.chdir "html" do
127
+ sh "zip vanity-api.zip -r api"
128
+ end
129
+ end
130
+ desc "Create documentation in docs directory (including API)"
131
+ task :docs=>[:jekyll, :yardoc, "html/vanity-api.zip", "html/vanity.pdf"]
132
+ desc "Remove temporary files and directories"
133
+ task(:clobber) { rm_rf "html" ; rm_rf ".yardoc" }
134
+
135
+ desc "Publish documentation to vanity.labnotes.org"
136
+ task :publish=>[:clobber, :docs] do
137
+ sh "rsync -cr --del --progress html/ labnotes.org:/var/www/vanity/"
138
+ end
139
+
140
+
141
+ # -- Misc --
142
+
143
+ task :report do
144
+ $LOAD_PATH.unshift "lib"
145
+ require "vanity"
146
+ require "timecop"
147
+ Vanity.playground.load_path = "test/experiments"
148
+ Vanity.playground.experiments.values.each(&:destroy)
149
+ Vanity.playground.metrics.values.each(&:destroy!)
150
+ Vanity.playground.reload!
151
+
152
+ # Control 182 35 19.23% N/A
153
+ # Treatment A 180 45 25.00% 1.33
154
+ # Treatment B 189 28 14.81% -1.13
155
+ # Treatment C 188 61 32.45% 2.94
156
+ Vanity.playground.experiment(:null_abc).instance_eval do
157
+ fake nil=>[182,35], :red=>[180,45], :green=>[189,28], :blue=>[188,61]
158
+ @created_at = (Date.today - 40).to_time
159
+ @completed_at = (Date.today - 35).to_time
160
+ end
161
+
162
+ Vanity.playground.experiment(:age_and_zipcode).instance_eval do
163
+ fake false=>[80,35], true=>[84,32]
164
+ @created_at = (Date.today - 30).to_time
165
+ @completed_at = (Date.today - 15).to_time
166
+ end
167
+
168
+ Vanity.context = Object.new
169
+ Vanity.context.instance_eval { def vanity_identity ; 0 ; end }
170
+ signups = 50
171
+ (Date.today - 90..Date.today).each do |date|
172
+ Timecop.travel date do
173
+ signups += rand(15) - 5
174
+ Vanity.playground.track! :signups, signups
175
+ end
176
+ end
177
+
178
+ cheers, yawns = 0, 0
179
+ (Date.today - 80..Date.today).each do |date|
180
+ Timecop.travel date do
181
+ cheers = cheers - 5 + rand(20)
182
+ Vanity.playground.track! :yawns, cheers
183
+ yawns = yawns - 5 + rand(30)
184
+ Vanity.playground.track! :cheers, yawns
185
+ end
186
+ end
187
+
188
+ Vanity::Commands.report ENV["OUTPUT"]
189
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'vanity' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('vanity', 'vanity')
@@ -0,0 +1,2 @@
1
+ # http://wiki.github.com/mojombo/jekyll/configuration
2
+ pygments: false
@@ -0,0 +1,34 @@
1
+ <html>
2
+ <head>
3
+ <script>
4
+ function subst() {
5
+ var vars={};
6
+ var x=document.location.search.substring(1).split('&');
7
+ for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
8
+ var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
9
+ for(var i in x) {
10
+ var y = document.getElementsByClassName(x[i]);
11
+ for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
12
+ }
13
+ }
14
+ </script>
15
+ <style>
16
+ body { margin: 0; padding: 0; height: 2em }
17
+ .header {
18
+ font-size: 10pt;
19
+ margin: 0;
20
+ padding: 0;
21
+ border-bottom: 1px solid #ccc;
22
+ width: 100%;
23
+ }
24
+ </style>
25
+ </head>
26
+ <body onload="subst()">
27
+ <table class="header">
28
+ <tr>
29
+ <td class="section"></td>
30
+ <td style="text-align:right"><span class="page"></span></td>
31
+ </tr>
32
+ </table>
33
+ </body>
34
+ </html>
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Vanity &mdash; {{ page.title }}</title>
5
+ <link href="css/page.css" media="screen,print" rel="stylesheet" type="text/css">
6
+ <link href="css/print.css" media="print" rel="stylesheet" type="text/css">
7
+ <link href="images/favicon.png" rel="shortcut icon">
8
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
9
+ <script type="text/javascript" src="site.js"></script>
10
+ </head>
11
+ <body>
12
+ <div id="header">
13
+ <div class="title"><a href="http://vanity.labnotes.org" title="Mirror, mirror, on the wall &hellip;">Vanity</a></div>
14
+ <div class="tagline">Experiment<br>Driven Development</div>
15
+ </div>
16
+ <div id="links">
17
+ <a href="http://github.com/assaf/vanity">Source code</a> |
18
+ <a href="http://groups.google.com/group/vanity-talk" title="vanity-talk google group">Google Group</a> |
19
+ <a href="http://vanity.labnotes.org/api/index.html">API reference</a>
20
+ </div>
21
+ <div id="sidebar">
22
+ <ul>
23
+ <li><a href="index.html#intro" title="A/B Testing with Rails in 5 easy steps">2 Minute Demo</a></li>
24
+ <li><a href="metrics.html">Metrics</a></li>
25
+ <li><a href="ab_testing.html" title="Everything you need to know">A/B Testing</a></li>
26
+ <li><a href="rails.html">Using with Rails</a></li>
27
+ <li><a href="email.html">Testing emails</a></li>
28
+ <li><a href="identity.html">Managing Identity</a></li>
29
+ <li><a href="configuring.html">Configuring</a></li>
30
+ <li><a href="contributing.html">Contributing</a></li>
31
+ <li><a href="experimental.html">Experimental</a></li>
32
+ <li>Download: <a href="vanity-api.zip">API</a>/<a href="vanity.pdf">PDF</a></li>
33
+ </ul>
34
+ <ul id="stats">
35
+ <li><a href="http://travis-ci.org/assaf/vanity"><img src="http://travis-ci.org/assaf/vanity.png"></a></li>
36
+ <li><a href="http://github.com/assaf/vanity/graphs/traffic">Traffic</a></li>
37
+ <li><a href="http://wiki.github.com/assaf/vanity/whos-using-vanity">Who's using it?</a></li>
38
+ </ul>
39
+ </div>
40
+ <div id="content">
41
+ <h1 id="{{ page.title | downcase | replace(' ', '_') }}">{{ page.title }}</h1>
42
+ {{ content }}
43
+ </div>
44
+ <div id="footer"><a href="credits.html">Credits / License</a></div>
45
+ <script type="text/javascript">var _gaq=_gaq||[];_gaq.push(["_setAccount", "UA-1828623-6"], ["_trackPageview"]);(function(){var ga=document.createElement("script");ga.src=("https:"==document.location.protocol?"https://ssl":"http://www")+".google-analytics.com/ga.js";ga.setAttribute("async", "true");document.documentElement.firstChild.appendChild(ga);})();</script>
46
+ </body>
47
+ </html>
@@ -0,0 +1,12 @@
1
+ Metrics and experiments both measure, the main difference is scope. Typically you would use metrics for long-term measurements of general indicators you are interested in. For example, metrics for number of sign ups, comments submitted, videos uploaded, etc. You would then run short-term experiments to improve on these metrics. For example, experiment with different landing page to see how it affect sign ups. You may run several experiments in attempt to improve the same metric, an experiment may also measure against multiple metrics (e.g. it may increase uploaded videos, but decrease comments). A successful experiment is one that teaches what to do in order to improve a metric.
2
+
3
+ Start collecting metrics early. The longer you collect them, the better you'll be able to spot trends.
4
+
5
+ You can collect different kinds of metrics. Web applications focus on visits, but you may want to look deeper into the application and collect metrics that gauge engagement and growth from within your user base. Use metrics to spot usage patterns and see which direction you should take your application in. Metrics could point you in the direction of features that get all the usage, and are the basis for your application. They'll also tell you which features are dead-weight so you can trim the fat.
6
+
7
+ Watching metrics is fun, but not where the action is. The action is making changes that improve these metrics. Once you get into the practice of changing code and measuring impact you will notice that your intuition is often right, but not always. People will surprise you. You'll realize that, rather than brainstorming five headlines for a page and using one, you can use all five and pick the one that works best. You'll notice that more options you try out, the faster and further you can improve your metrics. You'll want to make your experiments lightweight, quick to setup, measure and teardown (or officiate into code). Welcome to EDD.
8
+
9
+ Metrics collect historical data which you can use to predict future behavior. Classical example is looking at growth patterns to determine when it's time for an infrastructure upgrade and how large will it be. You can also use historical data to predict probable range for future behavior (bounds), and issue alerts when system gets out of these bounds. For example, if you get 50~75 registrations a day, and all of a sudden than number drops to zero, you might have a problem with the registration system. You'll want an alert for that. If it shoots into 200, you might have an awesome PR opportunity going on.
10
+
11
+ Metrics are not just for business people. They can help you improve usability by measuring -- rather than guessing -- which actions have a positive affect. Was it speeding up database queries or JavaScript code? The new edit-in-place feature? IE6 support? Just kidding. Metrics can help you find broken code that slipped past the test suite, although it won't tell you where the bug is, you'll notice when a broken feature gets no usage. Metrics can also help you with content improvement (micro-copy).
12
+
@@ -0,0 +1,210 @@
1
+ ---
2
+ layout: page
3
+ title: A/B Testing
4
+ ---
5
+
6
+ <div id="toc">
7
+ # "True or False":#tf
8
+ # "Interpreting the Results":#interpret
9
+ # "Multiple Alternatives":#multiple
10
+ # "A/B Testing and Code Testing":#test
11
+ # "Let the Experiment Decide":#decide
12
+ </div>
13
+
14
+
15
+ "A/B testing":http://en.wikipedia.org/wiki/A/B_testing (or "split testing") are experiments you can run to compare the performance of different alternatives. A classical example is using an A/B test to compare two versions of a landing page, to find out which alternative leads to more registrations.
16
+
17
+ You can use A/B tests to gauge interest in a new feature, response to a feature change, improve the site's design and copy, and so forth. In spite of the name, you can use A/B tests to check out more than two alternatives.
18
+
19
+ bq. "If you are not embarrassed by the first version of your product, you’ve launched too late" -- Reid Hoffman, founder of LinkedIn
20
+
21
+
22
+ h3(#tf). True or False
23
+
24
+ Let's start with a simple experiment. We have this idea that a bigger sign-up link will increase the number of people who sign up for our service. Let's see how well our hypothesis holds.
25
+
26
+ We already have a "metric":metrics.html we're monitoring, and our experiment will measure against it:
27
+
28
+ <pre>
29
+ ab_test "Big singup link" do
30
+ description "Testing to see if a bigger sign-up link increases number of signups."
31
+ metrics :signup
32
+ end
33
+ </pre>
34
+
35
+ Next, we're going to show some of our visitors a bigger sign-up link:
36
+
37
+ <pre>
38
+ <% bigger = "font:14pt;font-weight:bold" if ab_test(:big_signup_link) %>
39
+ <%= link_to "Sign up", signup_url, style: bigger %>
40
+ </pre>
41
+
42
+ Approximately half the visitors to our site will see this link:
43
+
44
+ <notextile>
45
+ <a href="#">Sign up</a>
46
+ </notextile>
47
+
48
+ The other half will see this one:
49
+
50
+ <notextile>
51
+ <a href="#" style="font:14pt;font-weight:bold">Sign up</a>
52
+ </notextile>
53
+
54
+ An A/B test has two parts, we just covered the part which decides which alternative to show. The second part measures the effectiveness of each alternative. This happens as result of measuring the metric.
55
+
56
+ Remember that we're measuring signups, so we already have this in the code:
57
+
58
+ <pre>
59
+ class SignupController < ApplicationController
60
+ def signup
61
+ Account.create(params[:account])
62
+ track! :signup
63
+ end
64
+ end
65
+ </pre>
66
+
67
+
68
+ h3(#interpret). Interpreting the Results
69
+
70
+ We're going to let the experiment run for a while and track the results using "the dashboard":rails.html#dashboard, or by running the command @vanity report@.
71
+
72
+ Vanity splits the audience randomly -- using "cookies and other mechanisms":identity.html -- and records who got to see each alternative, and how many in each group converted (in our case, signed up). Dividing conversions by participants gives you the conversion rate.
73
+
74
+ !images/clear_winner.png!
75
+
76
+ Vanity will show the conversion rate for each alternative, and how that conversion compares to the worst performing alternative. In the example above, option A has 80.6% conversion rate, 11% more than option B's 72.6% conversion rate (72.6 * 111% ~ 80.6%).
77
+
78
+ (These large numbers are easily explained by the fact that this report was generated form made up data)
79
+
80
+ It takes only a handful of visits before you'll see one alternative clearly performing better than all others. That's a sign that you should continue running the experiment. You see, small sample size tend to give out random results.
81
+
82
+ To get actionable results, you want a large enough sample, more specifically, you want to look at the probability. Vanity picks the top two alternatives and "calculates a z-score":http://20bits.com/articles/statistical-analysis-and-ab-testing/ to determine the probability that the best alternative performed better than second best. It presents that probability which should tell you when is a good time to wrap up the experiment.
83
+
84
+ This is the part that gets most people confused about A/B testing. Let's say we ran an experiment with two alternatives and we notice that option B performs 50% better than option A (A * 150% = B). We calculate from the z-score a 90% probability.
85
+
86
+ "With 90% probability" does not mean 90% of the difference (50%), it does not mean B performs 45% better than A (90% * 50% = 45%). In fact, it doesn't tell us how well B performs relative to A. Option B may perform exceptionally well during the experiment, not so well later on.
87
+
88
+ The only thing "with 90% probability" tells us is the probability that option B is somewhat better than option A. And that means 10% probability that the results we're seeing are totally random and mean nothing in the long run. In other words: 9 out of 10 times, B is indeed better than A.
89
+
90
+ If you run the test longer to collect a larger sample size you'll see the probability increase to 95%, then 99% and finally 99.9%. That's big confidence in the outcome of the experiment, but it might take a long time to get there.
91
+
92
+ You might want to instead decide on some target probability (which could change from one experiment to another). For example, if you pick 95% as the target, you're going to act on the wrong conclusion 1 out of 20 times, but you're going to finish your experiments faster, which means you'll get to iterate quickly and more often. Fast iterations are one way to improve the quality of your software.
93
+
94
+ You'll want to read more about "A/B testing and statistical significance":http://www.cennydd.co.uk/2009/statistical-significance-other-ab-test-pitfalls/
95
+
96
+
97
+ h3(#multiple). Multiple Alternatives
98
+
99
+ Your A/B tests can have as many alternatives as you care, with two caveats. The more alternatives you have the larger the sample size you need, and so the longer it will take to find out the outcome of your experiment. You want alternatives that are significantly different from each other, testing two pricing options at $5 and $25 is fast, testing all the prices between $5 and $25 at $1 increments will take a long time to reach any conclusive result.
100
+
101
+ The second caveat is that right now Vanity only scores the two best performing alternatives. This may be an issue in some experiments, it may also be fixed in a future release.
102
+
103
+ To define an experiment with multiple alternatives:
104
+
105
+ <pre>
106
+ ab_test "Price options" do
107
+ description "Mirror, mirror on the wall, who's the better price of them all?"
108
+ alternatives 5, 15, 25
109
+ metrics :signup
110
+ end
111
+ </pre>
112
+
113
+ The @ab_test@ method returns the value of one of the chosen alternatives, so in your views you can write:
114
+
115
+ <pre>
116
+ <h2>Get started for only $<%= ab_test :price_options %> a month!</h2>
117
+ </pre>
118
+
119
+ !images/price_options.png!
120
+
121
+ If you don't given any values, Vanity will run your experiment with the values false and true. Here are other examples for rendering A/B tests with multiple values:
122
+
123
+ <pre>
124
+ def index
125
+ # alternatives are names of templates
126
+ render template: ab_test(:new_page)
127
+ end
128
+ </pre>
129
+
130
+ <pre>
131
+ <%= ab_test(:greeting) %> <%= current_user.name %>
132
+ </pre>
133
+
134
+ <pre>
135
+ <% ab_test :features do |count| %>
136
+ <%= count %> features to choose from!
137
+ <% end %>
138
+ </pre>
139
+
140
+
141
+ h3(#test). A/B Testing and Code Testing
142
+
143
+ If you're presenting more than one alternative to visitors of your site, you'll want to test more than one alternative. Don't let A/B testing become A/broken.
144
+
145
+ You can force a functional/integration test to choose a given alternative:
146
+
147
+ <pre>
148
+ def test_big_signup_link
149
+ experiment(:big_signup_link).chooses(true)
150
+ get :index
151
+ assert_select "a[href=/signup][style^=font:14pt]", "Sign up"
152
+ end
153
+ </pre>
154
+
155
+ Here's another example using Webrat:
156
+
157
+ <pre>
158
+ def test_price_option
159
+ [19, 25, 29].each do |price|
160
+ experiment(:price_options).chooses(price)
161
+ visit root_path
162
+ assert_contain "Get started for only $#{price} a month!"
163
+ end
164
+ end
165
+ </pre>
166
+
167
+ You'll also want to test each alternative visually, from your Web browser. For that you'll have to install the "the Dashboard":rails.html#dashboard, which lets you pick which alternative is shown to you:
168
+
169
+ !images/ab_in_dashboard.png!
170
+
171
+ Once the experiment is over, simply remove its definition from the experiments directory and run the test suite again. You'll see errors in all the places that touch the experiment (from failing to load it), pointing you to what parts of the code you need to remove/change.
172
+
173
+
174
+ h3(#decide). Let the Experiment Decide
175
+
176
+ Sample size and probability help you interpret the results, you can also use them to configure an experiment to automatically complete itself.
177
+
178
+ This experiment will conclude once it has 1000 participants for each alternative, or a leading alternative with probability of 95% or higher:
179
+
180
+ <pre>
181
+ ab_test "Self completed" do
182
+ description "This experiment will self-complete."
183
+ metrics :coolness
184
+ complete_if do
185
+ alternatives.all? { |alt| alt.participants >= 1000 } ||
186
+ (score.choice && score.choice.probability >= 95)
187
+ end
188
+ end
189
+ </pre>
190
+
191
+ When it reaches its end, the experiment will stop recording conversions, chose one of its alternatives as the outcome and switch every usage of @ab_test@ to that alternative.
192
+
193
+ By default Vanity will choose the alternative with the highest conversion rate. This is most often, but not always, the best outcome. Imagine an experiment where option B results in less conversions, but higher quality conversion that option A. Perhaps you're interested in option B conversions if you're losing no more than 20% compared to option A. Here's a way to write that outcome:
194
+
195
+ <pre>
196
+ ab_test "Self completed" do
197
+ description "This experiment will self-complete."
198
+ metrics :coolness
199
+
200
+ complete_if do
201
+ score(95).choice # only return choice with probability >= 95
202
+ end
203
+
204
+ outcome_is do
205
+ a, b = alternatives
206
+ b.conversion_rate >= 0.8 * a.conversion_rate ? b : a
207
+ end
208
+ end
209
+ </pre>
210
+