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.
Files changed (99) hide show
  1. data/CHANGELOG +61 -3
  2. data/Gemfile +22 -14
  3. data/README.rdoc +9 -4
  4. data/Rakefile +72 -12
  5. data/bin/vanity +16 -4
  6. data/lib/vanity.rb +7 -5
  7. data/lib/vanity/adapters/abstract_adapter.rb +135 -0
  8. data/lib/vanity/adapters/mock_adapter.rb +157 -0
  9. data/lib/vanity/adapters/mongo_adapter.rb +162 -0
  10. data/lib/vanity/adapters/redis_adapter.rb +154 -0
  11. data/lib/vanity/backport.rb +0 -17
  12. data/lib/vanity/commands/upgrade.rb +34 -0
  13. data/lib/vanity/experiment/ab_test.rb +46 -41
  14. data/lib/vanity/experiment/base.rb +13 -15
  15. data/lib/vanity/frameworks/rails.rb +5 -9
  16. data/lib/vanity/metric/active_record.rb +10 -4
  17. data/lib/vanity/metric/base.rb +46 -23
  18. data/lib/vanity/metric/google_analytics.rb +7 -0
  19. data/lib/vanity/metric/remote.rb +53 -0
  20. data/lib/vanity/playground.rb +133 -49
  21. data/test/{ab_test_test.rb → experiment/ab_test.rb} +47 -3
  22. data/test/{experiment_test.rb → experiment/base_test.rb} +8 -8
  23. data/test/metric/active_record_test.rb +253 -0
  24. data/test/metric/base_test.rb +293 -0
  25. data/test/metric/google_analytics_test.rb +104 -0
  26. data/test/metric/remote_test.rb +108 -0
  27. data/test/myapp/app/controllers/application_controller.rbc +66 -0
  28. data/test/myapp/app/controllers/main_controller.rb +3 -3
  29. data/test/myapp/app/controllers/main_controller.rbc +347 -0
  30. data/test/myapp/config/boot.rbc +2534 -0
  31. data/test/myapp/config/environment.rbc +403 -0
  32. data/test/myapp/config/routes.rbc +174 -0
  33. data/test/myapp/log/production.log +2601 -0
  34. data/test/passenger_test.rb +14 -5
  35. data/test/passenger_test.rbc +0 -0
  36. data/test/playground_test.rbc +256 -0
  37. data/test/rails_test.rb +75 -22
  38. data/test/rails_test.rbc +4086 -0
  39. data/test/test_helper.rb +30 -7
  40. data/test/test_helper.rbc +4297 -0
  41. data/vanity.gemspec +6 -2
  42. metadata +74 -73
  43. data/lib/vanity/commands.rb +0 -2
  44. data/lib/vanity/mock_redis.rb +0 -76
  45. data/test/metric_test.rb +0 -622
  46. data/vendor/cache/RedCloth-4.2.2.gem +0 -0
  47. data/vendor/cache/actionmailer-2.3.5.gem +0 -0
  48. data/vendor/cache/actionpack-2.3.5.gem +0 -0
  49. data/vendor/cache/activerecord-2.3.5.gem +0 -0
  50. data/vendor/cache/activeresource-2.3.5.gem +0 -0
  51. data/vendor/cache/activesupport-2.3.5.gem +0 -0
  52. data/vendor/cache/autotest-4.2.7.gem +0 -0
  53. data/vendor/cache/autotest-fsevent-0.2.1.gem +0 -0
  54. data/vendor/cache/autotest-growl-0.2.0.gem +0 -0
  55. data/vendor/cache/bundler-0.9.7.gem +0 -0
  56. data/vendor/cache/classifier-1.3.1.gem +0 -0
  57. data/vendor/cache/directory_watcher-1.3.1.gem +0 -0
  58. data/vendor/cache/fastthread-1.0.7.gem +0 -0
  59. data/vendor/cache/garb-0.7.0.gem +0 -0
  60. data/vendor/cache/happymapper-0.3.0.gem +0 -0
  61. data/vendor/cache/jekyll-0.5.7.gem +0 -0
  62. data/vendor/cache/libxml-ruby-1.1.3.gem +0 -0
  63. data/vendor/cache/liquid-2.0.0.gem +0 -0
  64. data/vendor/cache/maruku-0.6.0.gem +0 -0
  65. data/vendor/cache/mocha-0.9.8.gem +0 -0
  66. data/vendor/cache/open4-1.0.1.gem +0 -0
  67. data/vendor/cache/passenger-2.2.9.gem +0 -0
  68. data/vendor/cache/rack-1.0.1.gem +0 -0
  69. data/vendor/cache/rails-2.3.5.gem +0 -0
  70. data/vendor/cache/rake-0.8.7.gem +0 -0
  71. data/vendor/cache/rubygems-update-1.3.5.gem +0 -0
  72. data/vendor/cache/shoulda-2.10.3.gem +0 -0
  73. data/vendor/cache/sqlite3-ruby-1.2.5.gem +0 -0
  74. data/vendor/cache/stemmer-1.0.1.gem +0 -0
  75. data/vendor/cache/syntax-1.0.0.gem +0 -0
  76. data/vendor/cache/sys-uname-0.8.4.gem +0 -0
  77. data/vendor/cache/timecop-0.3.4.gem +0 -0
  78. data/vendor/redis-rb/LICENSE +0 -20
  79. data/vendor/redis-rb/README.markdown +0 -36
  80. data/vendor/redis-rb/Rakefile +0 -62
  81. data/vendor/redis-rb/bench.rb +0 -44
  82. data/vendor/redis-rb/benchmarking/suite.rb +0 -24
  83. data/vendor/redis-rb/benchmarking/worker.rb +0 -71
  84. data/vendor/redis-rb/bin/distredis +0 -33
  85. data/vendor/redis-rb/examples/basic.rb +0 -16
  86. data/vendor/redis-rb/examples/incr-decr.rb +0 -18
  87. data/vendor/redis-rb/examples/list.rb +0 -26
  88. data/vendor/redis-rb/examples/sets.rb +0 -36
  89. data/vendor/redis-rb/lib/dist_redis.rb +0 -124
  90. data/vendor/redis-rb/lib/hash_ring.rb +0 -128
  91. data/vendor/redis-rb/lib/pipeline.rb +0 -21
  92. data/vendor/redis-rb/lib/redis.rb +0 -370
  93. data/vendor/redis-rb/lib/redis/raketasks.rb +0 -1
  94. data/vendor/redis-rb/profile.rb +0 -22
  95. data/vendor/redis-rb/redis-rb.gemspec +0 -30
  96. data/vendor/redis-rb/spec/redis_spec.rb +0 -637
  97. data/vendor/redis-rb/spec/spec_helper.rb +0 -4
  98. data/vendor/redis-rb/speed.rb +0 -16
  99. data/vendor/redis-rb/tasks/redis.tasks.rb +0 -140
