vanity 1.6.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/CHANGELOG +28 -0
  2. data/Gemfile +1 -0
  3. data/README.rdoc +10 -3
  4. data/Rakefile +3 -3
  5. data/bin/b2json +16 -0
  6. data/bin/convert_to_should_syntax +16 -0
  7. data/bin/dw +16 -0
  8. data/bin/j2bson +16 -0
  9. data/bin/jekyll +16 -0
  10. data/bin/kramdown +16 -0
  11. data/bin/maruku +16 -0
  12. data/bin/marutex +16 -0
  13. data/bin/mongo_console +16 -0
  14. data/bin/passenger-config +16 -0
  15. data/bin/passenger-install-apache2-module +16 -0
  16. data/bin/passenger-install-nginx-module +16 -0
  17. data/bin/passenger-make-enterprisey +16 -0
  18. data/bin/passenger-memory-stats +16 -0
  19. data/bin/passenger-spawn-server +16 -0
  20. data/bin/passenger-status +16 -0
  21. data/bin/passenger-stress-test +16 -0
  22. data/bin/posix-spawn-benchmark +16 -0
  23. data/bin/rackup +16 -0
  24. data/bin/rails +16 -0
  25. data/bin/rake +16 -0
  26. data/bin/redcloth +16 -0
  27. data/bin/vanity +12 -65
  28. data/bin/yard +16 -0
  29. data/bin/yardoc +16 -0
  30. data/bin/yri +16 -0
  31. data/lib/generators/templates/vanity_migration.rb +53 -0
  32. data/lib/generators/vanity_generator.rb +15 -0
  33. data/lib/vanity/adapters/abstract_adapter.rb +5 -0
  34. data/lib/vanity/adapters/active_record_adapter.rb +10 -69
  35. data/lib/vanity/adapters/redis_adapter.rb +1 -1
  36. data/lib/vanity/frameworks/rails.rb +68 -7
  37. data/lib/vanity/images/x.gif +0 -0
  38. data/lib/vanity/playground.rb +0 -5
  39. data/lib/vanity/templates/vanity.css +1 -1
  40. data/lib/vanity/version.rb +1 -1
  41. data/test/adapters/redis_adapter_test.rb +1 -1
  42. data/test/experiment/ab_test.rb +0 -1
  43. data/test/myapp/log/production.log +522 -0
  44. data/test/myapp/log/test.log +304 -0
  45. data/test/passenger_test.rb +1 -1
  46. data/test/rails_helper_test.rb +8 -0
  47. data/test/rails_test.rb +23 -13
  48. data/test/test_helper.rb +23 -11
  49. metadata +40 -10
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' 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('rake', 'rake')
data/bin/redcloth ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'redcloth' 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('RedCloth', 'redcloth')
data/bin/vanity CHANGED
@@ -1,69 +1,16 @@
1
1
  #!/usr/bin/env ruby
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
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
+ #
10
8
 
