vanity 1.8.4 → 1.9.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.travis.yml +3 -2
  2. data/CHANGELOG +12 -0
  3. data/Gemfile +6 -3
  4. data/Gemfile.lock +12 -10
  5. data/README.rdoc +45 -16
  6. data/Rakefile +14 -9
  7. data/doc/_layouts/page.html +4 -6
  8. data/doc/ab_testing.textile +1 -1
  9. data/doc/configuring.textile +2 -4
  10. data/doc/email.textile +1 -3
  11. data/doc/index.textile +3 -63
  12. data/doc/rails.textile +34 -8
  13. data/gemfiles/rails3.gemfile +12 -3
  14. data/gemfiles/rails3.gemfile.lock +37 -11
  15. data/gemfiles/rails31.gemfile +12 -3
  16. data/gemfiles/rails31.gemfile.lock +37 -11
  17. data/gemfiles/rails32.gemfile +12 -3
  18. data/gemfiles/rails32.gemfile.lock +37 -11
  19. data/gemfiles/rails4.gemfile +12 -3
  20. data/gemfiles/rails4.gemfile.lock +37 -11
  21. data/lib/vanity/adapters/abstract_adapter.rb +4 -0
  22. data/lib/vanity/adapters/active_record_adapter.rb +18 -10
  23. data/lib/vanity/adapters/mock_adapter.rb +8 -4
  24. data/lib/vanity/adapters/mongodb_adapter.rb +11 -7
  25. data/lib/vanity/adapters/redis_adapter.rb +88 -37
  26. data/lib/vanity/commands/report.rb +9 -9
  27. data/lib/vanity/experiment/ab_test.rb +120 -101
  28. data/lib/vanity/experiment/alternative.rb +21 -21
  29. data/lib/vanity/experiment/base.rb +5 -5
  30. data/lib/vanity/experiment/bayesian_bandit_score.rb +51 -51
  31. data/lib/vanity/experiment/definition.rb +10 -10
  32. data/lib/vanity/frameworks/rails.rb +39 -36
  33. data/lib/vanity/helpers.rb +6 -4
  34. data/lib/vanity/metric/active_record.rb +1 -1
  35. data/lib/vanity/metric/base.rb +23 -24
  36. data/lib/vanity/metric/google_analytics.rb +5 -5
  37. data/lib/vanity/playground.rb +118 -24
  38. data/lib/vanity/templates/_report.erb +20 -6
  39. data/lib/vanity/templates/vanity.css +2 -0
  40. data/lib/vanity/version.rb +1 -1
  41. data/test/adapters/redis_adapter_test.rb +106 -1
  42. data/test/dummy/config/database.yml +21 -4
  43. data/test/dummy/config/routes.rb +1 -1
  44. data/test/experiment/ab_test.rb +93 -13
  45. data/test/metric/active_record_test.rb +9 -4
  46. data/test/passenger_test.rb +43 -42
  47. data/test/playground_test.rb +50 -1
  48. data/test/rails_dashboard_test.rb +38 -1
  49. data/test/rails_helper_test.rb +5 -0
  50. data/test/rails_test.rb +66 -15
  51. data/test/test_helper.rb +24 -2
  52. data/vanity.gemspec +0 -2
  53. metadata +45 -57
@@ -5,15 +5,14 @@ GIT
5
5
  fastthread (1.0.7)
6
6
 
7
7
  PATH
8
- remote: /Users/phill/Development/ruby/vanity
8
+ remote: ..
9
9
  specs:
10
- vanity (1.8.4)
11
- redis (>= 2.1)
12
- redis-namespace (>= 1.1.0)
10
+ vanity (1.8.3)
13
11
 
14
12
  GEM
15
13
  remote: https://rubygems.org/
16
14
  specs:
15
+ RedCloth (4.2.9)
17
16
  SystemTimer (1.2.3)
18
17
  actionmailer (3.2.1)
19
18
  actionpack (= 3.2.1)
@@ -43,30 +42,48 @@ GEM
43
42
  i18n (~> 0.6)
44
43
  multi_json (~> 1.0)
45
44
  addressable (2.2.7)
46
- appraisal (0.4.1)
45
+ albino (1.3.3)
46
+ posix-spawn (>= 0.3.6)
47
+ appraisal (1.0.0.beta2)
47
48
  bundler
48
49
  rake
50
+ thor (>= 0.14.0)
49
51
  arel (3.0.2)
