vanity 1.8.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA512:
3
+ data.tar.gz: cf97c9c3b23e4edf20b47b84fda3f7f31eed9f770ca16c3fd4850bc38b542a2c335fa91c3b68ec912ffa21ccfa14eb14f4b3fc370016bcf797852d6089f99075
4
+ metadata.gz: 2377c5146a59528271b9bfc244236e8d7a3b142676bafbee8d527a0037a7a1cab0bf065e086922f3b354ef4f798a17d0e0f9aaa5e0fd7f04367970a660fd691e
5
+ SHA1:
6
+ data.tar.gz: e713ef94697abdc32fb3dc46e960b83d703f0451
7
+ metadata.gz: ece1cada68e0f549be1ad0cf0fea02f7ce387c75
data/.travis.yml CHANGED
@@ -1,9 +1,13 @@
1
1
  language: ruby
2
2
  bundler_args: --without development
3
3
  script: 'rake appraisal test'
4
+ services:
5
+ - mongodb
4
6
  rvm:
5
7
  - 1.8.7
6
- - 1.9.2
8
+ - 1.9.3
9
+ - 2.0.0
10
+ - ruby-head
7
11
  env:
8
12
  - DB=mongodb
9
13
  - DB=redis
@@ -13,3 +17,7 @@ before_script:
13
17
  - "mysql -e 'create database vanity_test;' >/dev/null"
14
18
  - "rake appraisal:install"
15
19
  #- "psql -c 'create database vanity_test;' -U postgres >/dev/null"
20
+ matrix:
21
+ allow_failures:
22
+ - rvm: 2.0.0
23
+ - rvm: ruby-head
data/Appraisals CHANGED
@@ -1,7 +1,3 @@
1
- appraise "rails2" do
2
- gem "rails", "2.3.14"
3
- end
4
-
5
1
  appraise "rails3" do
6
2
  gem "rails", "3.0.11"
7
3
  gem "passenger", "~>3.0"
data/CHANGELOG CHANGED
@@ -1,9 +1,23 @@
1
+ == 1.8.1 (2013-10-05)
2
+
3
+ Added vanity_experiments helper for rails to return read only copy of active experiments (@iceberg901).
4
+ Fixed support for Mass Assignment on Rails 3.2 (@hcarver).
5
+ Fixed commandlin usage (@phillbaker).
6
+
7
+
8
+ == 1.8.0 (2012-03-12)
9
+
10
+ Improved support for Rails 3.1 and Rails 3.2. Thanks to masverba, Daan and IsaacLewis.
11
+
12
+ Finer Grained Exceptions for Missing Experiments (Nick Barnwell)
13
+
14
+
1
15
  == 1.7.1 (2011-09-30)
2
16
 
3
17
  Minor change in locating config file (Ariel Salomon)
4
18
 
5
19
  Allow for table names in specified :timestamps (Peter Kovacs)
6
-
20
+
7
21
  When a named scope includes a join, created_at can be ambiguous. Use
8
22
 
9
23
  metric "Sky is limit" do
@@ -28,7 +42,7 @@ connections are deprecated in favor of using the connection option with a URI
28
42
 
29
43
  1.7.0 adds support for ActionMailer, for example:
30
44
 
31
- class UserMailer < ActionMailer::Base
45
+ class UserMailer < ActionMailer::Base
32
46
  def invite_email(user)
33
47
  use_vanity_mailer user
34
48
  mail :to => user.email, :subject => ab_test(:invite_subject)
@@ -218,7 +232,7 @@ To view Google Analytics metrics from within Vanity, first make sure you are
218
232
  using Garb. For example, in your Gemfile:
219
233
 
220
234
  gem "vanity", "1.3.0"
221
- gem "garb", "0.5.0"
235
+ gem "garb", "0.5.0"
222
236
 
223
237
  Next, authenticate using your account credentials. For example, in your
224
238
  config/environments/production.rb:
@@ -310,7 +324,7 @@ Much thanks to Ian Sefferman for fixing issues with Ruby 1.8.7 and Rails support
310
324
  * Added: Metrics.
311
325
  * Added: Use Vanity.playground.mock! when running tests and you'd rather not access a live Redis server.
312
326
  * Changed: A/B tests now using metrics for tracking.
313
- * Changed: Now throwing NameError instead of LoadError when failing to load experiment/metric. NameError can be rescued on same line.
327
+ * Changed: Now throwing NameError instead of LoadError when failing to load experiment/metric. NameError can be rescued on same line.
314
328
  * Changed: New, easier URL mapping for Dashboard: map.vanity "/vanity", :controller=>:vanity.
315
329
  * Changed: All tests are green on Ruby 1.8.6, 1.8.7 and 1.9.1.
316
330
  * Changed: Switched to redis-rb from http://github.com/ezmobius/redis-rb.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
4
  gem "appraisal"
data/Gemfile.lock CHANGED
@@ -1,12 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- vanity (1.8.0)
4
+ vanity (1.8.1)
5
5
  redis (~> 2.0)
6
6
  redis-namespace (~> 1.0.0)
7
7
 
8
8
  GEM
9
- remote: http://rubygems.org/
9
+ remote: https://rubygems.org/
10
10
  specs:
11
11
  RedCloth (4.2.9)
12
12
  SystemTimer (1.2.3)
@@ -23,7 +23,7 @@ GEM
23
23
  addressable (2.2.7)