11
- require "optparse"
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
12
 
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"
13
+ require 'rubygems'
14
+ require 'bundler/setup'
21
15
 
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 fail "No such command: #{cmd}"
68
- end
69
- end
16
+ load Gem.bin_path('vanity', 'vanity')
data/bin/yard ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'yard' 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('yard', 'yard')
data/bin/yardoc ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'yardoc' 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('yard', 'yardoc')
data/bin/yri ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'yri' 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('yard', 'yri')
@@ -0,0 +1,53 @@
1
+ class VanityMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :vanity_metrics do |t|
4
+ t.string :metric_id
5
+ t.datetime :updated_at
6
+ end
7
+ add_index :vanity_metrics, [:metric_id]
8
+
9
+ create_table :vanity_metric_values do |t|
10
+ t.integer :vanity_metric_id
11
+ t.integer :index
12
+ t.integer :value
13
+ t.string :date
14
+ end
15
+ add_index :vanity_metric_values, [:vanity_metric_id]
16
+
17
+ create_table :vanity_experiments do |t|
18
+ t.string :experiment_id
19
+ t.integer :outcome
20
+ t.datetime :created_at
21
+ t.datetime :completed_at
22
+ end
23
+ add_index :vanity_experiments, [:experiment_id]
24
+
25
+ create_table :vanity_conversions do |t|
26
+ t.integer :vanity_experiment_id
27
+ t.integer :alternative
28
+ t.integer :conversions
29
+ end
30
+ add_index :vanity_conversions, [:vanity_experiment_id, :alternative], :name => "by_experiment_id_and_alternative"
31
+
32
+ create_table :vanity_participants do |t|
33
+ t.string :experiment_id
34
+ t.string :identity
35
+ t.integer :shown
36
+ t.integer :seen
37
+ t.integer :converted
38
+ end
39
+ add_index :vanity_participants, [:experiment_id]
40
+ add_index :vanity_participants, [:experiment_id, :identity], :name => "by_experiment_id_and_identity"
41
+ add_index :vanity_participants, [:experiment_id, :shown], :name => "by_experiment_id_and_shown"
42
+ add_index :vanity_participants, [:experiment_id, :seen], :name => "by_experiment_id_and_seen"
43
+ add_index :vanity_participants, [:experiment_id, :converted], :name => "by_experiment_id_and_converted"
44
+ end
45
+
46
+ def self.down
47
+ drop_table :vanity_metrics
48
+ drop_table :vanity_metric_values
49
+ drop_table :vanity_experiments
50
+ drop_table :vanity_conversions
51
+ drop_table :vanity_participants
52
+ end
53
+ end
@@ -0,0 +1,15 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class VanityGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ def self.next_migration_number(path)
9
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
10
+ end
11
+
12
+ def create_model_file
13
+ migration_template "vanity_migration.rb", "db/migrate/vanity_migration.rb"
14
+ end
15
+ end
@@ -8,6 +8,11 @@ module Vanity
8
8
  #
9
9
  # @since 1.4.0
10
10
  def establish_connection(spec)
11
+ begin
12
+ require "vanity/adapters/#{spec[:adapter]}_adapter"
13
+ rescue LoadError
14
+ raise "Could not find #{spec[:adapter]} in your load path"
15
+ end
11
16
  adapter_method = "#{spec[:adapter]}_connection"
12
17
  send adapter_method, spec
13
18
  end
@@ -3,7 +3,7 @@ module Vanity
3
3
  class << self
4
4
  # Creates new ActiveRecord connection and returns ActiveRecordAdapter.
5
5
  def active_record_connection(spec)
6
- require 'active_record'
6
+ require "active_record"
7
7
  ActiveRecordAdapter.new(spec)
8
8
  end
9
9
  end
@@ -12,61 +12,6 @@ module Vanity
12
12
  class ActiveRecordAdapter < AbstractAdapter
13
13
  # Base model, stores connection and defines schema
14
14
  class VanityRecord < ActiveRecord::Base
15
- def self.define_schema
16
- # Create a schema table to store the schema version
17
- unless connection.tables.include?('vanity_schema')
18
- connection.create_table :vanity_schema do |t|
19
- t.integer :version
20
- end
21
- end
22
-
23
- # Migrate
24
- unless VanitySchema.find_by_version(1)
25
- connection.create_table :vanity_metrics do |t|
26
- t.string :metric_id
27
- t.datetime :updated_at
28
- end
29
- connection.add_index :vanity_metrics, [:metric_id]
30
-
31
- connection.create_table :vanity_metric_values do |t|
32
- t.integer :vanity_metric_id
33
- t.integer :index
34
- t.integer :value
35
- t.string :date
36
- end
37
- connection.add_index :vanity_metric_values, [:vanity_metric_id]
38
-
39
- connection.create_table :vanity_experiments do |t|
40
- t.string :experiment_id
41
- t.integer :outcome
42
- t.datetime :created_at
43
- t.datetime :completed_at
44
- end
45
- connection.add_index :vanity_experiments, [:experiment_id]
46
-
47
- connection.create_table :vanity_conversions do |t|
48
- t.integer :vanity_experiment_id
49
- t.integer :alternative
50
- t.integer :conversions
51
- end
52
- connection.add_index :vanity_conversions, [:vanity_experiment_id, :alternative], :name => "by_experiment_id_and_alternative"
53
-
54
- connection.create_table :vanity_participants do |t|
55
- t.string :experiment_id
56
- t.string :identity
57
- t.integer :shown
58
- t.integer :seen
59
- t.integer :converted
60
- end
61
- connection.add_index :vanity_participants, [:experiment_id]
62
- connection.add_index :vanity_participants, [:experiment_id, :identity], :name => "by_experiment_id_and_identity"
63
- connection.add_index :vanity_participants, [:experiment_id, :shown], :name => "by_experiment_id_and_shown"
64
- connection.add_index :vanity_participants, [:experiment_id, :seen], :name => "by_experiment_id_and_seen"
65
- connection.add_index :vanity_participants, [:experiment_id, :converted], :name => "by_experiment_id_and_converted"
66
-
67
- VanitySchema.create(:version => 1)
68
- end
69
- end
70
15
  end
