Class: Vanity::Metric

Inherits:
Object show all
Defined in:
lib/vanity/metric/base.rb,
lib/vanity/metric/remote.rb,
lib/vanity/metric/active_record.rb,
lib/vanity/metric/google_analytics.rb

Overview

A metric is an object that implements two methods: name and values. It can also respond to addition methods (track!, bounds, etc), these are optional.

This class implements a basic metric that tracks data and stores it in the database. You can use this as the basis for your metric, or as reference for the methods your metric must and can implement.

Since:

Defined Under Namespace

Modules: ActiveRecord, Definition, GoogleAnalytics, Remote

Constant Summary

AGGREGATES =

Since:

  • 1.1.0

[:average, :minimum, :maximum, :sum]

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (Metric) initialize(playground, name, id = nil)

Takes playground (need this to access Redis), friendly name and optional id (can infer from name).

Since:

  • 1.1.0



119
120
121
122
123
# File 'lib/vanity/metric/base.rb', line 119

def initialize(playground, name, id = nil)
  @playground, @name = playground, name.to_s
  @id = (id || name.to_s.downcase.gsub(/\W+/, '_')).to_sym
  @hooks = []
end

Instance Attribute Details

- (Object) description(text = nil)

Sets or returns description. For example

metric "Yawns/sec" do
  description "Most boring metric ever"
end

puts "Just defined: " + metric(:boring).description

Since:

  • 1.1.0



194
195
196
# File 'lib/vanity/metric/base.rb', line 194

def description
  @description
end

- (Object) name (readonly) Also known as: to_s

Human readable metric name. All metrics must implement this method.

Since:

  • 1.1.0



190
191
192
# File 'lib/vanity/metric/base.rb', line 190

def name
  @name
end

Class Method Details

+ (Object) bounds(metric)

Helper method to return bounds for a metric.

A metric object may have a bounds method that returns lower and upper bounds. It may also have no bounds, or no bounds # method, in which case we return [nil, nil].

Examples:

upper = Vanity::Metric.bounds(metric).last

Since:

  • 1.1.0



70
71
72
# File 'lib/vanity/metric/base.rb', line 70

def bounds(metric)
  metric.respond_to?(:bounds) && metric.bounds || [nil, nil]
end

+ (Object) data(metric, *args)

Returns data set for a given date range. The data set is an array of date, value pairs.

First argument is the metric. Second argument is the start date, or number of days to go back in history, defaults to 90 days. Third argument is end date, defaults to today.

Examples:

These are all equivalent:

Vanity::Metric.data(my_metric)
Vanity::Metric.data(my_metric, 90)
Vanity::Metric.data(my_metric, Date.today - 89)
Vanity::Metric.data(my_metric, Date.today - 89, Date.today)

Since:

  • 1.1.0



86
87
88
89
90
91
# File 'lib/vanity/metric/base.rb', line 86

def data(metric, *args)
  first = args.shift || 90
  to = args.shift || Date.today
  from = first.respond_to?(:to_date) ? first.to_date : to - (first - 1)
  (from..to).zip(metric.values(from, to))
end

+ (Object) description(metric)

Helper method to return description for a metric.

A metric object may have a description method that returns a detailed description. It may also have no description, or no description method, in which case return nil.

Examples:

puts Vanity::Metric.description(metric)

Since:

  • 1.1.0



58
59
60
# File 'lib/vanity/metric/base.rb', line 58

def description(metric)
  metric.description if metric.respond_to?(:description)
end

+ (Object) load(playground, stack, file)

Playground uses this to load metric definitions.

Since:

  • 1.1.0



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/vanity/metric/base.rb', line 94

def load(playground, stack, file)
  fail "Circular dependency detected: #{stack.join('=>')}=>#{file}" if stack.include?(file)
  source = File.read(file)
  stack.push file
  id = File.basename(file, ".rb").downcase.gsub(/\W/, "_").to_sym
  context = Object.new
  context.instance_eval do
    extend Definition
    metric = eval(source, context.new_binding(playground, id), file)
    fail NameError.new("Expected #{file} to define metric #{id}", id) unless playground.metrics[id]
    metric
  end
rescue
  error = NameError.exception($!.message, id)
  error.set_backtrace $!.backtrace
  raise error
ensure
  stack.pop
end

Instance Method Details

- (Object) bounds

This method returns the acceptable bounds of a metric as an array with two values: low and high. Use nil for unbounded.

Alerts are created when metric values exceed their bounds. For example, a metric of user registration can use historical data to calculate expected range of new registration for the next day. If actual metric falls below the expected range, it could indicate registration process is broken. Going above higher bound could trigger opening a Champagne bottle.

The default implementation returns nil.

Since:

  • 1.1.0



183
184
# File 'lib/vanity/metric/base.rb', line 183

def bounds
end

- (Object) call_hooks(timestamp, identity, values)

Since:

  • 1.1.0



236
237
238
239
240
# File 'lib/vanity/metric/base.rb', line 236

def call_hooks(timestamp, identity, values)
  @hooks.each do |hook|
    hook.call @id, timestamp, values.first || 1, :identity=>identity
  end
end

- (Object) connection

Since:

  • 1.1.0



228
229
230
# File 'lib/vanity/metric/base.rb', line 228

def connection
  @playground.connection
end