50
52
  backports (3.3.5)
51
53
  bson (1.6.0)
52
54
  bson_ext (1.6.0)
53
55
  bson (= 1.6.0)
54
56
  builder (3.0.0)
57
+ classifier (1.3.3)
58
+ fast-stemmer (>= 1.0.0)
55
59
  crack (0.3.1)
56
60
  daemon_controller (1.0.0)
61
+ directory_watcher (1.4.1)
57
62
  erubis (2.7.0)
63
+ fast-stemmer (1.0.0)
58
64
  garb (0.9.1)
59
65
  activesupport (>= 2.2.0)
60
66
  crack (>= 0.1.6)
61
67
  hike (1.2.1)
62
68
  i18n (0.6.0)
63
69
  integration (0.1.0)
70
+ jekyll (0.11.2)
71
+ albino (~> 1.3)
72
+ classifier (~> 1.3)
73
+ directory_watcher (~> 1.1)
74
+ kramdown (~> 0.13)
75
+ liquid (~> 2.3)
76
+ maruku (~> 0.5)
64
77
  journey (1.0.3)
65
78
  json (1.6.5)
79
+ kramdown (0.13.5)
80
+ liquid (2.3.0)
66
81
  mail (2.4.1)
67
82
  i18n (>= 0.4.0)
68
83
  mime-types (~> 1.16)
69
84
  treetop (~> 1.4.8)
85
+ maruku (0.6.0)
86
+ syntax (>= 1.0.0)
70
87
  metaclass (0.0.1)
71
88
  mime-types (1.17.2)
72
89
  mocha (0.10.5)
@@ -80,8 +97,8 @@ GEM
80
97
  fastthread (>= 1.0.1)
81
98
  rack
82
99
  rake (>= 0.8.1)
83
- pg (0.13.2)
84
100
  polyglot (0.3.3)
101
+ posix-spawn (0.3.6)
85
102
  rack (1.4.1)
86
103
  rack-cache (1.1)
87
104
  rack (>= 0.4)
@@ -107,9 +124,9 @@ GEM
107
124
  rake (0.9.2.2)
108
125
  rdoc (3.12)
109
126
  json (~> 1.4)
110
- redis (3.2.0)
111
- redis-namespace (1.5.1)
112
- redis (~> 3.0, >= 3.0.4)
127
+ redis (3.0.6)
128
+ redis-namespace (1.3.2)
129
+ redis (~> 3.0.4)
113
130
  rubystats (0.2.3)
114
131
  shoulda (3.0.1)
115
132
  shoulda-context (~> 1.0.0)
@@ -120,6 +137,8 @@ GEM
120
137
  hike (~> 1.2)
121
138
  rack (~> 1.0)
122
139
  tilt (~> 1.1, != 1.3.0)
140
+ sqlite3 (1.3.8)
141
+ syntax (1.0.0)
123
142
  thor (0.14.6)
124
143
  tilt (1.3.3)
125
144
  timecop (0.3.5)
@@ -130,27 +149,34 @@ GEM
130
149
  webmock (1.8.0)
131
150
  addressable (>= 2.2.7)
132
151
  crack (>= 0.1.7)
152
+ yard (0.7.5)
133
153
 
134
154
  PLATFORMS
135
155
  ruby
136
156
 
137
157
  DEPENDENCIES
158
+ RedCloth
138
159
  SystemTimer (= 1.2.3)
139
- appraisal
160
+ appraisal (>= 1.0.0.beta2)
140
161
  backports
141
162
  bson_ext
142
163
  fastthread!
143
164
  garb
144
165
  integration
166
+ jekyll
145
167
  mocha
146
168
  mongo
147
169
  mysql
148
170
  passenger (~> 3.0)
149
- pg
150
171
  rack
151
172
  rails (= 3.2.1)
173
+ rake
174
+ redis (>= 2.1)
175
+ redis-namespace (>= 1.1.0)
152
176
  rubystats
153
177
  shoulda
178
+ sqlite3
154
179
  timecop
155
180
  vanity!
156
181
  webmock
182
+ yard
@@ -3,16 +3,17 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "rack"
6
+ gem "redis", ">= 2.1"
7
+ gem "redis-namespace", ">= 1.1.0"
6
8
  gem "bson_ext"
7
9
  gem "mongo"
8
10
  gem "mysql"
