Module: DulyNoted
- Extended by:
- DulyNoted
- Includes:
- Configuration, Helpers, Updater
- Included in:
- DulyNoted
- Defined in:
- lib/duly_noted.rb,
lib/duly_noted/helpers.rb,
lib/duly_noted/updater.rb,
lib/duly_noted/version.rb,
lib/duly_noted/configuration.rb
Overview
-
‘track`
-
‘update`
-
‘query`
-
‘count`
-
‘chart`
Defined Under Namespace
Modules: Configuration, Helpers, Updater Classes: InvalidId, InvalidOptions, InvalidStep, NotValidMetric, UpdateError
Constant Summary collapse
- VERSION =
"1.0.2"
Instance Method Summary collapse
-
#chart(metric_name, options = {}) ⇒ Object
##Chart.
- #configure {|DulyNoted::Configuration| ... } ⇒ Object
-
#count(metric_name, options = {}) ⇒ Object
##Count.
-
#count_x_by_y(metric_name, meta_field, options) ⇒ Object
##Magic.
-
#method_missing(method, *args, &block) ⇒ Object
##Behind the curtain (metaprogramming).
-
#query(metric_name, options = {}) ⇒ Object
##Query.
- #redis ⇒ Object
-
#redis=(url) ⇒ Object
##Redis.
-
#track(metric_name, options = {}) ⇒ Object
DulyNoted.track(“page_views”, for: “home”, meta: “chrome”).
-
#update(id, options = {}) ⇒ Object
##Update.
Methods included from Configuration
Methods included from Updater
Methods included from Helpers
#assemble_for, #build_key, #fields_for, #find_keys, #metrics, #normalize, #parse_time_range, #valid_metric?
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
##Behind the curtain (metaprogramming)
###method_missing
As I’m sure you’re aware, method_missing is the magic tool for ruby developers to define dynamic methods like the above ‘count_x_by_y`, which is exactly what we use it for.
343 344 345 346 347 348 349 |
# File 'lib/duly_noted.rb', line 343 def method_missing(method, *args, &block) if method.to_s =~ /^count_(.+)_by_(.+)$/ count_x_by_y($1, $2, args[0]) else super end end |
Instance Method Details
#chart(metric_name, options = {}) ⇒ Object
##Chart
_parameters: ‘metric_name`, `data_points`(required),`for`(optional), `time_start`(optional), `time_end`(optional), `time_range`(optional)_
Chart is a little complex, but I’ll try to explain all of the possibilities. It’s main purpose is to pull out your data and prepare it in a way that makes it easy to chart. The smallest amount of input it will take is just a ‘metric_name` and an amount of `data_points` to capture. This will check the time of the earliest known data point, and the time of the last known data point, and run chart with those values as the `time_start` and `time_end` respectively. It will take the amount of time that that spans, and divide it by the number of data points you asked for, and will split the time up evenly, and return a hash of times, and counts. If you specify both a `time_start` and a `time_end`, and a number of `data_points`, then it will divide the amount of time that that spans and will return a hash of times and counts. The other option is that you can specify either a `time_start` OR a `time_end` and a `step` and a number of `data_points`. This will start at whatever time you specified, and (if it’s ‘time_end`) count down by the step (if you specified `time_start`, it would count up), as many times as the number of data points you requested.
###Usage
DulyNoted.chart("page_views",
:time_range => 1.month.ago..Time.now,
:step => 1.day)
DulyNoted.chart("page_views",
:time_range => 1.day.ago..Time.now,
:data_points => 12)
DulyNoted.chart("page_views",
:time_start => 1.day.ago,
:step => 1.hour,
:data_points => 12)
DulyNoted.chart("downloads",
:time_end => Time.now,
:step => 1.month,
:data_points => 12)
DulyNoted.chart("page_views",
:data_points => 100)
Chart can be a little confusing but it’s pretty powerful, so play around with it.
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/duly_noted.rb', line 272 def chart(metric_name, ={}) parse_time_range() chart = Hash.new(0) if [:time_start] && [:time_end] time = [:time_start] if [:data_points] total_time = [:time_end] - [:time_start] [:step] = total_time.to_i / [:data_points] end while time < [:time_end] chart[time.to_i] = DulyNoted.count(metric_name, :time_start => time, :time_end => time+[:step], :for => [:for]) time += [:step] end elsif [:step] && [:data_points] && ([:time_end] || [:time_start]) raise InvalidStep if [:step] == 0 [:step] *= -1 if ([:step] > 0 && [:time_end]) || ([:step] < 0 && [:time_start]) time = [:time_start] || [:time_end] step = [:step] [:data_points].times do [:time_start] = time [:time_start] += step if step < 0 [:time_end] = time [:time_end] += step if step > 0 chart[time.to_i] = DulyNoted.count(metric_name, ) time += step end elsif [:data_points] key = build_key(metric_name) key << assemble_for() [:time_start] = Time.at(DulyNoted.redis.zrange(key, 0, 0, :withscores => true)[1].to_f) [:time_end] = Time.at(DulyNoted.redis.zrevrange(key, 0, 0, :withscores => true)[1].to_f) chart = DulyNoted.chart(metric_name, ) else raise InvalidOptions end return chart end |
#configure {|DulyNoted::Configuration| ... } ⇒ Object
376 377 378 |
# File 'lib/duly_noted.rb', line 376 def configure yield DulyNoted::Configuration end |
#count(metric_name, options = {}) ⇒ Object
##Count
_parameters: ‘metric_name`, `for`(optional), `time_start`(optional), `time_end`(optional), `time_range`(optional)_
Count will return the number of events logged in a given time range, or if no time range is given, the total count. As with ‘#query`, you can specify `for` to return a subset of counts, or you can leave it off to get the count across the whole `metric_name`.
###Usage
DulyNoted.count("page_views",
for: "home_page",
time_start: 1.day.ago,
time_end: Time.now)
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/duly_noted.rb', line 221 def count(metric_name, ={}) parse_time_range() key = build_key(metric_name) key << assemble_for() keys = find_keys(key) sum = 0 if [:time_start] && [:time_end] keys.each do |key| sum += DulyNoted.redis.zcount(key, [:time_start].to_f, [:time_end].to_f) end return sum else keys.each do |key| sum += DulyNoted.redis.zcard(key) end return sum end end |
#count_x_by_y(metric_name, meta_field, options) ⇒ Object
##Magic
###count_x_by_y
If you want to count a number of events by a meta field, you can use this magic command. So imagine this scenario:
DulyNoted.track("page_views", meta: {browser: "chrome"})
And you wanted to see a break down of page views by various browsers, you can call ‘DulyNoted.count_page_views_by_browser` and you’d get a hash that looked something like this:
{"chrome" => 2913, "firefox" => 5281, "IE" => 7182, "safari" => 3213}
So that method will work as soon as you’ve tracked something with that metric name. If you try to call the method on a metric that you haven’t yet tracked you will get a ‘DulyNoted::NotValidMetric`. But if you reference a meta field that didn’t exist, you’d just get a hash that looks like
{nil => 1}
326 327 328 329 330 331 332 333 334 335 |
# File 'lib/duly_noted.rb', line 326 def count_x_by_y(metric_name, , ) ||= {} = {:meta_fields => []}.merge() = query(metric_name, ) result = Hash.new(0) .each do || result[[]] += 1 end result end |
#query(metric_name, options = {}) ⇒ Object
##Query
_parameters: ‘metric_name`, `for`(optional), `meta_fields`(optional), `time_start`(optional), `time_end`(optional), `time_range`(optional)_
Query will return an array of all the metadata in chronological order from a time range, or for the whole data set. If for is specified, it will limit it by that context. For instance, if you have ‘track`ed several page views with `for` set to the name of the page that was viewed, you could query with `for` set to `home_page` to get all of the metadata from the page views from the home page, or you could leave off the `for`, and return all of the metadata for all of the page views, across all pages.
###Usage
DulyNoted.query("page_views",
for: "home_page",
time_start: 1.day.ago,
time_end: Time.now)
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/duly_noted.rb', line 163 def query(metric_name, ={}) key = build_key(metric_name) parse_time_range() key << assemble_for() if [:id] key = "dnid:#{[:id]}" real_key = DulyNoted.redis.get key if [:meta_fields] [:meta_fields].collect! { |x| x.to_s } result = {} [:meta_fields].each do |field| result[field] = DulyNoted.redis.hget real_key, field end results = [result] else results = [DulyNoted.redis.hgetall(real_key)] end else keys = find_keys(key) grab_results = Proc.new do |metric| if [:meta_fields] [:meta_fields].collect! { |x| x.to_s } result = {} [:meta_fields].each do |field| result[field] = DulyNoted.redis.hget metric, field end result else DulyNoted.redis.hgetall metric end end results = [] if [:time_start] && [:time_end] keys.each do |key| results += DulyNoted.redis.zrangebyscore(key, [:time_start].to_f, [:time_end].to_f).collect(&grab_results) end else keys.each do |key| results += DulyNoted.redis.zrange(key, 0, -1).collect(&grab_results) end end end return results end |
#redis ⇒ Object
363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/duly_noted.rb', line 363 def redis @redis ||= ( url = URI(@redis_url || "redis://127.0.0.1:6379/0") check_schema(Redis.new({ :host => url.host, :port => url.port, :db => url.path[1..-1], :password => url.password })) ) end |
#redis=(url) ⇒ Object
357 358 359 360 361 |
# File 'lib/duly_noted.rb', line 357 def redis=(url) @redis = nil @redis_url = url redis end |
#track(metric_name, options = {}) ⇒ Object
DulyNoted.track(“page_views”,
for: "home",
meta: {browser: "chrome"})
DulyNoted.track("video_plays",
for: ["user_7261", "video_917216"],
meta: {amount_watched: 0})
DulyNoted.track("purchases",
for: "user_281",
generated_at: 1.day.ago)
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/duly_noted.rb', line 108 def track(metric_name, ={}) = {:generated_at => Time.now}.merge() key = build_key(metric_name) key_without_for = key.dup id = DulyNoted.redis.incr "dnid" key << assemble_for() DulyNoted.redis.pipelined do DulyNoted.redis.sadd build_key("metrics", false), key_without_for DulyNoted.redis.zadd key, [:generated_at].to_f, "#{key}:#{id}:meta" DulyNoted.redis.set "dnid:#{id}", "#{key}:#{id}:meta" # set alias key DulyNoted.redis.expire "dnid:#{id}", DulyNoted::Configuration.editable_for if [:meta] # set meta data DulyNoted.redis.mapped_hmset "#{key}:#{id}:meta", [:meta] [:meta].keys.each do |field| DulyNoted.redis.sadd "#{key}:fields", field end end end id end |