71
16
 
72
17
  # Schema model
@@ -122,27 +67,19 @@ module Vanity
122
67
  # passed to create if creating, or will be used to
123
68
  # update the found participant.
124
69
  def self.retrieve(experiment, identity, create = true, update_with = nil)
125
- record = VanityParticipant.first(
126
- :conditions =>
127
- {:experiment_id => experiment.to_s, :identity => identity.to_s})
128
-
129
- if record
70
+ if record = VanityParticipant.first(:conditions=>{ :experiment_id=>experiment.to_s, :identity=>identity.to_s })
130
71
  record.update_attributes(update_with) if update_with
131
72
  elsif create
132
- record = VanityParticipant.create(
133
- {:experiment_id => experiment.to_s,
134
- :identity => identity}.merge(update_with || {}))
73
+ record = VanityParticipant.create({ :experiment_id=>experiment.to_s, :identity=>identity }.merge(update_with || {}))
135
74
  end
136
-
137
75
  record
138
76
  end
139
77
  end
140
78
 
141
79
  def initialize(options)
142
- options[:adapter] = options[:active_record_adapter] if options[:active_record_adapter]
143
-
144
- VanityRecord.establish_connection(options)
145
- VanityRecord.define_schema
80
+ @options = options.inject({}) { |h,kv| h[kv.first.to_s] = kv.last ; h }
81
+ @options["adapter"] = @options["active_record_adapter"] if @options["active_record_adapter"]
82
+ VanityRecord.establish_connection(@options)
146
83
  end
147
84
 
148
85
  def active?
@@ -299,6 +236,10 @@ module Vanity
299
236
  record = VanityExperiment.find_by_experiment_id(experiment.to_s)
300
237
  record && record.destroy
301
238
  end
239
+
240
+ def to_s
241
+ @options.to_s
242
+ end
302
243
  end
303
244
  end
304
245
  end
@@ -28,7 +28,7 @@ module Vanity
28
28
  def disconnect!
29
29
  if redis
30
30
  begin
31
- redis.quit
31
+ redis.client.disconnect
32
32
  rescue Exception => e
33
33
  warn("Error while disconnecting from redis: #{e.message}")
34
34
  end
@@ -44,6 +44,10 @@ module Vanity
44
44
  return @vanity_identity if @vanity_identity
45
45
  if symbol && object = send(symbol)
46
46
  @vanity_identity = object.id
47
+ elsif request.get? && params[:_identity]
48
+ @vanity_identity = params[:_identity]
49
+ cookies["vanity_id"] = { :value=>@vanity_identity, :expires=>1.month.from_now }
50
+ @vanity_identity
47
51
  elsif response # everyday use
48
52
  @vanity_identity = cookies["vanity_id"] || ActiveSupport::SecureRandom.hex(16)
49
53
  cookie = { :value=>@vanity_identity, :expires=>1.month.from_now }
@@ -60,11 +64,33 @@ module Vanity
60
64
  around_filter :vanity_context_filter
61
65
  before_filter :vanity_reload_filter unless ::Rails.configuration.cache_classes
62
66
  before_filter :vanity_query_parameter_filter
67
+ after_filter :vanity_track_filter
63
68
  end
64
69
  protected :use_vanity