- (Object) destroy!

– Storage –

Since:

  • 1.1.0



224
225
226
# File 'lib/vanity/metric/base.rb', line 224

def destroy!
  connection.destroy_metric @id
end

- (Object) google_analytics(web_property_id, *args)

Use Google Analytics metric. Note: you must require “garb” before vanity.

Examples:

Page views

metric "Page views" do
  google_analytics "UA-1828623-6"
end

Visits

metric "Visits" do
  google_analytics "UA-1828623-6", :visits
end

See Also:

Since:

  • 1.3.0



18
19
20
21
22
23
24
25
26
27
# File 'lib/vanity/metric/google_analytics.rb', line 18

def google_analytics(web_property_id, *args)
  require "garb"
  options = Hash === args.last ? args.pop : {}
  metric = args.shift || :pageviews
  @ga_resource = Vanity::Metric::GoogleAnalytics::Resource.new(web_property_id, metric)
  @ga_mapper = options[:mapper] ||= lambda { |entry| entry.send(@ga_resource.metrics.elements.first).to_i }
  extend GoogleAnalytics
rescue LoadError
  fail LoadError, "Google Analytics metrics require Garb, please gem install garb first"
end

- (Object) hook(&block)

Metric definitions use this to introduce tracking hooks. The hook is called with metric identifier, timestamp, count and possibly additional arguments.

For example:

hook do |metric_id, timestamp, count|
  syslog.info metric_id
end

Since:

  • 1.1.0



168
169
170
# File 'lib/vanity/metric/base.rb', line 168

def hook(&block)
  @hooks << block
end

- (Object) key(*args)

Since:

  • 1.1.0



232
233
234
# File 'lib/vanity/metric/base.rb', line 232

def key(*args)
  "metrics:#{@id}:#{args.join(':')}"
end

- (Object) last_update_at

Returns date/time of the last update to this metric.

Since:

  • 1.4.0



217
218
219
# File 'lib/vanity/metric/base.rb', line 217

def last_update_at
  connection.get_metric_last_update_at(@id)
end

- (Object) model(class_or_scope, options = nil)

Use an ActiveRecord model to get metric data from database table. Also forwards after_create callbacks to hooks (updating experiments).

Supported options: :conditions – Only select records that match this condition :average – Metric value is average of this column :minimum – Metric value is minimum of this column :maximum – Metric value is maximum of this column :sum – Metric value is sum of this column :timestamp – Use this column to filter/group records (defaults to created_at)

Examples:

Track sign ups using User model

metric "Signups" do
  model Account
end

Track satisfaction using Survey model

metric "Satisfaction" do
  model Survey, :average=>:rating
end

Track only high ratings

metric "High ratings" do
  model Rating, :conditions=>["stars >= 4"]
end

Track only high ratings (using scope)

metric "High ratings" do
  model Rating.high
end

See Also:

Since:

  • 1.2.0



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/vanity/metric/active_record.rb', line 37

def model(class_or_scope, options = nil)
  options = options || {}
  conditions = options.delete(:conditions)
  @ar_scoped = conditions ? class_or_scope.scoped(:conditions=>conditions) : class_or_scope
  @ar_aggregate = AGGREGATES.find { |key| options.has_key?(key) }
  @ar_column = options.delete(@ar_aggregate)
  fail "Cannot use multiple aggregates in a single metric" if AGGREGATES.find { |key| options.has_key?(key) }
  @ar_timestamp = options.delete(:timestamp) || :created_at
  @ar_timestamp, @ar_timestamp_table = @ar_timestamp.to_s.split('.').reverse
  @ar_timestamp_table ||= @ar_scoped.table_name
  fail "Unrecognized options: #{options.keys * ", "}" unless options.empty?
  @ar_scoped.after_create self
  extend ActiveRecord
end

- (Object) remote(url = nil)

Specifies the base URL to use for a remote metric. For example:

metric :sandbox do
  remote "http://api.vanitydash.com/metrics/sandbox"
end

Since:

  • 1.1.0



11
12
13
14
15
16
# File 'lib/vanity/metric/remote.rb', line 11

def remote(url = nil)
  @remote_url = URI.parse(url) if url
  @mutex ||= Mutex.new
  extend Remote
  @remote_url
end

- (Object) track!(args = nil)

Called to track an action associated with this metric. Most common is not passing an argument, and it tracks a count of 1. You can pass a different value as the argument, or array of value (for multi-series metrics), or hash with the optional keys timestamp, identity and values.

Example:

hits.track!
foo_and_bar.track! [5,11]

Since:

  • 1.1.0



136
137
138
139
140
141
142
# File 'lib/vanity/metric/base.rb', line 136

def track!(args = nil)
  return unless @playground.collecting?
  timestamp, identity, values = track_args(args)
  connection.metric_track @id, timestamp, identity, values
  @playground.logger.info "vanity: #{@id} with value #{values.join(", ")}"
  call_hooks timestamp, identity, values
end

- (Object) values(from, to)

Given two arguments, a start date and an end date (inclusive), returns an array of measurements. All metrics must implement this method.

Since:

  • 1.1.0



209
210
211
212
# File 'lib/vanity/metric/base.rb', line 209

def values(from, to)
  values = connection.metric_values(@id, from, to)
  values.map { |row| row.first.to_i }
end