mikeg-vanity 1.3.0

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 (66) hide show
  1. data/CHANGELOG +153 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README.rdoc +83 -0
  4. data/bin/vanity +53 -0
  5. data/lib/vanity.rb +38 -0
  6. data/lib/vanity/backport.rb +43 -0
  7. data/lib/vanity/commands.rb +2 -0
  8. data/lib/vanity/commands/list.rb +21 -0
  9. data/lib/vanity/commands/report.rb +60 -0
  10. data/lib/vanity/experiment/ab_test.rb +477 -0
  11. data/lib/vanity/experiment/base.rb +212 -0
  12. data/lib/vanity/helpers.rb +59 -0
  13. data/lib/vanity/metric/active_record.rb +77 -0
  14. data/lib/vanity/metric/base.rb +221 -0
  15. data/lib/vanity/metric/google_analytics.rb +70 -0
  16. data/lib/vanity/mock_redis.rb +76 -0
  17. data/lib/vanity/playground.rb +197 -0
  18. data/lib/vanity/rails.rb +22 -0
  19. data/lib/vanity/rails/dashboard.rb +24 -0
  20. data/lib/vanity/rails/helpers.rb +158 -0
  21. data/lib/vanity/rails/testing.rb +11 -0
  22. data/lib/vanity/templates/_ab_test.erb +26 -0
  23. data/lib/vanity/templates/_experiment.erb +5 -0
  24. data/lib/vanity/templates/_experiments.erb +7 -0
  25. data/lib/vanity/templates/_metric.erb +14 -0
  26. data/lib/vanity/templates/_metrics.erb +13 -0
  27. data/lib/vanity/templates/_report.erb +27 -0
  28. data/lib/vanity/templates/flot.min.js +1 -0
  29. data/lib/vanity/templates/jquery.min.js +19 -0
  30. data/lib/vanity/templates/vanity.css +26 -0
  31. data/lib/vanity/templates/vanity.js +82 -0
  32. data/test/ab_test_test.rb +656 -0
  33. data/test/experiment_test.rb +136 -0
  34. data/test/experiments/age_and_zipcode.rb +19 -0
  35. data/test/experiments/metrics/cheers.rb +3 -0
  36. data/test/experiments/metrics/signups.rb +2 -0
  37. data/test/experiments/metrics/yawns.rb +3 -0
  38. data/test/experiments/null_abc.rb +5 -0
  39. data/test/metric_test.rb +518 -0
  40. data/test/playground_test.rb +10 -0
  41. data/test/rails_test.rb +104 -0
  42. data/test/test_helper.rb +135 -0
  43. data/vanity.gemspec +18 -0
  44. data/vendor/redis-rb/LICENSE +20 -0
  45. data/vendor/redis-rb/README.markdown +36 -0
  46. data/vendor/redis-rb/Rakefile +62 -0
  47. data/vendor/redis-rb/bench.rb +44 -0
  48. data/vendor/redis-rb/benchmarking/suite.rb +24 -0
  49. data/vendor/redis-rb/benchmarking/worker.rb +71 -0
  50. data/vendor/redis-rb/bin/distredis +33 -0
  51. data/vendor/redis-rb/examples/basic.rb +16 -0
  52. data/vendor/redis-rb/examples/incr-decr.rb +18 -0
  53. data/vendor/redis-rb/examples/list.rb +26 -0
  54. data/vendor/redis-rb/examples/sets.rb +36 -0
  55. data/vendor/redis-rb/lib/dist_redis.rb +124 -0
  56. data/vendor/redis-rb/lib/hash_ring.rb +128 -0
  57. data/vendor/redis-rb/lib/pipeline.rb +21 -0
  58. data/vendor/redis-rb/lib/redis.rb +370 -0
  59. data/vendor/redis-rb/lib/redis/raketasks.rb +1 -0
  60. data/vendor/redis-rb/profile.rb +22 -0
  61. data/vendor/redis-rb/redis-rb.gemspec +30 -0
  62. data/vendor/redis-rb/spec/redis_spec.rb +637 -0
  63. data/vendor/redis-rb/spec/spec_helper.rb +4 -0
  64. data/vendor/redis-rb/speed.rb +16 -0
  65. data/vendor/redis-rb/tasks/redis.tasks.rb +140 -0
  66. metadata +125 -0