65
70
  end
66
71
 
67
-
72
+ module UseVanityMailer
73
+ def use_vanity_mailer(symbol = nil)
74
+ # Context is the instance of ActionMailer::Base
75
+ Vanity.context = self
76
+ if symbol && (@object = symbol)
77
+ class << self
78
+ define_method :vanity_identity do
79
+ @vanity_identity = (String === @object ? @object : @object.id)
80
+ end
81
+ end
82
+ else
83
+ class << self
84
+ define_method :vanity_identity do
85
+ @vanity_identity = @vanity_identity || ActiveSupport::SecureRandom.hex(16)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ protected :use_vanity_mailer
91
+ end
92
+
93
+
68
94
  # Vanity needs these filters. They are includes in ActionController and
69
95
  # automatically added when you use #use_vanity in your controller.
70
96
  module Filters
@@ -112,7 +138,15 @@ module Vanity
112
138
  def vanity_reload_filter
113
139
  Vanity.playground.reload!
114
140
  end
115
-
141
+
142
+ # Filter to track metrics
143
+ # pass _track param along to call track! on that alternative
144
+ def vanity_track_filter
145
+ if request.get? && params[:_track]
146
+ track! params[:_track]
147
+ end
148
+ end
149
+
116
150
  protected :vanity_context_filter, :vanity_query_parameter_filter, :vanity_reload_filter
117
151
  end
118
152
 
@@ -159,6 +193,18 @@ module Vanity
159
193
  value
160
194
  end
161
195
  end
196
+
197
+ # Generate url with the identity of the current user and the metric to track on click
198
+ def vanity_track_url_for(identity, metric, options = {})
199
+ options = options.merge(:_identity => identity, :_track => metric)
200
+ url_for(options)
201
+ end
202
+
203
+ # Generate url with the fingerprint for the current Vanity experiment
204
+ def vanity_tracking_image(identity, metric, options = {})
205
+ options = options.merge(:controller => :vanity, :action => :image, :_identity => identity, :_track => metric)
206
+ image_tag(url_for(options), :width => "1px", :height => "1px", :alt => "")
207
+ end
162
208
 
163
209
  def vanity_js
164
210
  return if @_vanity_experiments.nil?
@@ -206,15 +252,22 @@ module Vanity
206
252
  end
207
253
 
208
254
  def add_participant
209
- if params[:e].nil? || params[:e].empty?
210
- render :status => 404, :nothing => true
211
- return
212
- end
255
+ if params[:e].nil? || params[:e].empty?
256
+ render :status => 404, :nothing => true
257
+ return
258
+ end
213
259
  exp = Vanity.playground.experiment(params[:e])
214
260
  exp.chooses(exp.alternatives[params[:a].to_i].value)
215
261
  render :status => 200, :nothing => true
216
262
  end
217
263
  end
264
+
265
+ module TrackingImage
266
+ def image
267
+ # send image
268
+ send_file(File.expand_path("../images/x.gif", File.dirname(__FILE__)), :type => 'image/gif', :stream => false, :disposition => 'inline')
269
+ end
270
+ end
218
271
  end
219
272
  end
220
273
 
@@ -241,6 +294,14 @@ if defined?(ActionController)
241
294
  end
242
295
  end
243
296
 
297
+ if defined?(ActionMailer)
298
+ # Include in mailer, add view helper methods.
299
+ ActionMailer::Base.class_eval do
300
+ include Vanity::Rails::UseVanityMailer
301
+ include Vanity::Rails::Filters
302
+ helper Vanity::Rails::Helpers
303
+ end
304
+ end
244
305
 
245
306
  # Automatically configure Vanity.
246
307
  if defined?(Rails)
@@ -263,7 +324,7 @@ if defined?(PhusionPassenger)
263
324
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
264
325
  if forked
265
326
  begin
266
- Vanity.playground.establish_connection if Vanity.playground.collecting?
327
+ Vanity.playground.reconnect! if Vanity.playground.collecting?
267
328
  rescue Exception=>ex
268
329
  Rails.logger.error "Error reconnecting: #{ex.to_s}"
269
330
  end