9
- gem "pg"
11
+ gem "sqlite3"
10
12
  gem "backports", :platforms=>:mri_18
11
13
  gem "integration"
12
14
  gem "rubystats"
13
15
  gem "garb"
14
16
  gem "SystemTimer", "1.2.3", :platforms=>:mri_18
15
- gem "appraisal"
16
17
  gem "mocha", :require=>false
17
18
  gem "shoulda", :require=>false
18
19
  gem "timecop", :require=>false
@@ -21,4 +22,12 @@ gem "rails", "4.0.0"
21
22
  gem "fastthread", :git=>"git://github.com/zoltankiss/fastthread.git", :platforms=>:mri_20
22
23
  gem "passenger", "~>3.0"
23
24
 
24
- gemspec :path=>"../"
25
+ group :development do
26
+ gem "appraisal", ">= 1.0.0.beta2"
27
+ gem "jekyll"
28
+ gem "rake"
29
+ gem "RedCloth"
30
+ gem "yard"
31
+ end
32
+
33
+ gemspec :path=>".././"
@@ -5,15 +5,14 @@ GIT
5
5
  fastthread (1.0.7)
6
6
 
7
7
  PATH
8
- remote: /Users/phill/Development/ruby/vanity
8
+ remote: ..
9
9
  specs:
10
- vanity (1.8.4)
11
- redis (>= 2.1)
12
- redis-namespace (>= 1.1.0)
10
+ vanity (1.8.3)
13
11
 
14
12
  GEM
15
13
  remote: https://rubygems.org/
16
14
  specs:
15
+ RedCloth (4.2.9)
17
16
  SystemTimer (1.2.3)
18
17
  actionmailer (4.0.0)
19
18
  actionpack (= 4.0.0)
@@ -40,9 +39,12 @@ GEM
40
39
  thread_safe (~> 0.1)
41
40
  tzinfo (~> 0.3.37)
42
41
  addressable (2.3.5)
43
- appraisal (0.5.2)
42
+ albino (1.3.3)
43
+ posix-spawn (>= 0.3.6)
44
+ appraisal (1.0.0.beta2)
44
45
  bundler
45
46
  rake
47
+ thor (>= 0.14.0)
46
48
  arel (4.0.1)
47
49
  atomic (1.1.14)
48
50
  backports (3.3.5)
@@ -50,19 +52,34 @@ GEM
50
52
  bson_ext (1.9.2)
51
53
  bson (~> 1.9.2)
52
54
  builder (3.1.4)
55
+ classifier (1.3.3)
56
+ fast-stemmer (>= 1.0.0)
53
57
  crack (0.4.1)
54
58
  safe_yaml (~> 0.9.0)
55
59
  daemon_controller (1.1.7)
60
+ directory_watcher (1.4.1)
56
61
  erubis (2.7.0)
62
+ fast-stemmer (1.0.0)
57
63
  garb (0.9.1)
58
64
  activesupport (>= 2.2.0)
59
65
  crack (>= 0.1.6)
60
66
  hike (1.2.3)
61
67
  i18n (0.6.5)
62
68
  integration (0.1.0)
69
+ jekyll (0.11.2)
70
+ albino (~> 1.3)
71
+ classifier (~> 1.3)
72
+ directory_watcher (~> 1.1)
73
+ kramdown (~> 0.13)
74
+ liquid (~> 2.3)
75
+ maruku (~> 0.5)
76
+ kramdown (0.13.5)
77
+ liquid (2.3.0)
63
78
  mail (2.5.4)
64
79
  mime-types (~> 1.16)
65
80
  treetop (~> 1.4.8)
81
+ maruku (0.6.0)
82
+ syntax (>= 1.0.0)
66
83
  metaclass (0.0.1)
67
84
  mime-types (1.25)
68
85
  minitest (4.7.5)
@@ -77,8 +94,8 @@ GEM
77
94
  fastthread (>= 1.0.1)
78
95
  rack
79
96
  rake (>= 0.8.1)
80
- pg (0.17.0)
81
97
  polyglot (0.3.3)
98
+ posix-spawn (0.3.6)
82
99
  rack (1.5.2)
83
100
  rack-test (0.6.2)
84
101
  rack (>= 1.0)
@@ -96,9 +113,9 @@ GEM
96
113
  rake (>= 0.8.7)
97
114
  thor (>= 0.18.1, < 2.0)
98
115
  rake (10.1.0)