24
24
  albino (1.3.3)
25
25
  posix-spawn (>= 0.3.6)
26
- appraisal (0.4.1)
26
+ appraisal (0.5.2)
27
27
  bundler
28
28
  rake
29
29
  bson (1.6.0)
@@ -69,9 +69,9 @@ GEM
69
69
  activeresource (= 2.3.14)
70
70
  activesupport (= 2.3.14)
71
71
  rake (>= 0.8.3)
72
- rake (0.9.2.2)
72
+ rake (10.1.0)
73
73
  redis (2.2.2)
74
- redis-namespace (1.0.3)
74
+ redis-namespace (1.0.4)
75
75
  redis (< 3.0.0)
76
76
  shoulda (3.0.1)
77
77
  shoulda-context (~> 1.0.0)
data/README.rdoc CHANGED
@@ -1,16 +1,21 @@
1
+ = Vanity
2
+ {<img src="https://travis-ci.org/assaf/vanity.png?branch=feature/readme-update" alt="Build Status" />}[https://travis-ci.org/assaf/vanity]
3
+
1
4
  Vanity is an Experiment Driven Development framework for Rails.
2
5
 
3
6
  * All about Vanity: http://vanity.labnotes.org
4
- * On github: http://github.com/assaf/vanity
7
+ * On Github: http://github.com/assaf/vanity
5
8
 
6
9
  http://farm3.static.flickr.com/2540/4099665871_497f274f68_o.jpg
7
10
 
8
11
 
9
12
  == A/B Testing With Rails (In 5 Easy Steps)
10
13
 
11
- <b>Step 1:</b> Start using Vanity in your Rails application:
14
+ === <b>Step 1:</b> Start using Vanity in your Rails application
15
+
16
+ ==== Step 1.1
12
17
 
13
- == Rails 2.x configuration
18
+ ===== Rails 2.x installation
14
19
 
15
20
  Rails::Initializer.run do |config|
16
21
  gem.config "vanity"
@@ -19,39 +24,49 @@ http://farm3.static.flickr.com/2540/4099665871_497f274f68_o.jpg
19
24
  require "vanity"
20
25
  end
21
26
  end
22
-
23
- == Rails 3 configuration
27
+
28
+ ===== Rails 3 installation
24
29
 
25
30
  Add to your Gemfile:
26
-
31
+
27
32
  gem "vanity"
28
33
 
29
- If using a relational database, run the generator and migrations to create the database schema:
34
+ ===== Rails 4 installation
35
+
36
+ Coming soon!
37
+
38
+ ==== Step 1.2
39
+
40
+ Choose a method to store experiment results: for Redis or an external tracking mechanism, migrations aren't needed. Mongo is also available. To use a relational database, via ActiveRecord, run the generator and migrations to create the database schema:
30
41
 
31
42
  $ rails generate vanity
32
43
  $ rake db:migrate
33
-
34
- Add to your application controller:
44
+
45
+ ==== Step 1.3
46
+
47
+ add to your application controller:
35
48
 
36
49
  class ApplicationController < ActionController::Base
37
50
  use_vanity :current_user
38
51
  end
39
52
 
40
- <b>Step 2:</b> Define your first A/B test. This experiment goes in the file <code>experiments/price_options.rb</code>:
53
+ === <b>Step 2:</b> Define your first A/B test
54
+
55
+ This experiment goes in the file <code>experiments/price_options.rb</code>:
41
56
 
42
57
  ab_test "Price options" do
43
58
  description "Mirror, mirror on the wall, who's the better price of all?"
44
59
  alternatives 19, 25, 29
45
60
  metrics :signups
46
61
  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 "signups.rb". The contents of the file can describe the signup metric, refer to the "Metrics" Vanity documentation page for an example.
49
62
 
50
- <b>Step 3:</b> Present the different options to your users:
63
+ 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 "signups.rb". The contents of the file can describe the signup metric, refer to the "Metrics" Vanity documentation page for an example.
64
+
65
+ === <b>Step 3:</b> Present the different options to your users
51
66
 
52
67
  <h2>Get started for only $<%= ab_test :price_options %> a month!</h2>
53
68
 
54
- <b>Step 4:</b> Measure conversion:
69
+ === <b>Step 4:</b> Measure conversion
55
70
 
56
71
  class SignupController < ApplicationController
57
72
  def signup
@@ -65,21 +80,11 @@ Add to your application controller:
65
80
  end
66
81
  end
67
82
 
68
- <b>Step 5:</b> Check the report:
83
+ === <b>Step 5:</b> Check the report:
69
84
 
70
85
  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
86
 
87
+ Or see instructions for viewing the results using Rails.
83
88
 
84
89
  == Registering participants with Javascript
85
90
 
@@ -89,6 +94,16 @@ If robots or spiders make up a significant portion of your sites traffic they ca
89
94
  * 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
95
  * 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
96
 
97
+ == Compatibility Matrix
98
+
99
+ Running Rails 2 on Ruby 2? Vanity probably won't work for you. But here's what does:
100
+
101
+ Persistence: Redis, Mongo, ActiveRecord
102
+ Rails: 2.3, 3, 3.1, 3.2
103
+ Ruby: 1.8.7, 1.9.3
104
+
105
+ NOTE: Support for Rails 4 and Ruby 2.0 are coming soon!
106
+
92
107
  == Contributing
93
108
 
94
109
  * Fork the project
@@ -102,7 +117,7 @@ If robots or spiders make up a significant portion of your sites traffic they ca
102
117
 
103
118
  == Credits/License
104
119
 
105
- Original code, copyright of Assaf Arkin, released under the MIT license.
120
+ Original code, copyright of Assaf Arkin, released under the MIT license.
106
121
 
107
122
  Documentation available under the Creative Commons Attribution license.
108
123
 
data/Rakefile CHANGED
@@ -17,7 +17,7 @@ task :install=>:build do
17
17
  end
18
18
 
19
19
  desc "Push new release to gemcutter and git tag"
20
- task :push=>["test:all", "build"] do
20
+ task :push=>["build"] do
21
21
  sh "git push"
22
22
  puts "Tagging version #{spec.version} .."
23
23
  sh "git tag v#{spec.version}"
@@ -33,7 +33,7 @@ desc "Test everything"
33
33
  task "test:all"=>"test:adapters"
34
34
 
35
35
  # Ruby versions we're testing with.
36
- RUBIES = %w{1.8.7 1.9.2}
36
+ RUBIES = %w{1.8.7 1.9.2 2.0.0}
37
37
 
38
38
  # Use rake test:rubies to run all combination of tests (see test:adapters) using
39
39
  # all the versions of Ruby specified in RUBIES. Or to test a specific version of
data/bin/vanity CHANGED
@@ -1,16 +1,69 @@
1
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
- #
2
+ # Is there a better way to detect Rails?
3
+ if File.exist?("config/boot.rb") && File.exist?("config/environment.rb")
4
+ require "./config/environment"
5
+ else
6
+ path = File.expand_path("../lib", File.dirname(__FILE__))
7
+ $LOAD_PATH.unshift path unless $LOAD_PATH.include?(path)
8
+ require "vanity"
9
+ end
8
10
 
9
- require 'pathname'
10
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
- Pathname.new(__FILE__).realpath)
11
+ require "optparse"
12
12
 
