fiverr-vanity 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +247 -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 +224 -0
@@ -0,0 +1,27 @@
1
+ $(function() {
2
+ var issuesTable = $("table#issues");
3
+ if (issuesTable.size() > 0) {
4
+ $.getJSON("http://github.com/api/v2/json/issues/list/assaf/vanity/open?callback=?", function(response) {
5
+ $.each(response.issues, function(i, issue) {
6
+ issuesTable.append(
7
+ $("<tr>").append(
8
+ $("<td>").append(
9
+ $("<a>").text(issue.title).attr("href", "http://github.com/assaf/vanity/issues#issue/" + issue.number)
10
+ ).append(
11
+ $("<span class='votes'>").text(issue.votes == 0 ? "no votes" : issue.votes == 1 ? "1 vote" : issue.votes + " votes")
12
+ )
13
+ )
14
+ );
15
+ });
16
+ });
17
+ }
18
+
19
+ var statsTable = $("#sidebar ul#stats");
20
+ if (statsTable.size() > 0) {
21
+ $.getJSON("http://github.com/api/v2/json/repos/show/assaf/vanity?callback=?", function(response) {
22
+ statsTable.
23
+ prepend( $("<li>").append("Forks: " + response.repository.forks) ).
24
+ prepend( $("<li>").append("Watchers: " + response.repository.watchers) )
25
+ })
26
+ }
27
+ });
@@ -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,8 @@
1
+ class VanityGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template 'vanity_migration.rb', 'db/migrate',
5
+ :migration_file_name => "vanity_migration"
6
+ end
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,36 @@
1
+ require "date"
2
+ require "time"
3
+ require "logger"
4
+ require "cgi"
5
+ require "erb"
6
+ require "yaml"
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
+ end
16
+
17
+ require "vanity/version"
18
+ require "vanity/backport" if RUBY_VERSION < "1.9"
19
+ # Metrics.
20
+ require "vanity/metric/base"
21
+ require "vanity/metric/active_record"
22
+ require "vanity/metric/google_analytics"
23
+ require "vanity/metric/remote"
24
+ # Experiments.
25
+ require "vanity/experiment/base"
26
+ require "vanity/experiment/ab_test"
27
+ # Database adapters
28
+ require "vanity/adapters/abstract_adapter"
29
+ require "vanity/adapters/redis_adapter"
30
+ require "vanity/adapters/mongodb_adapter"
31
+ require "vanity/adapters/mock_adapter"
32
+ # Playground.
33
+ require "vanity/playground"
34
+ require "vanity/helpers"
35
+ # Integration with various frameworks.
36
+ require "vanity/frameworks"
@@ -0,0 +1,140 @@
1
+ module Vanity
2
+ module Adapters
3
+
4
+ class << self
5
+ # Creates new connection to underlying datastore and returns suitable
6
+ # adapter (adapter object extends AbstractAdapter and wraps the
7
+ # connection). Vanity.playgroup.establish_connection uses this.
8
+ #
9
+ # @since 1.4.0
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
16
+ adapter_method = "#{spec[:adapter]}_connection"
17
+ send adapter_method, spec
18
+ end
19
+ end
20
+
21
+ # Base class for all adapters. Adapters wrap underlying connection to a
22
+ # datastore and implement an API that Vanity can use to store/access
23
+ # metrics, experiments, etc.
24
+ class AbstractAdapter
25
+ # Returns true if connected.
26
+ def active?
27
+ false
28
+ end
29
+
30
+ # Close connection, release any resources.
31
+ def disconnect!
32
+ end
33
+
34
+ # Close and reopen connection.
35
+ def reconnect!
36
+ end
37
+
38
+ # Empty the database. This is used during tests.
39
+ def flushdb
40
+ end
41
+
42
+
43
+ # -- Metrics --
44
+
45
+ # Return when metric was last updated.
46
+ def get_metric_last_update_at(metric)
47
+ fail "Not implemented"
48
+ end
49
+
50
+ # Track metric data.
51
+ def metric_track(metric, timestamp, identity, values)
52
+ fail "Not implemented"
53
+ end
54
+
55
+ # Returns all the metric values between from and to time instances
56
+ # (inclusive). Returns pairs of date and total count for that date.
57
+ def metric_values(metric, from, to)
58
+ fail "Not implemented"
59
+ end
60
+
61
+ # Deletes all information about this metric.
62
+ def destroy_metric(metric)
63
+ fail "Not implemented"
64
+ end
65
+
66
+
67
+ # -- Experiments --
68
+
69
+ # Store when experiment was created (do not write over existing value).
70
+ def set_experiment_created_at(experiment, time)
71
+ fail "Not implemented"
72
+ end
73
+
74
+ # Return when experiment was created.
75
+ def get_experiment_created_at(experiment)
76
+ fail "Not implemented"
77
+ end
78
+
79
+ # Returns true if experiment completed.
80
+ def is_experiment_completed?(experiment)
81
+ fail "Not implemented"
82
+ end
83
+
84
+ # Returns counts for given A/B experiment and alternative (by index).
85
+ # Returns hash with values for the keys :participants, :converted and
86
+ # :conversions.
87
+ def ab_counts(experiment, alternative)
88
+ fail "Not implemented"
89
+ end
90
+
91
+ # Pick particular alternative (by index) to show to this particular
92
+ # participant (by identity).
93
+ def ab_show(experiment, identity, alternative)
94
+ fail "Not implemented"
95
+ end
96
+
97
+ # Indicates which alternative to show to this participant. See #ab_show.
98
+ def ab_showing(experiment, identity)
99
+ fail "Not implemented"
100
+ end
101
+
102
+ # Cancels previously set association between identity and alternative. See
103
+ # #ab_show.
104
+ def ab_not_showing(experiment, identity)
105
+ fail "Not implemented"
106
+ end
107
+
108
+ # Records a participant in this experiment for the given alternative.
109
+ def ab_add_participant(experiment, alternative, identity)
110
+ fail "Not implemented"
111
+ end
112
+
113
+ # Records a conversion in this experiment for the given alternative.
114
+ # Associates a value with the conversion (default to 1). If implicit is
115
+ # true, add particpant if not already recorded for this experiment. If
116
+ # implicit is false (default), only add conversion is participant
117
+ # previously recorded as participating in this experiment.
118
+ def ab_add_conversion(experiment, alternative, identity, count = 1, implicit = false)
119
+ fail "Not implemented"
120
+ end
121
+
122
+ # Returns the outcome of this expriment (if set), the index of a
123
+ # particular alternative.
124
+ def ab_get_outcome(experiment)
125
+ fail "Not implemented"
126
+ end
127
+
128
+ # Sets the outcome of this experiment to a particular alternative.
129
+ def ab_set_outcome(experiment, alternative = 0)
130
+ fail "Not implemented"
131
+ end
132
+
133
+ # Deletes all information about this experiment.
134
+ def destroy_experiment(experiment)
135
+ fail "Not implemented"
136
+ end
137
+
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,247 @@
1
+ module Vanity
2
+ module Adapters
3
+ class << self
4
+ # Creates new ActiveRecord connection and returns ActiveRecordAdapter.
5
+ def active_record_connection(spec)
6
+ require "active_record"
7
+ ActiveRecordAdapter.new(spec)
8
+ end
9
+ end
10
+
11
+ # ActiveRecord adapter
12
+ class ActiveRecordAdapter < AbstractAdapter
13
+ # Base model, stores connection and defines schema
14
+ class VanityRecord < ActiveRecord::Base
15
+ end
16
+
17
+ # Schema model
18
+ class VanitySchema < VanityRecord
19
+ set_table_name :vanity_schema
20
+ end
21
+
22
+ # Metric model
23
+ class VanityMetric < VanityRecord
24
+ set_table_name :vanity_metrics
25
+ has_many :vanity_metric_values
26
+
27
+ def self.retrieve(metric)
28
+ find_or_create_by_metric_id(metric.to_s)
29
+ end
30
+ end
31
+
32
+ # Metric value
33
+ class VanityMetricValue < VanityRecord
34
+ set_table_name :vanity_metric_values
35
+ belongs_to :vanity_metric
36
+ end
37
+
38
+ # Experiment model
39
+ class VanityExperiment < VanityRecord
40
+ set_table_name :vanity_experiments
41
+ has_many :vanity_conversions, :dependent => :destroy
42
+
43
+ # Finds or creates the experiment
44
+ def self.retrieve(experiment)
45
+ find_or_create_by_experiment_id(experiment.to_s)
46
+ end
47
+
48
+ def increment_conversion(alternative, count = 1)
49
+ record = vanity_conversions.find_or_create_by_alternative(alternative)
50
+ record.increment!(:conversions, count)
51
+ end
52
+ end
53
+
54
+ # Conversion model
55
+ class VanityConversion < VanityRecord
56
+ set_table_name :vanity_conversions
57
+ belongs_to :vanity_experiment
58
+ end
59
+
60
+ # Participant model
61
+ class VanityParticipant < VanityRecord
62
+ set_table_name :vanity_participants
63
+
64
+ # Finds the participant by experiment and identity. If
65
+ # create is true then it will create the participant
66
+ # if not found. If a hash is passed then this will be
67
+ # passed to create if creating, or will be used to
68
+ # update the found participant.
69
+ def self.retrieve(experiment, identity, create = true, update_with = nil)
70
+ if record = VanityParticipant.first(:conditions=>{ :experiment_id=>experiment.to_s, :identity=>identity.to_s })
71
+ record.update_attributes(update_with) if update_with
72
+ elsif create
73
+ record = VanityParticipant.create({ :experiment_id=>experiment.to_s, :identity=>identity }.merge(update_with || {}))
74
+ end
75
+ record
76
+ end
77
+ end
78
+
79
+ def initialize(options)
80
+ @options = options.inject({}) { |h,kv| h[kv.first.to_s] = kv.last ; h }
81
+ if @options["active_record_adapter"] && (@options["active_record_adapter"] != "default")
82
+ @options["adapter"] = @options["active_record_adapter"]
83
+ VanityRecord.establish_connection(@options)
84
+ end
85
+ end
86
+
87
+ def active?
88
+ VanityRecord.connected?
89
+ end
90
+
91
+ def disconnect!
92
+ VanityRecord.connection.disconnect! if active?
93
+ end
94
+
95
+ def reconnect!
96
+ VanityRecord.connection.reconnect!
97
+ end
98
+
99
+ def flushdb
100
+ [VanityExperiment, VanityMetric, VanityParticipant, VanityMetricValue, VanityConversion].each do |klass|
101
+ klass.delete_all
102
+ end
103
+ end
104
+
105
+ def get_metric_last_update_at(metric)
106
+ record = VanityMetric.find_by_metric_id(metric.to_s)
107
+ record && record.updated_at
108
+ end
109
+
110
+ def metric_track(metric, timestamp, identity, values)
111
+ record = VanityMetric.retrieve(metric)
112
+
113
+ values.each_with_index do |value, index|
114
+ record.vanity_metric_values.create(:date => timestamp.to_date.to_s, :index => index, :value => value)
115
+ end
116
+
117
+ record.updated_at = Time.now
118
+ record.save
119
+ end
120
+
121
+ def metric_values(metric, from, to)
122
+ connection = VanityMetric.connection
123
+ record = VanityMetric.retrieve(metric)
124
+ dates = (from.to_date..to.to_date).map(&:to_s)
125
+ conditions = [connection.quote_column_name('date') + ' IN (?)', dates]
126
+ order = "#{connection.quote_column_name('date')}"
127
+ select = "sum(#{connection.quote_column_name('value')}) AS value, #{connection.quote_column_name('date')}"
128
+ group_by = "#{connection.quote_column_name('date')}"
129
+
130
+ values = record.vanity_metric_values.all(
131
+ :select => select,
132
+ :conditions => conditions,
133
+ :order => order,
134
+ :group => group_by
135
+ )
136
+
137
+ dates.map do |date|
138
+ value = values.detect{|v| v.date == date }
139
+ [(value && value.value) || 0]
140
+ end
141
+ end
142
+
143
+ def destroy_metric(metric)
144
+ record = VanityMetric.find_by_metric_id(metric.to_s)
145
+ record && record.destroy
146
+ end
147
+
148
+ # Store when experiment was created (do not write over existing value).
149
+ def set_experiment_created_at(experiment, time)
150
+ record = VanityExperiment.find_by_experiment_id(experiment.to_s) ||
151
+ VanityExperiment.new(:experiment_id => experiment.to_s)
152
+ record.created_at ||= time
153
+ record.save
154
+ end
155
+
156
+ # Return when experiment was created.
157
+ def get_experiment_created_at(experiment)
158
+ record = VanityExperiment.retrieve(experiment)
159
+ record && record.created_at
160
+ end
161
+
162
+ def set_experiment_completed_at(experiment, time)
163
+ VanityExperiment.retrieve(experiment).update_attribute(:completed_at, time)
164
+ end
165
+
166
+ def get_experiment_completed_at(experiment)
167
+ VanityExperiment.retrieve(experiment).completed_at
168
+ end
169
+
170
+ # Returns true if experiment completed.
171
+ def is_experiment_completed?(experiment)
172
+ !!VanityExperiment.retrieve(experiment).completed_at
173
+ end
174
+
175
+ # Returns counts for given A/B experiment and alternative (by index).
176
+ # Returns hash with values for the keys :participants, :converted and
177
+ # :conversions.
178
+ def ab_counts(experiment, alternative)
179
+ record = VanityExperiment.retrieve(experiment)
180
+ participants = VanityParticipant.count(:conditions => {:experiment_id => experiment.to_s, :seen => alternative})
181
+ converted = VanityParticipant.count(:conditions => {:experiment_id => experiment.to_s, :converted => alternative})
182
+ conversions = record.vanity_conversions.sum(:conversions, :conditions => {:alternative => alternative})
183
+
184
+ {
185
+ :participants => participants,
186
+ :converted => converted,
187
+ :conversions => conversions
188
+ }
189
+ end
190
+
191
+ # Pick particular alternative (by index) to show to this particular
192
+ # participant (by identity).
193
+ def ab_show(experiment, identity, alternative)
194
+ VanityParticipant.retrieve(experiment, identity, true, :shown => alternative)
195
+ end
196
+
197
+ # Indicates which alternative to show to this participant. See #ab_show.
198
+ def ab_showing(experiment, identity)
199
+ participant = VanityParticipant.retrieve(experiment, identity, false)
200
+ participant && participant.shown
201
+ end
202
+
203
+ # Cancels previously set association between identity and alternative. See
204
+ # #ab_show.
205
+ def ab_not_showing(experiment, identity)
206
+ VanityParticipant.retrieve(experiment, identity, true, :shown => nil)
207
+ end
208
+
209
+ # Records a participant in this experiment for the given alternative.
210
+ def ab_add_participant(experiment, alternative, identity)
211
+ VanityParticipant.retrieve(experiment, identity, true, :seen => alternative)
212
+ end
213
+
214
+ # Records a conversion in this experiment for the given alternative.
215
+ # Associates a value with the conversion (default to 1). If implicit is
216
+ # true, add particpant if not already recorded for this experiment. If
217
+ # implicit is false (default), only add conversion is participant
218
+ # previously recorded as participating in this experiment.
219
+ def ab_add_conversion(experiment, alternative, identity, count = 1, implicit = false)
220
+ VanityParticipant.retrieve(experiment, identity, implicit, :converted => alternative)
221
+ VanityExperiment.retrieve(experiment).increment_conversion(alternative, count)
222
+ end
223
+
224
+ # Returns the outcome of this experiment (if set), the index of a
225
+ # particular alternative.
226
+ def ab_get_outcome(experiment)
227
+ VanityExperiment.retrieve(experiment).outcome
228
+ end
229
+
230
+ # Sets the outcome of this experiment to a particular alternative.
231
+ def ab_set_outcome(experiment, alternative = 0)
232
+ VanityExperiment.retrieve(experiment).update_attribute(:outcome, alternative)
233
+ end
234
+
235
+ # Deletes all information about this experiment.
236
+ def destroy_experiment(experiment)
237
+ VanityParticipant.delete_all(:experiment_id => experiment.to_s)
238
+ record = VanityExperiment.find_by_experiment_id(experiment.to_s)
239
+ record && record.destroy
240
+ end
241
+
242
+ def to_s
243
+ @options.to_s
244
+ end
245
+ end
246
+ end
247
+ end