99
- redis (3.2.0)
100
- redis-namespace (1.5.1)
101
- redis (~> 3.0, >= 3.0.4)
116
+ redis (3.0.6)
117
+ redis-namespace (1.3.2)
118
+ redis (~> 3.0.4)
102
119
  rubystats (0.2.3)
103
120
  safe_yaml (0.9.7)
104
121
  shoulda (3.5.0)
@@ -116,6 +133,8 @@ GEM
116
133
  actionpack (>= 3.0)
117
134
  activesupport (>= 3.0)
118
135
  sprockets (~> 2.8)
136
+ sqlite3 (1.3.8)
137
+ syntax (1.0.0)
119
138
  thor (0.18.1)
120
139
  thread_safe (0.1.3)
121
140
  atomic
@@ -128,27 +147,34 @@ GEM
128
147
  webmock (1.15.2)
129
148
  addressable (>= 2.2.7)
130
149
  crack (>= 0.3.2)
150
+ yard (0.7.5)
131
151
 
132
152
  PLATFORMS
133
153
  ruby
134
154
 
135
155
  DEPENDENCIES
156
+ RedCloth
136
157
  SystemTimer (= 1.2.3)
137
- appraisal
158
+ appraisal (>= 1.0.0.beta2)
138
159
  backports
139
160
  bson_ext
140
161
  fastthread!
141
162
  garb
142
163
  integration
164
+ jekyll
143
165
  mocha
144
166
  mongo
145
167
  mysql
146
168
  passenger (~> 3.0)
147
- pg
148
169
  rack
149
170
  rails (= 4.0.0)
171
+ rake
172
+ redis (>= 2.1)
173
+ redis-namespace (>= 1.1.0)
150
174
  rubystats
151
175
  shoulda
176
+ sqlite3
152
177
  timecop
153
178
  vanity!
154
179
  webmock
180
+ yard
@@ -66,6 +66,10 @@ module Vanity
66
66
 
67
67
  # -- Experiments --
68
68
 
69
+ def experiment_persisted?(experiment)
70
+ fail "Not implemented"
71
+ end
72
+
69
73
  # Store when experiment was created (do not write over existing value).
70
74
  def set_experiment_created_at(experiment, time)
71
75
  fail "Not implemented"
@@ -27,8 +27,7 @@ module Vanity
27
27
  # Metric model
28
28
  class VanityMetric < VanityRecord
29
29
  self.table_name = :vanity_metrics
30
- has_many :vanity_metric_values,
31
- :class_name => 'Vanity::Adapters::ActiveRecordAdapter::VanityMetricValue'
30
+ has_many :vanity_metric_values
32
31
 
33
32
  def self.retrieve(metric)
34
33
  find_or_create_by_metric_id(metric.to_s)
@@ -40,15 +39,13 @@ module Vanity
40
39
  attr_accessible :date, :index, :value if needs_attr_accessible?
41
40
 
42
41
  self.table_name = :vanity_metric_values
43
- belongs_to :vanity_metric,
44
- :class_name => 'Vanity::Adapters::ActiveRecordAdapter::VanityMetric'
42
+ belongs_to :vanity_metric
45
43
  end
46
44
 
47
45
  # Experiment model
48
46
  class VanityExperiment < VanityRecord
49
47
  self.table_name = :vanity_experiments
50
- has_many :vanity_conversions, :dependent => :destroy,
51
- :class_name => 'Vanity::Adapters::ActiveRecordAdapter::VanityConversion'
48
+ has_many :vanity_conversions, :dependent => :destroy
52
49
  attr_accessible :experiment_id if needs_attr_accessible?
53
50
 
54
51
  # Finds or creates the experiment
@@ -65,8 +62,7 @@ module Vanity
65
62
  # Conversion model
66
63
  class VanityConversion < VanityRecord
67
64
  self.table_name = :vanity_conversions
68
- belongs_to :vanity_experiment,
69
- :class_name => 'Vanity::Adapters::ActiveRecordAdapter::VanityExperiment'
65
+ belongs_to :vanity_experiment
70
66
  end
71
67
 
72
68
  # Participant model
@@ -90,6 +86,8 @@ module Vanity
90
86
  end
91
87
 
92
88
  def initialize(options)
89
+
90
+
93
91
  @options = options.inject({}) { |h,kv| h[kv.first.to_s] = kv.last ; h }
94
92
  if @options["active_record_adapter"] && (@options["active_record_adapter"] != "default")