13
- require 'rubygems'
14
- require 'bundler/setup'
13
+ playground = Vanity.playground
14
+ options = Struct.new(:output).new
15
+ opts = OptionParser.new("", 24, " ") do |opts|
16
+ opts.banner = "Usage: #{File.basename($0)} [options] command\n"
17
+ opts.banner << "Commands:\n"
18
+ opts.banner << " list List all experiments and metrics\n"
19
+ opts.banner << " report Report on all running experiments/metrics\n"
20
+ opts.banner << " upgrade Upgrade your database when deploying new release\n"
15
21
 
16
- load Gem.bin_path('vanity', 'vanity')
22
+ opts.separator ""
23
+ opts.separator "Reporting options:"
24
+ opts.on "--output FILE", "Write report to this file (default: stdout)" do |path|
25
+ options.output = path
26
+ end
27
+
28
+ opts.separator ""
29
+ opts.separator "Common options:"
30
+ opts.on "--load_path PATH", "Path to experiments directory (default: #{playground.load_path})" do |path|
31
+ playground.load_path = path
32
+ end
33
+ opts.on "-d", "--database url", "Database connection URL (e.g. redis:/localhost:6379)" do |conn|
34
+ playground.establish_connection conn
35
+ end
36
+ opts.on "--redis HOST:PORT:DB", "DEPRECATED: Redis server host (default: localhost:6379)" do |redis|
37
+ host, port, db = redis.split(":")
38
+ playground.establish_connection "redis:/#{host}:#{port}/#{db}"
39
+ end
40
+ opts.on_tail "-h", "--help", "Show this message" do
41
+ puts opts.to_s.gsub(/^.*DEPRECATED.*$/s, '')
42
+ exit
43
+ end
44
+ opts.on_tail "-v", "--version", "Show version" do
45
+ puts "Vanity #{Vanity::Version::STRING}"
46
+ exit
47
+ end
48
+ end
49
+
50
+ opts.parse!(ARGV)
51
+ if ARGV.empty?
52
+ puts opts.banner
53
+ exit
54
+ end
55
+
56
+ ARGV.each do |cmd|
57
+ case cmd
58
+ when "report"
59
+ require "vanity/commands/report"
60
+ Vanity::Commands.report options.output
61
+ when "list"
62
+ require "vanity/commands/list"
63
+ Vanity::Commands.list
64
+ when "upgrade"
65
+ require "vanity/commands/upgrade"
66
+ Vanity::Commands.upgrade
67
+ else puts "No such command: #{cmd}"
68
+ end
69
+ end
@@ -1,6 +1,6 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source :rubygems
3
+ source "https://rubygems.org"
4
4
 
5
5
  gem "appraisal"
6
6
  gem "garb"
@@ -1,12 +1,12 @@
1
1
  PATH
2
- remote: /Users/dougcole/workspace/vanity
2
+ remote: /Users/phill/Development/ruby/vanity
3
3
  specs:
4
- vanity (1.8.0)
4
+ vanity (1.8.1)
5
5
  redis (~> 2.0)
6
6
  redis-namespace (~> 1.0.0)
7
7
 
8
8
  GEM
9
- remote: http://rubygems.org/
9
+ remote: https://rubygems.org/
10
10
  specs:
11
11
  SystemTimer (1.2.3)
12
12
  abstract (1.0.0)
@@ -97,7 +97,7 @@ GEM
97
97
  rdoc (3.12)
98
98
  json (~> 1.4)
99
99
  redis (2.2.2)