@@ -0,0 +1,136 @@
1
+ require "test/test_helper"
2
+
3
+ class ExperimentTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ super
7
+ metric "Happiness"
8
+ end
9
+
10
+ # -- Defining experiment --
11
+
12
+ def test_can_access_experiment_by_id
13
+ exp = new_ab_test(:ice_cream_flavor) { metrics :happiness }
14
+ assert_equal exp, experiment(:ice_cream_flavor)
15
+ end
16
+
17
+ def test_fail_when_defining_same_experiment_twice
18
+ File.open "tmp/experiments/ice_cream_flavor.rb", "w" do |f|
19
+ f.write <<-RUBY
20
+ ab_test "Ice Cream Flavor" do
21
+ metrics :happiness
22
+ end
23
+ ab_test "Ice Cream Flavor" do
24
+ metrics :happiness
25
+ end
26
+ RUBY
27
+ end
28
+ assert_raises NameError do
29
+ experiment(:ice_cream_flavor)
30
+ end
31
+ end
32
+
33
+ def test_uses_playground_namespace_for_experiment
34
+ new_ab_test(:ice_cream_flavor) { metrics :happiness }
35
+ assert_equal "vanity:#{Vanity::Version::MAJOR}:ice_cream_flavor", experiment(:ice_cream_flavor).send(:key)
36
+ assert_equal "vanity:#{Vanity::Version::MAJOR}:ice_cream_flavor:participants", experiment(:ice_cream_flavor).send(:key, "participants")
37
+ end
38
+
39
+
40
+ # -- Loading experiments --
41
+
42
+ def test_fails_if_cannot_load_named_experiment
43
+ assert_raises NameError do
44
+ experiment(:ice_cream_flavor)
45
+ end
46
+ end
47
+
48
+ def test_loading_experiment
49
+ File.open "tmp/experiments/ice_cream_flavor.rb", "w" do |f|
50
+ f.write <<-RUBY
51
+ ab_test "Ice Cream Flavor" do
52
+ def xmts
53
+ "x"
54
+ end
55
+ metrics :happiness
56
+ end
57
+ RUBY
58
+ end
59
+ assert_equal "x", experiment(:ice_cream_flavor).xmts
60
+ end
61
+
62
+ def test_fails_if_error_loading_experiment
63
+ File.open "tmp/experiments/ice_cream_flavor.rb", "w" do |f|
64
+ f.write "fail 'yawn!'"
65
+ end
66
+ assert_raises NameError do
67
+ experiment(:ice_cream_flavor)
68
+ end
69
+ end
70
+
71
+ def test_complains_if_not_defined_where_expected
72
+ File.open "tmp/experiments/ice_cream_flavor.rb", "w" do |f|
73
+ f.write ""
74
+ end
75
+ assert_raises NameError do
76
+ experiment(:ice_cream_flavor)
77
+ end
78
+ end
79
+
80
+ def test_reloading_experiments
81
+ new_ab_test(:ab) { metrics :happiness }
82
+ new_ab_test(:cd) { metrics :happiness }
83
+ assert 2, Vanity.playground.experiments.size
84
+ Vanity.playground.reload!
85
+ assert Vanity.playground.experiments.empty?
86
+ end
87
+
88
+
89
+ # -- Attributes --
90
+
91
+ def test_experiment_mapping_name_to_id
92
+ experiment = new_ab_test("Ice Cream Flavor/Tastes") { metrics :happiness }
93
+ assert_equal "Ice Cream Flavor/Tastes", experiment.name
94
+ assert_equal :ice_cream_flavor_tastes, experiment.id
95
+ end
96
+
97
+ def test_saving_experiment_after_definition
98
+ File.open "tmp/experiments/ice_cream_flavor.rb", "w" do |f|
99
+ f.write <<-RUBY
100
+ ab_test "Ice Cream Flavor" do
101
+ metrics :happiness
102
+ expects(:save)
103
+ end
104
+ RUBY
105
+ end
106
+ Vanity.playground.experiment(:ice_cream_flavor)
107
+ end
108
+
109
+ def test_experiment_has_created_timestamp
110
+ new_ab_test(:ice_cream_flavor) { metrics :happiness }
111
+ assert_instance_of Time, experiment(:ice_cream_flavor).created_at
112
+ assert_in_delta experiment(:ice_cream_flavor).created_at.to_i, Time.now.to_i, 1
113
+ end
114
+
115
+ def test_experiment_keeps_created_timestamp_across_definitions
116
+ past = Date.today - 1
117
+ Timecop.travel past do
118
+ new_ab_test(:ice_cream_flavor) { metrics :happiness }
119
+ assert_equal past.to_time.to_i, experiment(:ice_cream_flavor).created_at.to_i
120
+ end
121
+
122
+ new_playground
123
+ metric :happiness
124
+ new_ab_test(:ice_cream_flavor) { metrics :happiness }
125
+ assert_equal past.to_time.to_i, experiment(:ice_cream_flavor).created_at.to_i
126
+ end
127
+
128
+ def test_experiment_has_description
129
+ new_ab_test :ice_cream_flavor do
130
+ description "Because 31 is not enough ..."
131
+ metrics :happiness
132
+ end
133
+ assert_equal "Because 31 is not enough ...", experiment(:ice_cream_flavor).description
134
+ end
135
+
136
+ end
@@ -0,0 +1,19 @@
1
+ ab_test "Age and Zipcode" do
2
+ description <<-TEXT
3
+ Testing new registration form that asks for age and zipcode. Option A presents
4
+ the existing form, and option B adds age and zipcode fields.
5
+
6
+ We know option B will convert less, but higher quality leads. If we lose less
7
+ than 20% conversions, we're going to switch to option B.
8
+ TEXT
9
+ metrics :signups
10
+
11
+ complete_if do
12
+ alternatives.all? { |alt| alt.participants > 100 }
13
+ end
14
+ outcome_is do
15
+ one_field = alternative(false)
16
+ three_fields = alternative(true)
17
+ three_fields.conversion_rate >= 0.8 * one_field.conversion_rate ? three_fields : one_field
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ metric "Cheers" do
2
+ description "They love us, don't they?"
3
+ end
@@ -0,0 +1,2 @@
1
+ metric "Signups" do
2
+ end
@@ -0,0 +1,3 @@
1
+ metric "Yawns" do
2
+ description "How many yawns/sec can our video-sharing micro-blogging social network elicit?"
3
+ end
@@ -0,0 +1,5 @@
1
+ ab_test "Null/ABC" do
2
+ description "Testing three alternatives"
3
+ alternatives nil, :red, :green, :blue
4
+ metrics :signups
5
+ end
@@ -0,0 +1,518 @@
1
+ require "test/test_helper"
2
+
3
+ class Sky < ActiveRecord::Base
4
+ connection.drop_table :skies if table_exists?
5
+ connection.create_table :skies do |t|
6
+ t.integer :height
7
+ t.timestamps
8
+ end
9
+
10
+ named_scope :high, lambda { { :conditions=>"height >= 4" } }
11
+ end
12
+
13
+
14
+ context "Metric" do
15
+
16
+ # -- Via the playground --
17
+
18
+ context "playground" do
19
+
20
+ test "knows all loaded metrics" do
21
+ metric "Yawns/sec", "Cheers/sec"
22
+ assert Vanity.playground.metrics.keys.include?(:yawns_sec)
23
+ assert Vanity.playground.metrics.keys.include?(:cheers_sec)
24
+ end
25
+
26
+ test "loads metric definitions" do
27
+ File.open "tmp/experiments/metrics/yawns_sec.rb", "w" do |f|
28
+ f.write <<-RUBY
29
+ metric "Yawns/sec" do
30
+ def xmts
31
+ "x"
32
+ end
33
+ end
34
+ RUBY
35
+ end
36
+ assert_equal "x", Vanity.playground.metric(:yawns_sec).xmts
37
+ end
38
+
39
+ test "bubbles up loaded metrics" do
40
+ File.open "tmp/experiments/metrics/yawns_sec.rb", "w" do |f|
41
+ f.write "fail 'yawn!'"
42
+ end
43
+ assert_raises NameError do
44
+ Vanity.playground.metric(:yawns_sec)
45
+ end
46
+ end
47
+
48
+ test "map identifier from file name" do
49
+ File.open "tmp/experiments/metrics/yawns_sec.rb", "w" do |f|
50
+ f.write <<-RUBY
51
+ metric "yawns/hour" do
52
+ end
53
+ RUBY
54
+ end
55
+ assert Vanity.playground.metric(:yawns_sec)
56
+ end
57
+
58
+ test "fails tracking unknown metric" do
59
+ assert_raises NameError do
60
+ Vanity.playground.track! :yawns_sec
61
+ end
62
+ end
63
+
64
+ test "reloading metrics" do
65
+ metric "Yawns/sec", "Cheers/sec"
66
+ Vanity.playground.metric(:yawns_sec)
67
+ Vanity.playground.metric(:cheers_sec)
68
+ assert 2, Vanity.playground.metrics.size
69
+ metrics = Vanity.playground.metrics.values
70
+ Vanity.playground.reload!
71
+ assert 2, Vanity.playground.metrics.size
72
+ assert_not_equal metrics, Vanity.playground.metrics.values
73
+ end
74
+
75
+ test "ignores undefined metrics in database" do
76
+ metric "Yawns/sec"
77
+ Vanity.playground.reload!
78
+ assert Vanity.playground.metrics.empty?
79
+ end
80
+
81
+ end
82
+
83
+
84
+ # -- Tracking --
85
+
86
+ context "tracking" do
87
+ test "can count" do
88
+ metric "Yawns/sec", "Cheers/sec"
89
+ 4.times { Vanity.playground.track! :yawns_sec }
90
+ 2.times { Vanity.playground.track! :cheers_sec }
91
+ yawns = Vanity.playground.metric(:yawns_sec).values(today, today).first
92
+ cheers = Vanity.playground.metric(:cheers_sec).values(today, today).first
93
+ assert yawns = 2 * cheers
94
+ end
95
+
96
+ test "can tell the time" do
97
+ metric "Yawns/sec"
98
+ Timecop.travel(today - 4) { 4.times { Vanity.playground.track! :yawns_sec } }
99
+ Timecop.travel(today - 2) { 2.times { Vanity.playground.track! :yawns_sec } }
100
+ 1.times { Vanity.playground.track! :yawns_sec }
101
+ boredom = Vanity.playground.metric(:yawns_sec).values(today - 5, today)
102
+ assert_equal [0,4,0,2,0,1], boredom
103
+ end
104
+
105
+ test "with no value" do
106
+ metric "Yawns/sec", "Cheers/sec", "Looks"
107
+ Vanity.playground.track! :yawns_sec, 0
108
+ Vanity.playground.track! :cheers_sec, -1
109
+ Vanity.playground.track! :looks, 10
110
+ assert_equal 0, Vanity.playground.metric(:yawns_sec).values(today, today).sum
111
+ assert_equal 0, Vanity.playground.metric(:cheers_sec).values(today, today).sum
112
+ assert_equal 10, Vanity.playground.metric(:looks).values(today, today).sum
113
+ end
114
+
115
+ test "with count" do
116
+ metric "Yawns/sec"
117
+ Timecop.travel(today - 4) { Vanity.playground.track! :yawns_sec, 4 }
118
+ Timecop.travel(today - 2) { Vanity.playground.track! :yawns_sec, 2 }
119
+ Vanity.playground.track! :yawns_sec
120
+ boredom = Vanity.playground.metric(:yawns_sec).values(today - 5, today)
121
+ assert_equal [0,4,0,2,0,1], boredom
122
+ end
123
+
124
+ test "runs hook" do
125
+ metric "Many Happy Returns"
126
+ total = 0
127
+ Vanity.playground.metric(:many_happy_returns).hook do |metric_id, timestamp, count|
128
+ assert_equal :many_happy_returns, metric_id
129
+ assert_in_delta Time.now.to_i, timestamp.to_i, 1
130
+ total += count
131
+ end
132
+ Vanity.playground.track! :many_happy_returns, 6
133
+ assert_equal 6, total
134
+ end
135
+
136
+ test "runs multiple hooks" do
137
+ metric "Many Happy Returns"
138
+ returns = 0
139
+ Vanity.playground.metric(:many_happy_returns).hook { returns += 1 }
140
+ Vanity.playground.metric(:many_happy_returns).hook { returns += 1 }
141
+ Vanity.playground.metric(:many_happy_returns).hook { returns += 1 }
142
+ Vanity.playground.track! :many_happy_returns
143
+ assert_equal 3, returns
144
+ end
145
+
146
+ test "destroy wipes metrics" do
147
+ metric "Many Happy Returns"
148
+ Vanity.playground.track! :many_happy_returns, 3
149
+ assert_equal [3], Vanity.playground.metric(:many_happy_returns).values(today, today)
150
+ Vanity.playground.metric(:many_happy_returns).destroy!
151
+ assert_equal [0], Vanity.playground.metric(:many_happy_returns).values(today, today)
152
+ end
153
+ end
154
+
155
+
156
+ # -- Metric name --
157
+
158
+ context "name" do
159
+ test "can be whatever" do
160
+ File.open "tmp/experiments/metrics/yawns_sec.rb", "w" do |f|
161
+ f.write <<-RUBY
162
+ metric "Yawns per second" do
163
+ end
164
+ RUBY
165
+ end
166
+ assert_equal "Yawns per second", Vanity.playground.metric(:yawns_sec).name
167
+ end
168
+ end
169
+
170
+
171
+ # -- Description helper --
172
+
173
+ context "description" do
174
+ test "metric with description" do
175
+ File.open "tmp/experiments/metrics/yawns_sec.rb", "w" do |f|
176
+ f.write <<-RUBY
177
+ metric "Yawns/sec" do
178
+ description "Am I that boring?"
179
+ end
180
+ RUBY
181
+ end
182
+ assert_equal "Am I that boring?", Vanity::Metric.description(Vanity.playground.metric(:yawns_sec))
183
+ end
184
+
185
+ test "metric without description" do
186
+ File.open "tmp/experiments/metrics/yawns_sec.rb", "w" do |f|
187
+ f.write <<-RUBY
188
+ metric "Yawns/sec" do
189
+ end
190
+ RUBY
191
+ end
192
+ assert_nil Vanity::Metric.description(Vanity.playground.metric(:yawns_sec))
193
+ end
194
+
195
+ test "metric with no method description" do
196
+ metric = Object.new
197
+ assert_nil Vanity::Metric.description(metric)
198
+ end
199
+ end
200
+
201
+
202
+ # -- Metric bounds --
203
+
204
+ context "bounds" do
205
+ test "metric with bounds" do
206
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
207
+ f.write <<-RUBY
208
+ metric "Sky is limit" do
209
+ def bounds
210
+ [6,12]
211
+ end
212
+ end
213
+ RUBY
214
+ end
215
+ assert_equal [6,12], Vanity::Metric.bounds(Vanity.playground.metric(:sky_is_limit))
216
+ end
217
+
218
+ test "metric without bounds" do
219
+ metric "Sky is limit"
220
+ assert_equal [nil, nil], Vanity::Metric.bounds(Vanity.playground.metric(:sky_is_limit))
221
+ end
222
+
223
+ test "metric with no method bounds" do
224
+ metric = Object.new
225
+ assert_equal [nil, nil], Vanity::Metric.bounds(metric)
226
+ end
227
+ end
228
+
229
+
230
+ # -- Timestamp --
231
+
232
+ context "created_at" do
233
+ test "for new metric" do
234
+ metric "Coolness"
235
+ metric = Vanity.playground.metric(:coolness)
236
+ assert_instance_of Time, metric.created_at
237
+ assert_in_delta metric.created_at.to_i, Time.now.to_i, 1
238
+ end
239
+
240
+ test "across restarts" do
241
+ past = Date.today - 1
242
+ Timecop.travel past do
243
+ metric "Coolness"
244
+ coolness = Vanity.playground.metric(:coolness)
245
+ assert_in_delta coolness.created_at.to_i, past.to_time.to_i, 1
246
+ end
247
+
248
+ new_playground
249
+ metric "Coolness"
250
+ new_cool = Vanity.playground.metric(:coolness)
251
+ assert_in_delta new_cool.created_at.to_i, past.to_time.to_i, 1
252
+ end
253
+ end
254
+
255
+
256
+ # -- Data --
257
+
258
+ context "data" do
259
+ test "explicit dates" do
260
+ metric "Yawns/sec"
261
+ Timecop.travel(today - 4) { Vanity.playground.track! :yawns_sec, 4 }
262
+ Timecop.travel(today - 2) { Vanity.playground.track! :yawns_sec, 2 }
263
+ Vanity.playground.track! :yawns_sec
264
+ boredom = Vanity::Metric.data(Vanity.playground.metric(:yawns_sec), Date.today - 5, Date.today)
265
+ assert_equal [[today - 5, 0], [today - 4, 4], [today - 3, 0], [today - 2, 2], [today - 1, 0], [today, 1]], boredom
266
+ end
267
+
268
+ test "start date only" do
269
+ metric "Yawns/sec"
270
+ Timecop.travel(today - 4) { Vanity.playground.track! :yawns_sec, 4 }
271
+ Timecop.travel(today - 2) { Vanity.playground.track! :yawns_sec, 2 }
272
+ Vanity.playground.track! :yawns_sec
273
+ boredom = Vanity::Metric.data(Vanity.playground.metric(:yawns_sec), Date.today - 4)
274
+ assert_equal [[today - 4, 4], [today - 3, 0], [today - 2, 2], [today - 1, 0], [today, 1]], boredom
275
+ end
276
+
277
+ test "start date and duration" do
278
+ metric "Yawns/sec"
279
+ Timecop.travel(today - 4) { Vanity.playground.track! :yawns_sec, 4 }
280
+ Timecop.travel(today - 2) { Vanity.playground.track! :yawns_sec, 2 }
281
+ Vanity.playground.track! :yawns_sec
282
+ boredom = Vanity::Metric.data(Vanity.playground.metric(:yawns_sec), 5)
283
+ assert_equal [[today - 4, 4], [today - 3, 0], [today - 2, 2], [today - 1, 0], [today, 1]], boredom
284
+ end
285
+
286
+ test "no data" do
287
+ metric "Yawns/sec"
288
+ boredom = Vanity::Metric.data(Vanity.playground.metric(:yawns_sec))
289
+ assert_equal 90, boredom.size
290
+ assert_equal [today - 89, 0], boredom.first
291
+ assert_equal [today, 0], boredom.last
292
+ end
293
+
294
+ test "using custom values method" do
295
+ File.open "tmp/experiments/metrics/hours_in_day.rb", "w" do |f|
296
+ f.write <<-RUBY
297
+ metric "Hours in day" do
298
+ def values(from, to)
299
+ (from..to).map { |d| 24 }
300
+ end
301
+ end
302
+ RUBY
303
+ end
304
+ data = Vanity::Metric.data(Vanity.playground.metric(:hours_in_day))
305
+ assert_equal [24] * 90, data.map(&:last)
306
+ end
307
+
308
+ end
309
+
310
+
311
+ # -- ActiveRecord --
312
+
313
+ context "ActiveRecord" do
314
+
315
+ test "record count" do
316
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
317
+ f.write <<-RUBY
318
+ metric "Sky is limit" do
319
+ model Sky
320
+ end
321
+ RUBY
322
+ end
323
+ Vanity.playground.metrics
324
+ Sky.create!
325
+ assert_equal 1, Sky.count
326
+ assert_equal 1, Vanity::Metric.data(metric(:sky_is_limit)).last.last
327
+ end
328
+
329
+ test "record sum" do
330
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
331
+ f.write <<-RUBY
332
+ metric "Sky is limit" do
333
+ model Sky, :sum=>:height
334
+ end
335
+ RUBY
336
+ end
337
+ Vanity.playground.metrics
338
+ Sky.create! :height=>4
339
+ Sky.create! :height=>2
340
+ assert_equal 6, Vanity::Metric.data(metric(:sky_is_limit)).last.last
341
+ end
342
+
343
+ test "record average" do
344
+ Sky.aggregates
345
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
346
+ f.write <<-RUBY
347
+ metric "Sky is limit" do
348
+ model Sky, :average=>:height
349
+ end
350
+ RUBY
351
+ end
352
+ Vanity.playground.metrics
353
+ Sky.create! :height=>4
354
+ Sky.create! :height=>2
355
+ assert_equal 3, Vanity::Metric.data(metric(:sky_is_limit)).last.last
356
+ end
357
+
358
+ test "record minimum" do
359
+ Sky.aggregates
360
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
361
+ f.write <<-RUBY
362
+ metric "Sky is limit" do
363
+ model Sky, :minimum=>:height
364
+ end
365
+ RUBY
366
+ end
367
+ Vanity.playground.metrics
368
+ Sky.create! :height=>4
369
+ Sky.create! :height=>2
370
+ assert_equal 2, Vanity::Metric.data(metric(:sky_is_limit)).last.last
371
+ end
372
+
373
+ test "record maximum" do
374
+ Sky.aggregates
375
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
376
+ f.write <<-RUBY
377
+ metric "Sky is limit" do
378
+ model Sky, :maximum=>:height
379
+ end
380
+ RUBY
381
+ end
382
+ Vanity.playground.metrics
383
+ Sky.create! :height=>4
384
+ Sky.create! :height=>2
385
+ assert_equal 4, Vanity::Metric.data(metric(:sky_is_limit)).last.last
386
+ end
387
+
388
+ test "with conditions" do
389
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
390
+ f.write <<-RUBY
391
+ metric "Sky is limit" do
392
+ model Sky, :sum=>:height, :conditions=>["height > 4"]
393
+ end
394
+ RUBY
395
+ end
396
+ Vanity.playground.metrics
397
+ high_skies = 0
398
+ metric(:sky_is_limit).hook do |metric_id, timestamp, height|
399
+ assert height > 4
400
+ high_skies += height
401
+ end
402
+ [nil,5,3,6].each do |height|
403
+ Sky.create! :height=>height
404
+ end
405
+ assert_equal 11, Vanity::Metric.data(metric(:sky_is_limit)).sum(&:last)
406
+ assert_equal 11, high_skies
407
+ end
408
+
409
+ test "with scope" do
410
+ Sky.aggregates
411
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
412
+ f.write <<-RUBY
413
+ metric "Sky is limit" do
414
+ model Sky.high
415
+ end
416
+ RUBY
417
+ end
418
+ Vanity.playground.metrics
419
+ total = 0
420
+ metric(:sky_is_limit).hook do |metric_id, timestamp, count|
421
+ total += count
422
+ end
423
+ Sky.create! :height=>4
424
+ Sky.create! :height=>2
425
+ assert_equal 1, Vanity::Metric.data(metric(:sky_is_limit)).last.last
426
+ assert_equal 1, total
427
+ end
428
+
429
+ test "hooks" do
430
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
431
+ f.write <<-RUBY
432
+ metric "Sky is limit" do
433
+ model Sky, :sum=>:height
434
+ end
435
+ RUBY
436
+ end
437
+ Vanity.playground.metrics
438
+ total = 0
439
+ metric(:sky_is_limit).hook do |metric_id, timestamp, count|
440
+ assert_equal :sky_is_limit, metric_id
441
+ assert_in_delta Time.now.to_i, timestamp.to_i, 1
442
+ total += count
443
+ end
444
+ Sky.create! :height=>4
445
+ assert_equal 4, total
446
+ end
447
+
448
+ test "after_create not after_save" do
449
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
450
+ f.write <<-RUBY
451
+ metric "Sky is limit" do
452
+ model Sky
453
+ end
454
+ RUBY
455
+ end
456
+ Vanity.playground.metrics
457
+ once = nil
458
+ metric(:sky_is_limit).hook do
459
+ fail "Metric tracked twice" if once
460
+ once = true
461
+ end
462
+ Sky.create!
463
+ Sky.last.update_attributes :height=>4
464
+ end
465
+
466
+ test "with after_save" do
467
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
468
+ f.write <<-RUBY
469
+ metric "Sky is limit" do
470
+ model Sky, :conditions=>["height > 3"]
471
+ Sky.after_save { |sky| track! if sky.height_changed? && sky.height > 3 }
472
+ end
473
+ RUBY
474
+ end
475
+ Vanity.playground.metrics
476
+ times = 0
477
+ metric(:sky_is_limit).hook do
478
+ times += 1
479
+ end
480
+ Sky.create!
481
+ (1..5).each do |height|
482
+ Sky.last.update_attributes! :height=>height
483
+ end
484
+ assert_equal 2, times
485
+ end
486
+
487
+ test "do it youself" do
488
+ File.open "tmp/experiments/metrics/sky_is_limit.rb", "w" do |f|
489
+ f.write <<-RUBY
490
+ metric "Sky is limit" do
491
+ Sky.after_save { |sky| track! if sky.height_changed? && sky.height > 3 }
492
+ end
493
+ RUBY
494
+ end
495
+ Vanity.playground.metrics
496
+ (1..5).each do |height|
497
+ Sky.create! :height=>height
498
+ end
499
+ Sky.first.update_attributes! :height=>4
500
+ assert_equal 3, Vanity::Metric.data(metric(:sky_is_limit)).last.last
501
+ end
502
+
503
+ end
504
+
505
+
506
+ # -- Helper methods --
507
+
508
+ def today
509
+ @today ||= Date.today
510
+ end
511
+
512
+ teardown do
513
+ Sky.delete_all
514
+ Sky.after_create.clear
515
+ Sky.after_save.clear
516
+ end
517
+
518
+ end