95
93
  @options["adapter"] = @options["active_record_adapter"]
@@ -115,6 +113,9 @@ module Vanity
115
113
  end
116
114
  end
117
115
 
116
+
117
+ # -- Metrics --
118
+
118
119
  def get_metric_last_update_at(metric)
119
120
  record = VanityMetric.find_by_metric_id(metric.to_s)
120
121
  record && record.updated_at
@@ -158,6 +159,13 @@ module Vanity
158
159
  record && record.destroy
159
160
  end
160
161
 
162
+
163
+ # -- Experiments --
164
+
165
+ def experiment_persisted?(experiment)
166
+ VanityExperiment.find_by_experiment_id(experiment.to_s).present?
167
+ end
168
+
161
169
  # Store when experiment was created (do not write over existing value).
162
170
  def set_experiment_created_at(experiment, time)
163
171
  record = VanityExperiment.find_by_experiment_id(experiment.to_s) ||
@@ -226,8 +234,8 @@ module Vanity
226
234
 
227
235
  # Determines if a participant already has seen this alternative in this experiment.
228
236
  def ab_seen(experiment, identity, alternative)
229
- participant = VanityParticipant.retrieve(experiment, identity, false)
230
- participant && participant.seen == alternative.id
237
+ participant = VanityParticipant.retrieve(experiment, identity, false)
238
+ participant && participant.seen == alternative.id
231
239
  end
232
240
 
233
241
  # Returns the participant's seen alternative in this experiment, if it exists
@@ -44,7 +44,7 @@ module Vanity
44
44
 
45
45
 
46
46
  # -- Metrics --
47
-
47
+
48
48
  def get_metric_last_update_at(metric)
49
49
  @metrics[metric] && @metrics[metric][:last_update_at]
50
50
  end
@@ -66,10 +66,14 @@ module Vanity
66
66
  def destroy_metric(metric)
67
67
  @metrics.delete metric
68
68
  end
69
-
69
+
70
70
 
71
71
  # -- Experiments --
72
-
72
+
73
+ def experiment_persisted?(experiment)
74
+ !!@experiments[experiment]
75
+ end
76
+
73
77
  def set_experiment_created_at(experiment, time)
74
78
  @experiments[experiment] ||= {}
75
79
  @experiments[experiment][:created_at] ||= time
@@ -133,7 +137,7 @@ module Vanity
133
137
  if implicit
134
138
  alt[:participants] << identity
135
139
  else
136
- participating = alt[:participants].include?(identity)
140
+ participating = alt[:participants].include?(identity)
137
141
  end
138
142
  alt[:converted] << identity if implicit || participating
139
143
  alt[:conversions] += count
@@ -73,10 +73,10 @@ module Vanity
73
73
  @experiments.drop
74
74
  @participants.drop
75
75
  end
76
-
76
+
77
77
 
78
78
  # -- Metrics --
79
-
79
+
80
80
  def get_metric_last_update_at(metric)
81
81
  record = @metrics.find_one(:_id=>metric)
82
82
  record && record["last_update_at"]
@@ -99,12 +99,16 @@ module Vanity
99
99
  def destroy_metric(metric)
100
100
  @metrics.remove :_id=>metric
101
101
  end
102
-
102
+
103
103
 
104
104
  # -- Experiments --
105
-
105
+
106
+ def experiment_persisted?(experiment)
107
+ !!@experiments.find_one({ :_id=>experiment })
108
+ end
109
+
106
110
  def set_experiment_created_at(experiment, time)
107
- @experiments.insert :_id=>experiment, :created_at=>time
111
+ @experiments.insert(:_id=>experiment, :created_at=>time)
108
112
  end
109
113
 
110
114
  def get_experiment_created_at(experiment)
@@ -152,8 +156,8 @@ module Vanity
152
156
 
153
157
  # Determines if a participant already has seen this alternative in this experiment.
154
158
  def ab_seen(experiment, identity, alternative)
155
- participant = @participants.find_one({ :experiment=>experiment, :identity=>identity }, { :fields=>[:seen] })
156
- participant && participant["seen"].first == alternative.id
159
+ participant = @participants.find_one({ :experiment=>experiment, :identity=>identity }, { :fields=>[:seen] })
160
+ participant && participant["seen"].first == alternative.id
157
161
  end
158
162
 
159
163
  # Returns the participant's seen alternative in this experiment, if it exists