Class: Vanity::Metric
- 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.
Defined Under Namespace
Modules: ActiveRecord, Definition, GoogleAnalytics, Remote
Constant Summary collapse
- AGGREGATES =
[:average, :minimum, :maximum, :sum]
Instance Attribute Summary collapse
-
#description(text = nil) ⇒ Object
Sets or returns description.
-
#name ⇒ Object
(also: #to_s)
readonly
Human readable metric name.
Class Method Summary collapse
-
.bounds(metric) ⇒ Object
Helper method to return bounds for a metric.
-
.data(metric, *args) ⇒ Object
Returns data set for a given date range.
-
.description(metric) ⇒ Object
Helper method to return description for a metric.
-
.load(playground, stack, file) ⇒ Object
Playground uses this to load metric definitions.
Instance Method Summary collapse
-
#bounds ⇒ Object
This method returns the acceptable bounds of a metric as an array with two values: low and high.
- #call_hooks(timestamp, identity, values) ⇒ Object
- #connection ⇒ Object
-
#destroy! ⇒ Object
– Storage –.
-
#google_analytics(web_property_id, *args) ⇒ Object
Use Google Analytics metric.
-
#hook(&block) ⇒ Object
Metric definitions use this to introduce tracking hook.
-
#initialize(playground, name, id = nil) ⇒ Metric
constructor
Takes playground (need this to access Redis), friendly name and optional id (can infer from name).
- #key(*args) ⇒ Object
-
#last_update_at ⇒ Object
Returns date/time of the last update to this metric.
-
#model(class_or_scope, options = nil) ⇒ Object
Use an ActiveRecord model to get metric data from database table.
-
#remote(url = nil) ⇒ Object
Specifies the base URL to use for a remote metric.
-
#track!(args = nil) ⇒ Object
Called to track an action associated with this metric.
-
#values(from, to) ⇒ Object
Given two arguments, a start date and an end date (inclusive), returns an array of measurements.
Constructor Details
#initialize(playground, name, id = nil) ⇒ Metric
Takes playground (need this to access Redis), friendly name and optional id (can infer from name).
120 121 122 123 124 |
# File 'lib/vanity/metric/base.rb', line 120 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
#description(text = nil) ⇒ Object
Sets or returns description. For example
metric "Yawns/sec" do
description "Most boring metric ever"
end
puts "Just defined: " + metric(:boring).description
195 196 197 |
# File 'lib/vanity/metric/base.rb', line 195 def description @description end |
#name ⇒ Object (readonly) Also known as: to_s
Human readable metric name. All metrics must implement this method.
191 192 193 |
# File 'lib/vanity/metric/base.rb', line 191 def name @name end |
Class Method Details
.bounds(metric) ⇒ Object
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].
71 72 73 |
# File 'lib/vanity/metric/base.rb', line 71 def bounds(metric) metric.respond_to?(:bounds) && metric.bounds || [nil, nil] end |
.data(metric, *args) ⇒ Object
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.
87 88 89 90 91 92 |
# File 'lib/vanity/metric/base.rb', line 87 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 |
.description(metric) ⇒ Object
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
.
59 60 61 |
# File 'lib/vanity/metric/base.rb', line 59 def description(metric) metric.description if metric.respond_to?(:description) end |
.load(playground, stack, file) ⇒ Object
Playground uses this to load metric definitions.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/vanity/metric/base.rb', line 95 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($!., id) error.set_backtrace $!.backtrace raise error ensure stack.pop end |
Instance Method Details
#bounds ⇒ Object
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
.
184 185 |
# File 'lib/vanity/metric/base.rb', line 184 def bounds end |
#call_hooks(timestamp, identity, values) ⇒ Object
237 238 239 240 241 |
# File 'lib/vanity/metric/base.rb', line 237 def call_hooks(, identity, values) @hooks.each do |hook| hook.call @id, , values.first || 1 end end |
#connection ⇒ Object
229 230 231 |
# File 'lib/vanity/metric/base.rb', line 229 def connection @playground.connection end |
#destroy! ⇒ Object
– Storage –
225 226 227 |
# File 'lib/vanity/metric/base.rb', line 225 def destroy! connection.destroy_metric @id end |
#google_analytics(web_property_id, *args) ⇒ Object
Use Google Analytics metric. Note: you must require “garb” before vanity.
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" = Hash === args.last ? args.pop : {} metric = args.shift || :pageviews @ga_resource = Vanity::Metric::GoogleAnalytics::Resource.new(web_property_id, metric) @ga_mapper = [: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 |
#hook(&block) ⇒ Object
Metric definitions use this to introduce tracking hook. The hook is called with metric identifier, timestamp, count and possibly additional arguments.
For example:
hook do |metric_id, , count|
syslog.info metric_id
end
169 170 171 |
# File 'lib/vanity/metric/base.rb', line 169 def hook(&block) @hooks << block end |
#key(*args) ⇒ Object
233 234 235 |
# File 'lib/vanity/metric/base.rb', line 233 def key(*args) "metrics:#{@id}:#{args.join(':')}" end |
#last_update_at ⇒ Object
Returns date/time of the last update to this metric.
218 219 220 |
# File 'lib/vanity/metric/base.rb', line 218 def last_update_at connection.get_metric_last_update_at(@id) end |
#model(class_or_scope, options = nil) ⇒ Object
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
)
37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/vanity/metric/active_record.rb', line 37 def model(class_or_scope, = nil) = || {} conditions = .delete(:conditions) @ar_scoped = conditions ? class_or_scope.scoped(:conditions=>conditions) : class_or_scope @ar_aggregate = AGGREGATES.find { |key| .has_key?(key) } @ar_column = .delete(@ar_aggregate) fail "Cannot use multiple aggregates in a single metric" if AGGREGATES.find { |key| .has_key?(key) } @ar_timestamp = .delete(:timestamp) || :created_at fail "Unrecognized options: #{.keys * ", "}" unless .empty? @ar_scoped.after_create self extend ActiveRecord end |
#remote(url = nil) ⇒ Object
Specifies the base URL to use for a remote metric. For example:
metric :sandbox do
remote "http://api.vanitydash.com/metrics/sandbox"
end
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 |
#track!(args = nil) ⇒ Object
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!
.track! [5,11]
137 138 139 140 141 142 143 |
# File 'lib/vanity/metric/base.rb', line 137 def track!(args = nil) return unless @playground.collecting? , identity, values = track_args(args) connection.metric_track @id, , identity, values @playground.logger.info "vanity: #{@id} with value #{values.join(", ")}" call_hooks , identity, values end |
#values(from, to) ⇒ Object
Given two arguments, a start date and an end date (inclusive), returns an array of measurements. All metrics must implement this method.
210 211 212 213 |
# File 'lib/vanity/metric/base.rb', line 210 def values(from, to) values = connection.metric_values(@id, from, to) values.map { |row| row.first.to_i } end |