vanity 1.8.4 → 1.9.0.beta
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.
- data/.travis.yml +3 -2
- data/CHANGELOG +12 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +12 -10
- data/README.rdoc +45 -16
- data/Rakefile +14 -9
- data/doc/_layouts/page.html +4 -6
- data/doc/ab_testing.textile +1 -1
- data/doc/configuring.textile +2 -4
- data/doc/email.textile +1 -3
- data/doc/index.textile +3 -63
- data/doc/rails.textile +34 -8
- data/gemfiles/rails3.gemfile +12 -3
- data/gemfiles/rails3.gemfile.lock +37 -11
- data/gemfiles/rails31.gemfile +12 -3
- data/gemfiles/rails31.gemfile.lock +37 -11
- data/gemfiles/rails32.gemfile +12 -3
- data/gemfiles/rails32.gemfile.lock +37 -11
- data/gemfiles/rails4.gemfile +12 -3
- data/gemfiles/rails4.gemfile.lock +37 -11
- data/lib/vanity/adapters/abstract_adapter.rb +4 -0
- data/lib/vanity/adapters/active_record_adapter.rb +18 -10
- data/lib/vanity/adapters/mock_adapter.rb +8 -4
- data/lib/vanity/adapters/mongodb_adapter.rb +11 -7
- data/lib/vanity/adapters/redis_adapter.rb +88 -37
- data/lib/vanity/commands/report.rb +9 -9
- data/lib/vanity/experiment/ab_test.rb +120 -101
- data/lib/vanity/experiment/alternative.rb +21 -21
- data/lib/vanity/experiment/base.rb +5 -5
- data/lib/vanity/experiment/bayesian_bandit_score.rb +51 -51
- data/lib/vanity/experiment/definition.rb +10 -10
- data/lib/vanity/frameworks/rails.rb +39 -36
- data/lib/vanity/helpers.rb +6 -4
- data/lib/vanity/metric/active_record.rb +1 -1
- data/lib/vanity/metric/base.rb +23 -24
- data/lib/vanity/metric/google_analytics.rb +5 -5
- data/lib/vanity/playground.rb +118 -24
- data/lib/vanity/templates/_report.erb +20 -6
- data/lib/vanity/templates/vanity.css +2 -0
- data/lib/vanity/version.rb +1 -1
- data/test/adapters/redis_adapter_test.rb +106 -1
- data/test/dummy/config/database.yml +21 -4
- data/test/dummy/config/routes.rb +1 -1
- data/test/experiment/ab_test.rb +93 -13
- data/test/metric/active_record_test.rb +9 -4
- data/test/passenger_test.rb +43 -42
- data/test/playground_test.rb +50 -1
- data/test/rails_dashboard_test.rb +38 -1
- data/test/rails_helper_test.rb +5 -0
- data/test/rails_test.rb +66 -15
- data/test/test_helper.rb +24 -2
- data/vanity.gemspec +0 -2
- metadata +45 -57
@@ -5,15 +5,14 @@ GIT
|
|
5
5
|
fastthread (1.0.7)
|
6
6
|
|
7
7
|
PATH
|
8
|
-
remote:
|
8
|
+
remote: ..
|
9
9
|
specs:
|
10
|
-
vanity (1.8.
|
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
|
-
|
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.
|
111
|
-
redis-namespace (1.
|
112
|
-
redis (~> 3.0
|
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
|
data/gemfiles/rails4.gemfile
CHANGED
@@ -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 "
|
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
|
-
|
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:
|
8
|
+
remote: ..
|
9
9
|
specs:
|
10
|
-
vanity (1.8.
|
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
|
-
|
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.
|
100
|
-
redis-namespace (1.
|
101
|
-
redis (~> 3.0
|
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
|
-
|
230
|
-
|
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
|
-
|
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
|
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
|
-
|
156
|
-
|
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
|