vanity 1.3.0 → 1.4.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +61 -3
- data/Gemfile +22 -14
- data/README.rdoc +9 -4
- data/Rakefile +72 -12
- data/bin/vanity +16 -4
- data/lib/vanity.rb +7 -5
- data/lib/vanity/adapters/abstract_adapter.rb +135 -0
- data/lib/vanity/adapters/mock_adapter.rb +157 -0
- data/lib/vanity/adapters/mongo_adapter.rb +162 -0
- data/lib/vanity/adapters/redis_adapter.rb +154 -0
- data/lib/vanity/backport.rb +0 -17
- data/lib/vanity/commands/upgrade.rb +34 -0
- data/lib/vanity/experiment/ab_test.rb +46 -41
- data/lib/vanity/experiment/base.rb +13 -15
- data/lib/vanity/frameworks/rails.rb +5 -9
- data/lib/vanity/metric/active_record.rb +10 -4
- data/lib/vanity/metric/base.rb +46 -23
- data/lib/vanity/metric/google_analytics.rb +7 -0
- data/lib/vanity/metric/remote.rb +53 -0
- data/lib/vanity/playground.rb +133 -49
- data/test/{ab_test_test.rb → experiment/ab_test.rb} +47 -3
- data/test/{experiment_test.rb → experiment/base_test.rb} +8 -8
- data/test/metric/active_record_test.rb +253 -0
- data/test/metric/base_test.rb +293 -0
- data/test/metric/google_analytics_test.rb +104 -0
- data/test/metric/remote_test.rb +108 -0
- data/test/myapp/app/controllers/application_controller.rbc +66 -0
- data/test/myapp/app/controllers/main_controller.rb +3 -3
- data/test/myapp/app/controllers/main_controller.rbc +347 -0
- data/test/myapp/config/boot.rbc +2534 -0
- data/test/myapp/config/environment.rbc +403 -0
- data/test/myapp/config/routes.rbc +174 -0
- data/test/myapp/log/production.log +2601 -0
- data/test/passenger_test.rb +14 -5
- data/test/passenger_test.rbc +0 -0
- data/test/playground_test.rbc +256 -0
- data/test/rails_test.rb +75 -22
- data/test/rails_test.rbc +4086 -0
- data/test/test_helper.rb +30 -7
- data/test/test_helper.rbc +4297 -0
- data/vanity.gemspec +6 -2
- metadata +74 -73
- data/lib/vanity/commands.rb +0 -2
- data/lib/vanity/mock_redis.rb +0 -76
- data/test/metric_test.rb +0 -622
- data/vendor/cache/RedCloth-4.2.2.gem +0 -0
- data/vendor/cache/actionmailer-2.3.5.gem +0 -0
- data/vendor/cache/actionpack-2.3.5.gem +0 -0
- data/vendor/cache/activerecord-2.3.5.gem +0 -0
- data/vendor/cache/activeresource-2.3.5.gem +0 -0
- data/vendor/cache/activesupport-2.3.5.gem +0 -0
- data/vendor/cache/autotest-4.2.7.gem +0 -0
- data/vendor/cache/autotest-fsevent-0.2.1.gem +0 -0
- data/vendor/cache/autotest-growl-0.2.0.gem +0 -0
- data/vendor/cache/bundler-0.9.7.gem +0 -0
- data/vendor/cache/classifier-1.3.1.gem +0 -0
- data/vendor/cache/directory_watcher-1.3.1.gem +0 -0
- data/vendor/cache/fastthread-1.0.7.gem +0 -0
- data/vendor/cache/garb-0.7.0.gem +0 -0
- data/vendor/cache/happymapper-0.3.0.gem +0 -0
- data/vendor/cache/jekyll-0.5.7.gem +0 -0
- data/vendor/cache/libxml-ruby-1.1.3.gem +0 -0
- data/vendor/cache/liquid-2.0.0.gem +0 -0
- data/vendor/cache/maruku-0.6.0.gem +0 -0
- data/vendor/cache/mocha-0.9.8.gem +0 -0
- data/vendor/cache/open4-1.0.1.gem +0 -0
- data/vendor/cache/passenger-2.2.9.gem +0 -0
- data/vendor/cache/rack-1.0.1.gem +0 -0
- data/vendor/cache/rails-2.3.5.gem +0 -0
- data/vendor/cache/rake-0.8.7.gem +0 -0
- data/vendor/cache/rubygems-update-1.3.5.gem +0 -0
- data/vendor/cache/shoulda-2.10.3.gem +0 -0
- data/vendor/cache/sqlite3-ruby-1.2.5.gem +0 -0
- data/vendor/cache/stemmer-1.0.1.gem +0 -0
- data/vendor/cache/syntax-1.0.0.gem +0 -0
- data/vendor/cache/sys-uname-0.8.4.gem +0 -0
- data/vendor/cache/timecop-0.3.4.gem +0 -0
- data/vendor/redis-rb/LICENSE +0 -20
- data/vendor/redis-rb/README.markdown +0 -36
- data/vendor/redis-rb/Rakefile +0 -62
- data/vendor/redis-rb/bench.rb +0 -44
- data/vendor/redis-rb/benchmarking/suite.rb +0 -24
- data/vendor/redis-rb/benchmarking/worker.rb +0 -71
- data/vendor/redis-rb/bin/distredis +0 -33
- data/vendor/redis-rb/examples/basic.rb +0 -16
- data/vendor/redis-rb/examples/incr-decr.rb +0 -18
- data/vendor/redis-rb/examples/list.rb +0 -26
- data/vendor/redis-rb/examples/sets.rb +0 -36
- data/vendor/redis-rb/lib/dist_redis.rb +0 -124
- data/vendor/redis-rb/lib/hash_ring.rb +0 -128
- data/vendor/redis-rb/lib/pipeline.rb +0 -21
- data/vendor/redis-rb/lib/redis.rb +0 -370
- data/vendor/redis-rb/lib/redis/raketasks.rb +0 -1
- data/vendor/redis-rb/profile.rb +0 -22
- data/vendor/redis-rb/redis-rb.gemspec +0 -30
- data/vendor/redis-rb/spec/redis_spec.rb +0 -637
- data/vendor/redis-rb/spec/spec_helper.rb +0 -4
- data/vendor/redis-rb/speed.rb +0 -16
- data/vendor/redis-rb/tasks/redis.tasks.rb +0 -140
data/lib/vanity/backport.rb
CHANGED
@@ -24,20 +24,3 @@ class Date
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
class Symbol
|
28
|
-
unless method_defined?(:to_proc)
|
29
|
-
# Backported from Ruby 1.9.
|
30
|
-
def to_proc
|
31
|
-
Proc.new { |*args| args.shift.__send__(self, *args) }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class Array
|
37
|
-
unless method_defined?(:minmax)
|
38
|
-
# Backported from Ruby 1.9.
|
39
|
-
def minmax
|
40
|
-
[min, max]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Vanity
|
2
|
+
module Commands
|
3
|
+
class << self
|
4
|
+
# Upgrade to newer version of Vanity (this usually means doing magic in
|
5
|
+
# the database)
|
6
|
+
def upgrade
|
7
|
+
if Vanity.playground.connection.respond_to?(:redis)
|
8
|
+
redis = Vanity.playground.connection.redis
|
9
|
+
# Upgrade metrics from 1.3 to 1.4
|
10
|
+
keys = redis.keys("metrics:*")
|
11
|
+
if keys.empty?
|
12
|
+
puts "No metrics to upgrade"
|
13
|
+
else
|
14
|
+
puts "Updating #{keys.map { |name| name.split(":")[1] }.uniq.length} metrics"
|
15
|
+
keys.each do |key|
|
16
|
+
key << ":value:0" if key[/\d{4}-\d{2}-\d{2}$/]
|
17
|
+
redis.renamenx key, "vanity:#{key}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
# Upgrade experiments from 1.3 to 1.4
|
21
|
+
keys = redis.keys("vanity:1:*")
|
22
|
+
if keys.empty?
|
23
|
+
puts "No experiments to upgrade"
|
24
|
+
else
|
25
|
+
puts "Updating #{keys.map { |name| name.split(":")[2] }.uniq.length} experiments"
|
26
|
+
keys.each do |key|
|
27
|
+
redis.renamenx key, key.gsub(":1:", ":experiments:")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -93,7 +93,6 @@ module Vanity
|
|
93
93
|
|
94
94
|
def initialize(*args)
|
95
95
|
super
|
96
|
-
@alternatives = [false, true]
|
97
96
|
end
|
98
97
|
|
99
98
|
|
@@ -130,22 +129,18 @@ module Vanity
|
|
130
129
|
# alts = experiment(:background_color).alternatives
|
131
130
|
# puts "#{alts.count} alternatives, with the colors: #{alts.map(&:value).join(", ")}"
|
132
131
|
def alternatives(*args)
|
133
|
-
|
134
|
-
@alternatives = args.clone
|
135
|
-
end
|
132
|
+
@alternatives = args.empty? ? [true, false] : args.clone
|
136
133
|
class << self
|
137
134
|
define_method :alternatives, instance_method(:_alternatives)
|
138
135
|
end
|
139
|
-
|
136
|
+
nil
|
140
137
|
end
|
141
138
|
|
142
139
|
def _alternatives
|
143
140
|
alts = []
|
144
141
|
@alternatives.each_with_index do |value, i|
|
145
|
-
|
146
|
-
|
147
|
-
conversions = redis[key("alts:#{i}:conversions")].to_i
|
148
|
-
alts << Alternative.new(self, i, value, participants, converted, conversions)
|
142
|
+
counts = @playground.collecting? ? connection.ab_counts(@id, i) : Hash.new(0)
|
143
|
+
alts << Alternative.new(self, i, value, counts[:participants], counts[:converted], counts[:conversions])
|
149
144
|
end
|
150
145
|
alts
|
151
146
|
end
|
@@ -186,16 +181,22 @@ module Vanity
|
|
186
181
|
# @example
|
187
182
|
# color = experiment(:which_blue).choose
|
188
183
|
def choose
|
189
|
-
if
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
index
|
194
|
-
|
195
|
-
|
184
|
+
if @playground.collecting?
|
185
|
+
if active?
|
186
|
+
identity = identity()
|
187
|
+
index = connection.ab_showing(@id, identity)
|
188
|
+
unless index
|
189
|
+
index = alternative_for(identity)
|
190
|
+
connection.ab_add_participant @id, index, identity
|
191
|
+
check_completion!
|
192
|
+
end
|
193
|
+
else
|
194
|
+
index = connection.ab_get_outcome(@id) || alternative_for(identity)
|
196
195
|
end
|
197
196
|
else
|
198
|
-
|
197
|
+
identity = identity()
|
198
|
+
@showing ||= {}
|
199
|
+
@showing[identity] ||= alternative_for(identity)
|
199
200
|
end
|
200
201
|
@alternatives[index.to_i]
|
201
202
|
end
|
@@ -227,12 +228,17 @@ module Vanity
|
|
227
228
|
# experiment(:green_button).select(nil)
|
228
229
|
# end
|
229
230
|
def chooses(value)
|
230
|
-
if
|
231
|
-
|
231
|
+
if @playground.collecting?
|
232
|
+
if value.nil?
|
233
|
+
connection.ab_not_showing @id, identity
|
234
|
+
else
|
235
|
+
index = @alternatives.index(value)
|
236
|
+
raise ArgumentError, "No alternative #{value.inspect} for #{name}" unless index
|
237
|
+
connection.ab_show @id, identity, index
|
238
|
+
end
|
232
239
|
else
|
233
|
-
|
234
|
-
|
235
|
-
redis[key("participant:#{identity}:show")] = index
|
240
|
+
@showing ||= {}
|
241
|
+
@showing[identity] = value.nil? ? nil : @alternatives.index(value)
|
236
242
|
end
|
237
243
|
self
|
238
244
|
end
|
@@ -240,8 +246,12 @@ module Vanity
|
|
240
246
|
# True if this alternative is currently showing (see #chooses).
|
241
247
|
def showing?(alternative)
|
242
248
|
identity = identity()
|
243
|
-
|
244
|
-
|
249
|
+
if @playground.collecting?
|
250
|
+
connection.ab_showing(@id, identity) == alternative.id
|
251
|
+
else
|
252
|
+
@showing ||= {}
|
253
|
+
@showing[identity] == alternative.id
|
254
|
+
end
|
245
255
|
end
|
246
256
|
|
247
257
|
|
@@ -358,12 +368,13 @@ module Vanity
|
|
358
368
|
|
359
369
|
# Alternative chosen when this experiment completed.
|
360
370
|
def outcome
|
361
|
-
|
362
|
-
outcome
|
371
|
+
return unless @playground.collecting?
|
372
|
+
outcome = connection.ab_get_outcome(@id)
|
373
|
+
outcome && _alternatives[outcome]
|
363
374
|
end
|
364
375
|
|
365
376
|
def complete!
|
366
|
-
return unless active?
|
377
|
+
return unless @playground.collecting? && active?
|
367
378
|
super
|
368
379
|
if @outcome_is
|
369
380
|
begin
|
@@ -377,24 +388,20 @@ module Vanity
|
|
377
388
|
outcome = best.id if best
|
378
389
|
end
|
379
390
|
# TODO: logging
|
380
|
-
|
391
|
+
connection.ab_set_outcome @id, outcome || 0
|
381
392
|
end
|
382
393
|
|
383
394
|
|
384
395
|
# -- Store/validate --
|
385
396
|
|
386
397
|
def destroy
|
387
|
-
|
388
|
-
redis.del key("alts:#{i}:participants")
|
389
|
-
redis.del key("alts:#{i}:converted")
|
390
|
-
redis.del key("alts:#{i}:conversions")
|
391
|
-
end
|
392
|
-
redis.del key(:outcome)
|
398
|
+
connection.destroy_experiment @id
|
393
399
|
super
|
394
400
|
end
|
395
401
|
|
396
402
|
def save
|
397
|
-
|
403
|
+
true_false unless @alternatives
|
404
|
+
fail "Experiment #{name} needs at least two alternatives" unless @alternatives.size >= 2
|
398
405
|
super
|
399
406
|
if @metrics.nil? || @metrics.empty?
|
400
407
|
warn "Please use metrics method to explicitly state which metric you are measuring against."
|
@@ -411,10 +418,9 @@ module Vanity
|
|
411
418
|
return unless active?
|
412
419
|
identity = identity() rescue nil
|
413
420
|
if identity
|
414
|
-
return if
|
421
|
+
return if connection.ab_showing(@id, identity)
|
415
422
|
index = alternative_for(identity)
|
416
|
-
|
417
|
-
redis.incrby key("alts:#{index}:conversions"), count
|
423
|
+
connection.ab_add_conversion @id, index, identity, count
|
418
424
|
check_completion!
|
419
425
|
end
|
420
426
|
end
|
@@ -432,13 +438,12 @@ module Vanity
|
|
432
438
|
participants.times do |identity|
|
433
439
|
index = @alternatives.index(value)
|
434
440
|
raise ArgumentError, "No alternative #{value.inspect} for #{name}" unless index
|
435
|
-
|
441
|
+
connection.ab_add_participant @id, index, "#{index}:#{identity}"
|
436
442
|
end
|
437
443
|
conversions.times do |identity|
|
438
444
|
index = @alternatives.index(value)
|
439
445
|
raise ArgumentError, "No alternative #{value.inspect} for #{name}" unless index
|
440
|
-
|
441
|
-
redis.incr key("alts:#{index}:conversions")
|
446
|
+
connection.ab_add_conversion @id, index, "#{index}:#{identity}"
|
442
447
|
end
|
443
448
|
end
|
444
449
|
end
|
@@ -68,7 +68,6 @@ module Vanity
|
|
68
68
|
@playground = playground
|
69
69
|
@id, @name = id.to_sym, name
|
70
70
|
@options = options || {}
|
71
|
-
@namespace = "#{@playground.namespace}:#{@id}"
|
72
71
|
@identify_block = method(:default_identify)
|
73
72
|
end
|
74
73
|
|
@@ -139,35 +138,35 @@ module Vanity
|
|
139
138
|
|
140
139
|
# Force experiment to complete.
|
141
140
|
def complete!
|
142
|
-
redis.setnx key(:completed_at), Time.now.to_i
|
143
|
-
@completed_at = redis[key(:completed_at)]
|
144
141
|
@playground.logger.info "vanity: completed experiment #{id}"
|
142
|
+
return unless @playground.collecting?
|
143
|
+
connection.set_experiment_completed_at @id, Time.now
|
144
|
+
@completed_at = connection.get_experiment_completed_at(@id)
|
145
145
|
end
|
146
146
|
|
147
147
|
# Time stamp when experiment was completed.
|
148
148
|
def completed_at
|
149
|
-
@completed_at ||=
|
150
|
-
@completed_at && Time.at(@completed_at.to_i)
|
149
|
+
@completed_at ||= connection.get_experiment_completed_at(@id)
|
151
150
|
end
|
152
151
|
|
153
152
|
# Returns true if experiment active, false if completed.
|
154
153
|
def active?
|
155
|
-
!
|
154
|
+
!@playground.collecting? || !connection.is_experiment_completed?(@id)
|
156
155
|
end
|
157
156
|
|
158
157
|
# -- Store/validate --
|
159
158
|
|
160
159
|
# Get rid of all experiment data.
|
161
160
|
def destroy
|
162
|
-
|
163
|
-
redis.del key(:completed_at)
|
161
|
+
connection.destroy_experiment @id
|
164
162
|
@created_at = @completed_at = nil
|
165
163
|
end
|
166
164
|
|
167
165
|
# Called by Playground to save the experiment definition.
|
168
166
|
def save
|
169
|
-
|
170
|
-
@
|
167
|
+
return unless @playground.collecting?
|
168
|
+
connection.set_experiment_created_at @id, Time.now
|
169
|
+
@created_at = connection.get_experiment_created_at(@id)
|
171
170
|
end
|
172
171
|
|
173
172
|
protected
|
@@ -199,15 +198,14 @@ module Vanity
|
|
199
198
|
# key => "vanity:experiments:green_button"
|
200
199
|
# key("participants") => "vanity:experiments:green_button:participants"
|
201
200
|
def key(name = nil)
|
202
|
-
|
201
|
+
"#{@id}:#{name}"
|
203
202
|
end
|
204
203
|
|
205
|
-
# Shortcut for Vanity.playground.
|
206
|
-
def
|
207
|
-
@playground.
|
204
|
+
# Shortcut for Vanity.playground.connection
|
205
|
+
def connection
|
206
|
+
@playground.connection
|
208
207
|
end
|
209
208
|
|
210
209
|
end
|
211
210
|
end
|
212
211
|
end
|
213
|
-
|
@@ -193,14 +193,10 @@ if defined?(Rails)
|
|
193
193
|
# Use Rails logger by default.
|
194
194
|
Vanity.playground.logger ||= Rails.logger
|
195
195
|
Vanity.playground.load_path = Rails.root + Vanity.playground.load_path
|
196
|
-
|
197
|
-
if !Vanity.playground.connected? && config_file.exist?
|
198
|
-
config = YAML.load_file(config_file)[Rails.env.to_s]
|
199
|
-
Vanity.playground.redis = config if config
|
200
|
-
end
|
196
|
+
Vanity.playground.collecting = Rails.env.production?
|
201
197
|
|
202
|
-
# Do this at the very end of initialization, allowing
|
203
|
-
#
|
198
|
+
# Do this at the very end of initialization, allowing you to change
|
199
|
+
# connection adapter, turn collection on/off, etc.
|
204
200
|
Rails.configuration.after_initialize do
|
205
201
|
Vanity.playground.load!
|
206
202
|
end
|
@@ -213,9 +209,9 @@ if defined?(PhusionPassenger)
|
|
213
209
|
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
214
210
|
if forked
|
215
211
|
begin
|
216
|
-
Vanity.playground.
|
212
|
+
Vanity.playground.establish_connection if Vanity.playground.collecting?
|
217
213
|
rescue Exception=>ex
|
218
|
-
Rails.logger.error "Error reconnecting
|
214
|
+
Rails.logger.error "Error reconnecting: #{ex.to_s}"
|
219
215
|
end
|
220
216
|
end
|
221
217
|
end
|
@@ -62,15 +62,21 @@ module Vanity
|
|
62
62
|
end
|
63
63
|
|
64
64
|
# This track! method stores nothing, but calls the hooks.
|
65
|
-
def track!(
|
66
|
-
|
67
|
-
call_hooks
|
65
|
+
def track!(args = nil)
|
66
|
+
return unless @playground.collecting?
|
67
|
+
call_hooks *track_args(args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def last_update_at
|
71
|
+
record = @ar_scoped.find(:first, :order=>"#@ar_timestamp DESC", :limit=>1, :select=>@ar_timestamp)
|
72
|
+
record && record.send(@ar_timestamp)
|
68
73
|
end
|
69
74
|
|
70
75
|
# AR model after_create callback notifies all the hooks.
|
71
76
|
def after_create(record)
|
77
|
+
return unless @playground.collecting?
|
72
78
|
count = @ar_column ? (record.send(@ar_column) || 0) : 1
|
73
|
-
call_hooks record.send(@ar_timestamp), count if count > 0 && @ar_scoped.exists?(record)
|
79
|
+
call_hooks record.send(@ar_timestamp), nil, [count] if count > 0 && @ar_scoped.exists?(record)
|
74
80
|
end
|
75
81
|
end
|
76
82
|
end
|
data/lib/vanity/metric/base.rb
CHANGED
@@ -4,9 +4,9 @@ module Vanity
|
|
4
4
|
# can also respond to addition methods (+track!+, +bounds+, etc), these are
|
5
5
|
# optional.
|
6
6
|
#
|
7
|
-
# This class implements a basic metric that tracks data and stores it in
|
8
|
-
#
|
9
|
-
# the methods your metric must and can implement.
|
7
|
+
# This class implements a basic metric that tracks data and stores it in the
|
8
|
+
# database. You can use this as the basis for your metric, or as reference
|
9
|
+
# for the methods your metric must and can implement.
|
10
10
|
#
|
11
11
|
# @since 1.1.0
|
12
12
|
class Metric
|
@@ -121,23 +121,42 @@ module Vanity
|
|
121
121
|
@playground, @name = playground, name.to_s
|
122
122
|
@id = (id || name.to_s.downcase.gsub(/\W+/, '_')).to_sym
|
123
123
|
@hooks = []
|
124
|
-
redis.setnx key(:created_at), Time.now.to_i
|
125
|
-
@created_at = Time.at(redis[key(:created_at)].to_i)
|
126
124
|
end
|
127
125
|
|
128
126
|
|
129
127
|
# -- Tracking --
|
130
128
|
|
131
|
-
# Called to track an action associated with this metric.
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
129
|
+
# Called to track an action associated with this metric. Most common is not
|
130
|
+
# passing an argument, and it tracks a count of 1. You can pass a different
|
131
|
+
# value as the argument, or array of value (for multi-series metrics), or
|
132
|
+
# hash with the optional keys timestamp, identity and values.
|
133
|
+
#
|
134
|
+
# Example:
|
135
|
+
# hits.track!
|
136
|
+
# foo_and_bar.track! [5,11]
|
137
|
+
def track!(args = nil)
|
138
|
+
return unless @playground.collecting?
|
139
|
+
timestamp, identity, values = track_args(args)
|
140
|
+
connection.metric_track @id, timestamp, identity, values
|
141
|
+
@playground.logger.info "vanity: #{@id} with value #{values.join(", ")}"
|
142
|
+
call_hooks timestamp, identity, values
|
143
|
+
end
|
144
|
+
|
145
|
+
# Parses arguments to track! method and return array with timestamp,
|
146
|
+
# identity and array of values.
|
147
|
+
def track_args(args)
|
148
|
+
case args
|
149
|
+
when Hash
|
150
|
+
timestamp, identity, values = args.values_at(:timestamp, :identity, :values)
|
151
|
+
when Array
|
152
|
+
values = args
|
153
|
+
when Numeric
|
154
|
+
values = [args]
|
139
155
|
end
|
156
|
+
identity = Vanity.context.vanity_identity rescue nil
|
157
|
+
[timestamp || Time.now, identity, values || [1]]
|
140
158
|
end
|
159
|
+
protected :track_args
|
141
160
|
|
142
161
|
# Metric definitions use this to introduce tracking hook. The hook is
|
143
162
|
# called with metric identifier, timestamp, count and possibly additional
|
@@ -172,9 +191,6 @@ module Vanity
|
|
172
191
|
attr_reader :name
|
173
192
|
alias :to_s :name
|
174
193
|
|
175
|
-
# Time stamp when metric was created.
|
176
|
-
attr_reader :created_at
|
177
|
-
|
178
194
|
# Human readable description. Use two newlines to break paragraphs.
|
179
195
|
attr_accessor :description
|
180
196
|
|
@@ -192,28 +208,35 @@ module Vanity
|
|
192
208
|
# Given two arguments, a start date and an end date (inclusive), returns an
|
193
209
|
# array of measurements. All metrics must implement this method.
|
194
210
|
def values(from, to)
|
195
|
-
|
211
|
+
values = connection.metric_values(@id, from, to)
|
212
|
+
values.map { |row| row.first.to_i }
|
213
|
+
end
|
214
|
+
|
215
|
+
# Returns date/time of the last update to this metric.
|
216
|
+
#
|
217
|
+
# @since 1.4.0
|
218
|
+
def last_update_at
|
219
|
+
connection.get_metric_last_update_at(@id)
|
196
220
|
end
|
197
221
|
|
198
222
|
|
199
223
|
# -- Storage --
|
200
224
|
|
201
225
|
def destroy!
|
202
|
-
|
226
|
+
connection.destroy_metric @id
|
203
227
|
end
|
204
228
|
|
205
|
-
def
|
206
|
-
@playground.
|
229
|
+
def connection
|
230
|
+
@playground.connection
|
207
231
|
end
|
208
232
|
|
209
233
|
def key(*args)
|
210
234
|
"metrics:#{@id}:#{args.join(':')}"
|
211
235
|
end
|
212
236
|
|
213
|
-
def call_hooks(timestamp,
|
214
|
-
count ||= 1
|
237
|
+
def call_hooks(timestamp, identity, values)
|
215
238
|
@hooks.each do |hook|
|
216
|
-
hook.call @id, timestamp,
|
239
|
+
hook.call @id, timestamp, values.first || 1
|
217
240
|
end
|
218
241
|
end
|
219
242
|
|