100
- redis-namespace (1.0.3)
100
+ redis-namespace (1.0.4)
101
101
  redis (< 3.0.0)
102
102
  shoulda (3.0.1)
103
103
  shoulda-context (~> 1.0.0)
@@ -1,6 +1,6 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source :rubygems
3
+ source "https://rubygems.org"
4
4
 
5
5
  gem "appraisal"
6
6
  gem "garb"
@@ -1,12 +1,12 @@
1
1
  PATH
2
- remote: /Users/dougcole/workspace/vanity
2
+ remote: /Users/phill/Development/ruby/vanity
3
3
  specs:
4
- vanity (1.8.0)
4
+ vanity (1.8.1)
5
5
  redis (~> 2.0)
6
6
  redis-namespace (~> 1.0.0)
7
7
 
8
8
  GEM
9
- remote: http://rubygems.org/
9
+ remote: https://rubygems.org/
10
10
  specs:
11
11
  SystemTimer (1.2.3)
12
12
  actionmailer (3.1.3)
@@ -103,7 +103,7 @@ GEM
103
103
  rdoc (3.12)
104
104
  json (~> 1.4)
105
105
  redis (2.2.2)
106
- redis-namespace (1.0.3)
106
+ redis-namespace (1.0.4)
107
107
  redis (< 3.0.0)
108
108
  shoulda (3.0.1)
109
109
  shoulda-context (~> 1.0.0)
@@ -1,6 +1,6 @@
1
1
  # This file was generated by Appraisal
2
2
 
3
- source :rubygems
3
+ source "https://rubygems.org"
4
4
 
5
5
  gem "appraisal"
6
6
  gem "garb"
@@ -1,12 +1,12 @@
1
1
  PATH
2
- remote: /Users/dougcole/workspace/vanity
2
+ remote: /Users/phill/Development/ruby/vanity
3
3
  specs:
4
- vanity (1.8.0)
4
+ vanity (1.8.1)
5
5
  redis (~> 2.0)
6
6
  redis-namespace (~> 1.0.0)
7
7
 
8
8
  GEM
9
- remote: http://rubygems.org/
9
+ remote: https://rubygems.org/
10
10
  specs:
11
11
  SystemTimer (1.2.3)
12
12
  actionmailer (3.2.1)
@@ -101,7 +101,7 @@ GEM
101
101
  rdoc (3.12)
102
102
  json (~> 1.4)
103
103
  redis (2.2.2)
104
- redis-namespace (1.0.3)
104
+ redis-namespace (1.0.4)
105
105
  redis (< 3.0.0)
106
106
  shoulda (3.0.1)
107
107
  shoulda-context (~> 1.0.0)
@@ -32,6 +32,8 @@ module Vanity
32
32
 
33
33
  # Metric value
34
34
  class VanityMetricValue < VanityRecord
35
+ attr_accessible :date, :index, :value if respond_to?(:attr_accessible)
36
+
35
37
  self.table_name = :vanity_metric_values
36
38
  belongs_to :vanity_metric
37
39
  end
@@ -40,6 +42,7 @@ module Vanity
40
42
  class VanityExperiment < VanityRecord
41
43
  self.table_name = :vanity_experiments
42
44
  has_many :vanity_conversions, :dependent => :destroy
45
+ attr_accessible :experiment_id if respond_to?(:attr_accessible)
43
46
 
44
47
  # Finds or creates the experiment
45
48
  def self.retrieve(experiment)
@@ -61,6 +64,7 @@ module Vanity
61
64
  # Participant model
62
65
  class VanityParticipant < VanityRecord
63
66
  self.table_name = :vanity_participants
67
+ attr_accessible :experiment_id, :identity, :seen, :shown, :converted if respond_to?(:attr_accessible)
64
68
 
65
69
  # Finds the participant by experiment and identity. If
66
70
  # create is true then it will create the participant
@@ -2,30 +2,28 @@ require "erb"
2
2
  require "cgi"
3
3
 
4
4
  module Vanity
5
-
5
+
6
6
  # Render method available to templates (when used by Vanity command line,
7
7
  # outside Rails).
8
8
  module Render
9
-
9
+
10
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 })
11
+ def render(path_or_options, locals = {})
12
+ if path_or_options.respond_to?(:keys)
13
+ render_erb(
14
+ path_or_options[:file] || path_or_options[:partial],
15
+ path_or_options[:locals]
16
+ )
17
+ else
18
+ render_erb(path_or_options, locals)
19
+ end
22
20
  end
23
21
 
24
22
  # Escape HTML.
25
23
  def vanity_h(html)
26
24
  CGI.escapeHTML(html.to_s)
27
25
  end
28
-
26
+
29
27
  def vanity_html_safe(text)
30
28
  text
31
29
  end
@@ -38,6 +36,29 @@ module Vanity
38
36
  gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') + # 1 newline -> br
39
37
  "</p>"
40
38
  end
39
+
40
+ protected
41
+
42
+ def render_erb(path, locals = {})
43
+ locals[:playground] = self
44
+ keys = locals.keys
45
+ struct = Struct.new(*keys)
46
+ struct.send :include, Render
47
+ locals = struct.new(*locals.values_at(*keys))
48
+ dir, base = File.split(path)
49
+ path = File.join(dir, partialize(base))
50
+ erb = ERB.new(File.read(path), nil, '<>')
51
+ erb.filename = path
52
+ erb.result(locals.instance_eval { binding })
53
+ end
54
+
55
+ def partialize(template_name)
56
+ if template_name[0] != '_'
57
+ "_#{template_name}"
58
+ else
59
+ template_name
60
+ end
61
+ end
41
62
  end
