Class: Vanity::Metric
- Inherits:
-
Object
- Object
- 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 hooks.
-
#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).
116 117 118 119 120 121 |
# File 'lib/vanity/metric/base.rb', line 116 def initialize(playground, name, id = nil) @playground = playground @name = 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
203 204 205 206 |
# File 'lib/vanity/metric/base.rb', line 203 def description(text = nil) @description = text if text @description if defined?(@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].
67 68 69 |
# File 'lib/vanity/metric/base.rb', line 67 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.
83 84 85 86 87 88 |
# File 'lib/vanity/metric/base.rb', line 83 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
.
55 56 57 |
# File 'lib/vanity/metric/base.rb', line 55 def description(metric) metric.description if metric.respond_to?(:description) end |
.load(playground, stack, file) ⇒ Object
Playground uses this to load metric definitions.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/vanity/metric/base.rb', line 91 def load(playground, stack, file) raise "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) # rubocop:todo Security/Eval raise NameError.new("Expected #{file} to define metric #{id}", id) unless playground.metrics[id] metric end rescue StandardError 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
.
185 186 |
# File 'lib/vanity/metric/base.rb', line 185 def bounds end |
#call_hooks(timestamp, identity, values) ⇒ Object
236 237 238 239 240 |
# File 'lib/vanity/metric/base.rb', line 236 def call_hooks(, identity, values) @hooks.each do |hook| hook.call @id, , values.first || 1, identity: identity end end |
#connection ⇒ Object
228 229 230 |
# File 'lib/vanity/metric/base.rb', line 228 def connection @playground.connection end |
#destroy! ⇒ Object
– Storage –
224 225 226 |
# File 'lib/vanity/metric/base.rb', line 224 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.
17 18 19 20 21 22 23 24 25 26 |
# File 'lib/vanity/metric/google_analytics.rb', line 17 def google_analytics(web_property_id, *args) require "garb" = args.last.is_a?(Hash) ? args.pop : {} metric = args.shift || :pageviews @ga_resource = Vanity::Metric::GoogleAnalytics::Resource.new(web_property_id, metric) @ga_mapper = [:mapper] ||= ->(entry) { entry.send(@ga_resource.metrics.elements.first).to_i } extend GoogleAnalytics rescue LoadError raise LoadError, "Google Analytics metrics require Garb, please gem install garb first" end |
#hook(&block) ⇒ Object
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, , count|
syslog.info metric_id
end
170 171 172 |
# File 'lib/vanity/metric/base.rb', line 170 def hook(&block) @hooks << block end |
#key(*args) ⇒ Object
232 233 234 |
# File 'lib/vanity/metric/base.rb', line 232 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
)
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/vanity/metric/active_record.rb', line 36 def model(class_or_scope, = nil) ActiveSupport.on_load(:active_record, yield: true) do class_or_scope = class_or_scope.constantize if class_or_scope.is_a?(String) ||= {} conditions = .delete(:conditions) @ar_scoped = conditions ? class_or_scope.where(conditions) : class_or_scope @ar_aggregate = AGGREGATES.find { |key| .has_key?(key) } @ar_column = .delete(@ar_aggregate) raise "Cannot use multiple aggregates in a single metric" if AGGREGATES.find { |key| .has_key?(key) } @ar_timestamp = .delete(:timestamp) || :created_at @ar_timestamp, @ar_timestamp_table = @ar_timestamp.to_s.split('.').reverse @ar_timestamp_table ||= @ar_scoped.table_name @ar_identity_block = .delete(:identity) raise "Unrecognized options: #{.keys * ', '}" unless .empty? @ar_scoped.after_create(self) extend ActiveRecord end 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
10 11 12 13 14 15 |
# File 'lib/vanity/metric/remote.rb', line 10 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]
133 134 135 136 137 138 139 140 |
# File 'lib/vanity/metric/base.rb', line 133 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 |