@@ -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
- unless args.empty?
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
- alternatives
136
+ nil
140
137
  end
141
138
 
142
139
  def _alternatives
143
140
  alts = []
144
141
  @alternatives.each_with_index do |value, i|
145
- participants = redis.scard(key("alts:#{i}:participants")).to_i
146
- converted = redis.scard(key("alts:#{i}:converted")).to_i
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 active?
190
- identity = identity()
191
- index = redis[key("participant:#{identity}:show")]
192
- unless index
193
- index = alternative_for(identity)
194
- redis.sadd key("alts:#{index}:participants"), identity
195
- check_completion!
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
- index = redis[key("outcome")] || alternative_for(identity)
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 value.nil?
231
- redis.del key("participant:#{identity}:show")
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
- index = @alternatives.index(value)
234
- raise ArgumentError, "No alternative #{value.inspect} for #{name}" unless index
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
- index = redis[key("participant:#{identity}:show")]
244
- index && index.to_i == alternative.id
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
- outcome = redis[key("outcome")]
362
- outcome && alternatives[outcome.to_i]
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
- redis.setnx key("outcome"), outcome || 0
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
- @alternatives.size.times do |i|
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
- fail "Experiment #{name} needs at least two alternatives" unless alternatives.size >= 2
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 redis[key("participants:#{identity}:show")]
421
+ return if connection.ab_showing(@id, identity)
415
422
  index = alternative_for(identity)
416
- redis.sadd key("alts:#{index}:converted"), identity if redis.sismember(key("alts:#{index}:participants"), identity)
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
- redis.sadd key("alts:#{index}:participants"), identity
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
- redis.sadd key("alts:#{index}:converted"), identity
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 ||= redis[key(: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
- !redis.exists(key(:completed_at))
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
- redis.del key(:created_at)
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
- redis.setnx key(:created_at), Time.now.to_i
170
- @created_at = Time.at(redis[key(:created_at)].to_i)
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
- name ? "#{@namespace}:#{name}" : @namespace
201
+ "#{@id}:#{name}"
203
202
  end
204
203
 
205
- # Shortcut for Vanity.playground.redis
206
- def redis
207
- @playground.redis
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
- config_file = Rails.root + "config/redis.yml"
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 test environment to do
203
- # Vanity.playground.mock! before any database access takes place.
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.reconnect!
212
+ Vanity.playground.establish_connection if Vanity.playground.collecting?
217
213
  rescue Exception=>ex
218
- Rails.logger.error "Error reconnecting Redis: #{ex.to_s}"
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!(*args)
66
- count = args.first || 1
67
- call_hooks Time.now, count if count > 0
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
@@ -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
- # Redis. You can use this as the basis for your metric, or as reference for
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
- def track!(count = 1)
133
- count ||= 1
134
- if count > 0
135
- timestamp = Time.now
136
- redis.incrby key(timestamp.to_date, "count"), count
137
- @playground.logger.info "vanity: #{@id} with count #{count}"
138
- call_hooks timestamp, count
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
- redis.mget((from.to_date..to.to_date).map { |date| key(date, "count") }).map(&:to_i)
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
- redis.del redis.keys(key("*"))
226
+ connection.destroy_metric @id
203
227
  end
204
228
 
205
- def redis
206
- @playground.redis
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, count)
214
- count ||= 1
237
+ def call_hooks(timestamp, identity, values)
215
238
  @hooks.each do |hook|
216
- hook.call @id, timestamp, count
239
+ hook.call @id, timestamp, values.first || 1
217
240
  end
218
241
  end
219
242