42
63
 
43
64
  # Commands available when running Vanity from the command line (see bin/vanity).
@@ -34,7 +34,7 @@ module Vanity
34
34
  class Base
35
35
 
36
36
  class << self
37
-
37
+
38
38
  # Returns the type of this class as a symbol (e.g. AbTest becomes
39
39
  # ab_test).
40
40
  def type
@@ -94,7 +94,7 @@ module Vanity
94
94
  def type
95
95
  self.class.type
96
96
  end
97
-
97
+
98
98
  # Defines how we obtain an identity for the current experiment. Usually
99
99
  # Vanity gets the identity form a session object (see use_vanity), but
100
100
  # there are cases where you want a particular experiment to use a
@@ -126,7 +126,7 @@ module Vanity
126
126
  @description = text if text
127
127
  @description
128
128
  end
129
-
129
+
130
130
 
131
131
  # -- Experiment completion --
132
132
 
@@ -152,7 +152,7 @@ module Vanity
152
152
  def completed_at
153
153
  @completed_at ||= connection.get_experiment_completed_at(@id)
154
154
  end
155
-
155
+
156
156
  # Returns true if experiment active, false if completed.
157
157
  def active?
158
158
  !@playground.collecting? || !connection.is_experiment_completed?(@id)
@@ -180,7 +180,7 @@ module Vanity
180
180
 
181
181
  def default_identify(context)
182
182
  raise "No Vanity.context" unless context
183
- raise "Vanity.context does not respond to vanity_identity" unless context.respond_to?(:vanity_identity)
183
+ raise "Vanity.context does not respond to vanity_identity" unless context.respond_to?(:vanity_identity, true)
184
184
  context.send(:vanity_identity) or raise "Vanity.context.vanity_identity - no identity"
185
185
  end
186
186
 
@@ -195,7 +195,7 @@ module Vanity
195
195
  end
196
196
  end
197
197
  end
198
-
198
+
199
199
  # Returns key for this experiment, or with an argument, return a key
200
200
  # using the experiment as the namespace. Examples:
201
201
  # key => "vanity:experiments:green_button"
@@ -208,11 +208,11 @@ module Vanity
208
208
  def connection
209
209
  @playground.connection
210
210
  end
211
-
211
+
212
212
  end
213
213
  end
214
214
 
215
215
  class NoExperimentError < NameError
216
216
  end
217
-
217
+
218
218
  end
@@ -1,5 +1,5 @@
1
1
  module Vanity
2
- module Rails #:nodoc:
2
+ module Rails
3
3
  def self.load!
4
4
  Vanity.playground.load_path = ::Rails.root + Vanity.playground.load_path
5
5
  Vanity.playground.logger ||= ::Rails.logger
@@ -93,8 +93,8 @@ module Vanity
93
93
  end
94
94
  protected :use_vanity_mailer
95
95
  end
96
-
97
-
96
+
97
+
98
98
  # Vanity needs these filters. They are includes in ActionController and
99
99
  # automatically added when you use #use_vanity in your controller.
100
100
  module Filters
@@ -142,7 +142,7 @@ module Vanity
142
142
  def vanity_reload_filter
143
143
  Vanity.playground.reload!
144
144
  end
145
-
145
+
146
146
  # Filter to track metrics
147
147
  # pass _track param along to call track! on that alternative
148
148
  def vanity_track_filter
@@ -150,7 +150,7 @@ module Vanity
150
150
  track! params[:_track]
151
151
  end
152
152
  end
153
-
153
+
154
154
  protected :vanity_context_filter, :vanity_query_parameter_filter, :vanity_reload_filter
155
155
  end
156
156
 
@@ -182,14 +182,10 @@ module Vanity
182
182
  # <%= count %> features to choose from!
183
183
  # <% end %>
184
184
  def ab_test(name, &block)
185
- if Vanity.playground.using_js?
186
- @_vanity_experiments ||= {}
187
- @_vanity_experiments[name] ||= Vanity.playground.experiment(name.to_sym).choose
188
- value = @_vanity_experiments[name].value
189
- else
190
- value = Vanity.playground.experiment(name.to_sym).choose.value
191
- end
192
-
185
+ @_vanity_experiments ||= {}
186
+ @_vanity_experiments[name] ||= Vanity.playground.experiment(name.to_sym).choose
187
+ value = @_vanity_experiments[name].value
188
+
193
189
  if block
194
190
  content = capture(value, &block)
195
191
  if defined?(block_called_from_erb?) && block_called_from_erb?(block)
@@ -201,13 +197,13 @@ module Vanity
201
197
  value
202
198
  end
203
199
  end
204
-
200
+
205
201
  # Generate url with the identity of the current user and the metric to track on click
206
202
  def vanity_track_url_for(identity, metric, options = {})
207
203
  options = options.merge(:_identity => identity, :_track => metric)
208
204
  url_for(options)
209
205
  end
210
-
206
+
211
207
  # Generate url with the fingerprint for the current Vanity experiment
212
208
  def vanity_tracking_image(identity, metric, options = {})
213
209
  options = options.merge(:controller => :vanity, :action => :image, :_identity => identity, :_track => metric)
@@ -215,7 +211,7 @@ module Vanity
215
211
  end
216
212
 
217
213
  def vanity_js
218
- return if @_vanity_experiments.nil?
214
+ return if @_vanity_experiments.nil? || @_vanity_experiments.empty?
219
215
  javascript_tag do
220
216
  render :file => Vanity.template("_vanity.js.erb")
221
217
  end
@@ -236,6 +232,34 @@ module Vanity
236
232
  def vanity_simple_format(text, html_options={})
237
233
  vanity_html_safe(simple_format(text, html_options))
238
234
  end
235
+
236
+ # Return a copy of the active experiments on a page
237
+ #
238
+ # @example Render some info about each active experiment in development mode
239
+ # <% if Rails.env.development? %>
240
+ # <% vanity_experiments.each do |name, alternative| %>
241
+ # <span>Participating in <%= name %>, seeing <%= alternative %>:<%= alternative.value %> </span>
242
+ # <% end %>
243
+ # <% end %>
244
+ # @example Push experiment values into javascript for use there
245
+ # <% experiments = vanity_experiments %>
246
+ # <% unless experiments.empty? %>
247
+ # <script>
248
+ # <% experiments.each do |name, alternative| %>
249
+ # myAbTests.<%= name.to_s.camelize(:lower) %> = '<%= alternative.value %>';
250
+ # <% end %>
251
+ # </script>
252
+ # <% end %>
253
+ def vanity_experiments
254
+ @_vanity_experiments ||= {}
255
+ experiments = {}
256
+
257
+ @_vanity_experiments.each do |name, alternative|
258
+ experiments[name] = alternative.clone
259
+ end
260
+
261
+ return experiments
262
+ end
239
263
  end
240
264
 
241
265
 
@@ -269,7 +293,7 @@ module Vanity
269
293
  render :status => 200, :nothing => true
270
294
  end
271
295
  end
272
-
296
+
273
297
  module TrackingImage
274
298
  def image
275
299
  # send image
@@ -1,20 +1,16 @@
1
- <% unless @_vanity_experiments.empty? %>
2
- var httpRequest;
3
- <%
4
- @_vanity_experiments.each do |name, alternative|
5
- %>
6
- var params = "e=<%= name %>&a=<%= alternative.id %>&authenticity_token=" + encodeURIComponent("<%= form_authenticity_token %>");
7
- if (window.XMLHttpRequest) { // Mozilla, Safari, ...
8
- httpRequest = new XMLHttpRequest();
9
- } else if (window.ActiveXObject) { // IE
10
- try { httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); }
11
- catch (e) { }
12
- }
13
- if (httpRequest) {
14
- httpRequest.open('POST', "<%= Vanity.playground.add_participant_path %>", true);
15
- httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
16
- httpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest");
17
- httpRequest.send(params);
18
- }
19
- <% end %>
20
- <% end %>
1
+ var httpRequest;
2
+ <% @_vanity_experiments.each do |name, alternative| %>
3
+ var params = "e=<%= name %>&a=<%= alternative.id %>&authenticity_token=" + encodeURIComponent("<%= form_authenticity_token %>");
4
+ if (window.XMLHttpRequest) { // Mozilla, Safari, ...
5
+ httpRequest = new XMLHttpRequest();
6
+ } else if (window.ActiveXObject) { // IE
7
+ try { httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); }
8
+ catch (e) { }
9
+ }
10
+ if (httpRequest) {
11
+ httpRequest.open('POST', "<%= Vanity.playground.add_participant_path %>", true);
12
+ httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
13
+ httpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest");
14
+ httpRequest.send(params);
15
+ }
16
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  module Vanity
2
- VERSION = "1.8.0"
2
+ VERSION = "1.8.1"
3
3
 
4
4
  module Version
5
5
  version = VERSION.to_s.split(".").map { |i| i.to_i }
data/test/cli_test.rb ADDED
@@ -0,0 +1,53 @@
1
+ require "test/test_helper"
2
+
3
+ class PlaygroundTest < Test::Unit::TestCase
4
+
5
+ def test_responds_to_version
6
+ (RUBY_VERSION == "1.8.7" ? Object : IO).any_instance.expects(:puts)
7
+ ARGV.clear
8
+ ARGV << '--version'
9
+ load "bin/vanity"
10
+ rescue SystemExit => e
11
+ assert e.status == 0
12
+ end
13
+
14
+ def test_responds_to_help
15
+ (RUBY_VERSION == "1.8.7" ? Object : IO).any_instance.expects(:puts)
16
+ ARGV.clear
17
+ ARGV << '--help'
18
+ load "bin/vanity"
19
+ rescue SystemExit => e
20
+ assert e.status == 0
21
+ end
22
+
23
+ def test_responds_to_list
24
+ require "vanity/commands/list"
25
+ Vanity::Commands.expects(:list)
26
+ ARGV.clear
27
+ ARGV << 'list'
28
+ load "bin/vanity"
29
+ rescue SystemExit => e
30
+ assert e.status == 0
31
+ end
32
+
33
+ def test_responds_to_report
34
+ require "vanity/commands/report"
35
+ Vanity::Commands.expects(:report)
36
+ ARGV.clear
37
+ ARGV << 'report'
38
+ load "bin/vanity"
39
+ rescue SystemExit => e
40
+ assert e.status == 0
41
+ end
42
+
43
+ def test_responds_to_unknown_commands
44
+ require "vanity/commands/upgrade"
45
+ Vanity::Commands.expects(:upgrade)
46
+ ARGV.clear
47
+ ARGV << 'upgrade'
48
+ load "bin/vanity"
49
+ rescue SystemExit => e
50
+ assert e.status == 0
51
+ end
52
+
53
+ end
@@ -35,6 +35,12 @@ module Dummy
35
35
  # JavaScript files you want as :defaults (application.js is always included).
36
36
  # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
37
37
 
38
+ # Enforce whitelist mode for mass assignment.
39
+ # This will create an empty whitelist of attributes available for mass-assignment for all models
40
+ # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
41
+ # parameters by using an attr_accessible or attr_protected declaration.
42
+ config.active_record.whitelist_attributes = true if ActiveRecord::Base.respond_to?(:whitelist_attributes=)
43
+
38
44
  # Configure the default encoding used in templates for Ruby 1.9.
39
45
  config.encoding = "utf-8"
40
46
 
@@ -21,18 +21,39 @@ class RailsHelperTest < ActionView::TestCase
21
21
  Vanity.playground.use_js!
22
22
  result = ab_test(:pie_or_cake)
23
23
  assert [:pie, :cake].include?(result)
24
- 10.times do
24
+ 10.times do
25
25
  assert result == ab_test(:pie_or_cake)
26
26
  end
27
27
  end
28
-
28
+
29
29
  def test_vanity_track_url_for_returns_url_with_identity_and_metrics
30
30
  self.expects(:url_for).with(:controller => "controller", :action => "action", :_identity => '123', :_track => :sugar_high)
31
31
  vanity_track_url_for("123", :sugar_high, :controller => "controller", :action => "action")
32
32
  end
33
-
33
+
34
34
  def test_vanity_tracking_image
35
35
  self.expects(:url_for).with(:controller => :vanity, :action => :image, :_identity => '123', :_track => :sugar_high).returns("/url")
36
36
  assert_equal image_tag("/url", :width => "1px", :height => "1px", :alt => ""), vanity_tracking_image("123", :sugar_high, options = {})
37
37
  end
38
+
39
+ def test_vanity_experiments
40
+ result = ab_test(:pie_or_cake)
41
+ test_description = ''
42
+ vanity_experiments.each do |name, alternative|
43
+ test_description += "Experiment #{name} chose #{alternative.value}"
44
+ end
45
+ assert_equal test_description, "Experiment pie_or_cake chose #{result}"
46
+ end
47
+
48
+ def test_vanity_experiments_return_is_read_only
49
+ result = ab_test(:pie_or_cake)
50
+ vanity_experiments_returns = vanity_experiments
51
+ vanity_experiments_returns[:some_new_key] = 'some new value'
52
+ assert_equal vanity_experiments.keys.length, 1
53
+ end
54
+
55
+ def test_vanity_experiments_returns_empty_hash_when_no_experiments
56
+ assert_equal vanity_experiments, {}
57
+ end
58
+
38
59
  end
metadata CHANGED
@@ -1,47 +1,46 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: vanity
3
- version: !ruby/object:Gem::Version
4
- version: 1.8.0
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.8.1
6
5
  platform: ruby
7
- authors:
6
+ authors:
8
7
  - Assaf Arkin
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-03-12 00:00:00.000000000Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
11
+
12
+ date: 2013-10-05 00:00:00 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
15
  name: redis
16
- requirement: &70288496150040 !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
16
+ prerelease: false
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ requirements:
19
19
  - - ~>
20
- - !ruby/object:Gem::Version
21
- version: '2.0'
20
+ - !ruby/object:Gem::Version
21
+ version: "2.0"
22
22
  type: :runtime
23
- prerelease: false
24
- version_requirements: *70288496150040
25
- - !ruby/object:Gem::Dependency
23
+ version_requirements: *id001
24
+ - !ruby/object:Gem::Dependency
26
25
  name: redis-namespace
27
- requirement: &70288496149580 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
26
+ prerelease: false
27
+ requirement: &id002 !ruby/object:Gem::Requirement
28
+ requirements:
30
29
  - - ~>
31
- - !ruby/object:Gem::Version
30
+ - !ruby/object:Gem::Version
32
31
  version: 1.0.0
33
32
  type: :runtime
34
- prerelease: false
35
- version_requirements: *70288496149580
33
+ version_requirements: *id002
36
34
  description: Mirror, mirror on the wall ...
37
35
  email: assaf@labnotes.org
38
- executables:
36
+ executables:
39
37
  - vanity
40
38
  extensions: []
41
- extra_rdoc_files:
39
+
40
+ extra_rdoc_files:
42
41
  - README.rdoc
43
42
  - CHANGELOG
44
- files:
43
+ files:
45
44
  - .autotest
46
45
  - .gitignore
47
46
  - .rvmrc
@@ -79,8 +78,6 @@ files:
79
78
  - doc/metrics.textile
80
79
  - doc/rails.textile
81
80
  - doc/site.js
82
- - gemfiles/rails2.gemfile
83
- - gemfiles/rails2.gemfile.lock
84
81
  - gemfiles/rails3.gemfile
85
82
  - gemfiles/rails3.gemfile.lock
86
83
  - gemfiles/rails31.gemfile
@@ -125,6 +122,7 @@ files:
125
122
  - lib/vanity/templates/vanity.js
126
123
  - lib/vanity/version.rb
127
124
  - test/adapters/redis_adapter_test.rb
125
+ - test/cli_test.rb
128
126
  - test/dummy/Rakefile
129
127
  - test/dummy/app/controllers/application_controller.rb
130
128
  - test/dummy/app/helpers/application_helper.rb
@@ -176,36 +174,39 @@ files:
176
174
  - vanity.gemspec
177
175
  homepage: http://vanity.labnotes.org
178
176
  licenses: []
177
+
178
+ metadata: {}
179
+
179
180
  post_install_message: To get started run vanity --help
180
- rdoc_options:
181
+ rdoc_options:
181
182
  - --title
182
- - Vanity 1.8.0
183
+ - Vanity 1.8.1
183
184
  - --main
184
185
  - README.rdoc
185
186
  - --webcvs
186
187
  - http://github.com/assaf/vanity
187
- require_paths:
188
+ require_paths:
188
189
  - lib
189
- required_ruby_version: !ruby/object:Gem::Requirement
190
- none: false
191
- requirements:
192
- - - ! '>='
193
- - !ruby/object:Gem::Version
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
194
  version: 1.8.7
195
- required_rubygems_version: !ruby/object:Gem::Requirement
196
- none: false
197
- requirements:
198
- - - ! '>='
199
- - !ruby/object:Gem::Version
200
- version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: "0"
201
200
  requirements: []
201
+
202
202
  rubyforge_project:
203
- rubygems_version: 1.8.10
203
+ rubygems_version: 2.1.5
204
204
  signing_key:
205
- specification_version: 3
205
+ specification_version: 4
206
206
  summary: Experience Driven Development framework for Ruby
207
- test_files:
207
+ test_files:
208
208
  - test/adapters/redis_adapter_test.rb
209
+ - test/cli_test.rb
209
210
  - test/dummy/Rakefile
210
211
  - test/dummy/app/controllers/application_controller.rb
211
212
  - test/dummy/app/helpers/application_helper.rb
@@ -1,20 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source :rubygems
4
-
5
- gem "appraisal"
6
- gem "garb"
7
- gem "mocha"
8
- gem "mongo"
9
- gem "bson_ext"
10
- gem "mysql"
11
- gem "passenger", "~>2.0"
12
- gem "pg"
13
- gem "rack"
14
- gem "shoulda"
15
- gem "timecop"
16
- gem "webmock"
17
- gem "SystemTimer", "1.2.3", :platforms=>:mri_18
18
- gem "rails", "2.3.14"
19
-
20
- gemspec :path=>"../"
@@ -1,85 +0,0 @@
1
- PATH
2
- remote: /Users/dougcole/workspace/vanity
3
- specs:
4
- vanity (1.8.0)
5
- redis (~> 2.0)
6
- redis-namespace (~> 1.0.0)
7
-
8
- GEM
9
- remote: http://rubygems.org/
10
- specs:
11
- SystemTimer (1.2.3)
12
- actionmailer (2.3.14)
13
- actionpack (= 2.3.14)
14
- actionpack (2.3.14)
15
- activesupport (= 2.3.14)
16
- rack (~> 1.1.0)
17
- activerecord (2.3.14)
18
- activesupport (= 2.3.14)
19
- activeresource (2.3.14)
20
- activesupport (= 2.3.14)
21
- activesupport (2.3.14)
22
- addressable (2.2.7)
23
- appraisal (0.4.1)
24
- bundler
25
- rake
26
- bson (1.6.0)
27
- bson_ext (1.6.0)
28
- bson (= 1.6.0)
29
- crack (0.3.1)
30
- fastthread (1.0.7)
31
- garb (0.9.1)
32
- activesupport (>= 2.2.0)
33
- crack (>= 0.1.6)
34
- metaclass (0.0.1)
35
- mocha (0.10.5)
36
- metaclass (~> 0.0.1)
37
- mongo (1.6.0)
38
- bson (= 1.6.0)
39
- mysql (2.8.1)
40
- passenger (2.2.15)
41
- fastthread (>= 1.0.1)
42
- rack
43
- rake (>= 0.8.1)
44
- pg (0.13.2)
45
- rack (1.1.3)
46
- rails (2.3.14)
47
- actionmailer (= 2.3.14)
48
- actionpack (= 2.3.14)
49
- activerecord (= 2.3.14)
50
- activeresource (= 2.3.14)
51
- activesupport (= 2.3.14)
52
- rake (>= 0.8.3)
53
- rake (0.9.2.2)
54
- redis (2.2.2)
55
- redis-namespace (1.0.3)
56
- redis (< 3.0.0)
57
- shoulda (3.0.1)
58
- shoulda-context (~> 1.0.0)
59
- shoulda-matchers (~> 1.0.0)
60
- shoulda-context (1.0.0)
61
- shoulda-matchers (1.0.0)
62
- timecop (0.3.5)
63
- webmock (1.8.0)
64
- addressable (>= 2.2.7)
65
- crack (>= 0.1.7)
66
-
67
- PLATFORMS
68
- ruby
69
-
70
- DEPENDENCIES
71
- SystemTimer (= 1.2.3)
72
- appraisal
73
- bson_ext
74
- garb
75
- mocha
76
- mongo
77
- mysql
78
- passenger (~> 2.0)
79
- pg
80
- rack
81
- rails (= 2.3.14)
82
- shoulda
83
- timecop
84
- vanity!
85
- webmock