duly_noted 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ # - jruby-18mode # JRuby in 1.8 mode
7
+ # - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: 1.8.7
13
+ # uncomment this line if your project needs to run something other than `rake`:
14
+ script: bundle exec rspec spec
data/DOCS.md ADDED
@@ -0,0 +1,90 @@
1
+ ##Dependency
2
+ * Redis
3
+
4
+ The **DulyNoted** module contains four main methods:
5
+
6
+ * `track`
7
+ * `update`
8
+ * `query`
9
+ * `count`
10
+
11
+ ##Track
12
+
13
+ _parameters: `metric_name`, `for`(optional), `generated_at`(optional), `meta`(optional), `ref_id`(optional)_
14
+
15
+ `metric_name`: The name of the metric to track, ex: `page_views`, `downloads`
16
+
17
+ `for`_(optional)_: A name space for your metric, ex: `home_page`
18
+
19
+ `generated_at`_(optional)_: If the metric was generated in the past but is just now being logged, you can set the time it was generated at
20
+
21
+ `meta`_(optional)_: A hash with whatever meta data fields you might want to store, ex: `ip_address`, `file_type`
22
+
23
+ `ref_id`_(optional)_: If you need to reference the metric later, perhaps to add more metadata later on, you can set a reference id that you can use to update the metric
24
+
25
+ ##Update
26
+
27
+ _parameters: `metric_name`, `ref_id`, `for`(required if set when created), `meta`(optional)_
28
+
29
+ The meta hash will not overwrite the old meta hash but be merged with it, with the new one overwriting conflicts.
30
+
31
+ `metric_name`: The name of the metric to update, ex: `page_views`, `downloads`
32
+
33
+ `ref_id`: The reference ID that you set when you called `track`
34
+
35
+ `for`_(required if you set `for` when you generated the metric)_: A name space for your metric, ex: `home_page`
36
+
37
+ `meta`_(optional)_: A hash with whatever meta data fields you might want to store, or update ex: `ip_address`, `file_type`, `time_left`
38
+
39
+ ###Usage
40
+
41
+ DulyNoted.update("page_views", "a_unique_id", for: "home_page", meta: { time_on_page: 30 })
42
+
43
+ ##Query
44
+
45
+ _parameters: `metric_name`, `for`(required if set when created), `time_start`(optional), `time_end`(optional)_
46
+
47
+ Query will return an array of all the metadata in chronological order from a time range, or for the whole data set.
48
+
49
+ `metric_name`: The name of the metric to query, ex: `page_views`, `downloads`
50
+
51
+ `for`_(required if you set `for` when you generated the metric)_: A name space for your metric, ex: `home_page`
52
+
53
+ `time_start`_(optional)_: The start of the time range to grab the data from.
54
+
55
+ `time_end`_(optional)_: The end of the time range to grab the data from.
56
+
57
+ ###Usage
58
+
59
+ DulyNoted.query("page_views",
60
+ for: "home_page",
61
+ time_start: 1.day.ago,
62
+ time_end: Time.now)
63
+
64
+ ##Count
65
+
66
+ _parameters: `metric_name`, `for`(required if set when created), `time_start`(optional), `time_end`(optional)_
67
+
68
+ Count will return the number of events logged in a given time range, or if no time range is given, the total count.
69
+
70
+ `metric_name`: The name of the metric to query, ex: `page_views`, `downloads`
71
+
72
+ `for`_(required if you set `for` when you generated the metric)_: A name space for your metric, ex: `home_page`
73
+
74
+ `time_start`_(optional)_: The start of the time range to grab the data from.
75
+
76
+ `time_end`_(optional)_: The end of the time range to grab the data from.
77
+
78
+ ###Usage
79
+
80
+ DulyNoted.count("page_views",
81
+ for: "home_page",
82
+ time_start: 1.day.ago,
83
+ time_end: Time.now)
84
+
85
+ ##Redis
86
+
87
+ DulyNoted will try to connect to Redis's default url and port if you don't specify a Redis connection URL. You can set the url with the method
88
+
89
+ DulyNoted.redis = REDIS_URL
90
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in duly_noted.gemspec
4
+ gemspec
@@ -0,0 +1,13 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
10
+ guard :bundler do
11
+ watch('Gemfile')
12
+ end
13
+
@@ -0,0 +1,52 @@
1
+ #Duly Noted
2
+ [![Build Status](https://secure.travis-ci.org/willcosgrove/duly_noted.png?branch=master)](http://travis-ci.org/willcosgrove/duly_noted)
3
+
4
+ Duly noted is a redis backed stats and metrics tracker. It works as follows:
5
+
6
+ DulyNoted.track("page_views", for: "homepage")
7
+
8
+ This would log one page view for the home page. Then to see how many page views the home page has gotten, you would simply call:
9
+
10
+ DulyNoted.count("page_views", for: "homepage")
11
+
12
+ You can also store meta data with your metrics by passing your data in a hash to the `meta` key like so:
13
+
14
+ DulyNoted.track("page_views", for: "homepage", meta: {user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2", ip_address: "128.194.3.138"})
15
+
16
+ If the metric was generated in the past but is just now being logged, you can alter it's time stamp with the `generated_at` key:
17
+
18
+ DulyNoted.track("page_views", for: "homepage", generated_at: 10.minutes.ago)
19
+
20
+ To get the count for a particular time range, you can use the `time_start` and `time_end` keys in the count call like so:
21
+
22
+ DulyNoted.count("page_views", for: "homepage", time_start: 1.day.ago, time_end: Time.now)
23
+
24
+ You can also just specify a `time_range` like so:
25
+
26
+ DulyNoted.count("page_views", for: "homepage", time_range: 1.day.ago..Time.now)
27
+
28
+ This will return the page view count for the home page for the past day.
29
+
30
+ ##What's New
31
+
32
+ ### 0.1.0
33
+
34
+ * Added the `time_range` option to `count`, and `query`
35
+
36
+ * Added the `meta_fields` option to `query`, which takes an array of fields to pull out from the meta hash
37
+
38
+ * Added the `ref_id` option to `query`, which takes a reference id and will return an array with one meta hash. I was going back and forth on whether or not it should wrap the hash in an array. It doesn't need to be, but I thought, just to make it consistant with it's usual output, I should make it return an array.
39
+
40
+ * Enough bug fixes to make it production ready! Yay!
41
+
42
+
43
+ ##To Do
44
+
45
+ * Count by meta fields: How many page views from each browser?
46
+
47
+ * A `chart` method which would take a `time_start`, `time_end`, and a `granularity` which would allow you to easily get the per hour for each hour for the past day, for example.
48
+
49
+ * Maybe some Rails view helpers to generate some code for a Javascript charting library, or Google Charts API.
50
+
51
+ ##Contributing
52
+ If you want to help, you should do it. Fork it, fix it, and send me a pull request. I will be delighted.
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Default: run specs.'
6
+ task :default => :spec
7
+
8
+ desc "Run specs"
9
+ RSpec::Core::RakeTask.new do |t|
10
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
11
+ # Put spec opts in a file named .rspec in root
12
+ end
13
+
14
+ desc "Generate code coverage"
15
+ RSpec::Core::RakeTask.new(:coverage) do |t|
16
+ t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
17
+ t.rcov = true
18
+ t.rcov_opts = ['--exclude', 'spec']
19
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/duly_noted/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Will Cosgrove"]
6
+ gem.email = ["will@willcosgrove.com"]
7
+ gem.description = %q{keep detailed metrics on your project with a speedy, powerful redis backend.}
8
+ gem.summary = %q{a simple redis based stat-tracker}
9
+ gem.homepage = "http://github.com/willcosgrove/duly_noted"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "duly_noted"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = DulyNoted::VERSION
17
+ gem.add_dependency("redis")
18
+ gem.add_development_dependency("rspec")
19
+ gem.add_development_dependency("rake")
20
+ gem.add_development_dependency("rb-fsevent")
21
+ gem.add_development_dependency("guard-rspec")
22
+ gem.add_development_dependency("growl")
23
+ gem.add_development_dependency("guard-bundler")
24
+ gem.add_development_dependency("chronic")
25
+ end
@@ -0,0 +1,270 @@
1
+ # Duly noted is a redis backed stats and metrics tracker. It works as follows:
2
+ #
3
+ # DulyNoted.track("page_views",
4
+ # for: "home_page")
5
+ #
6
+ # This would log one page view for the home page. Then to see how many page views the home page has gotten, you would simply call:
7
+ #
8
+ # DulyNoted.count("page_views",
9
+ # for: "home_page")
10
+ #
11
+ # You can also store meta data with your metrics by passing your data in a hash to the `meta` key like so:
12
+ #
13
+ # DulyNoted.track("page_views",
14
+ # for: "home_page",
15
+ # meta: {
16
+ # user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2)...",
17
+ # ip_address: "128.194.3.138"
18
+ # })
19
+ #
20
+ # If the metric was generated in the past but is just now being logged, you can alter it's time stamp with the `generated_at` key:
21
+ #
22
+ # DulyNoted.track("page_views",
23
+ # for: "home_page",
24
+ # generated_at: 10.minutes.ago)
25
+ #
26
+ # To get the count for a particular time range, you can use the `time_start` and `time_end` keys in the count call like so:
27
+ #
28
+ # DulyNoted.count("page_views",
29
+ # for: "home_page",
30
+ # time_start: 1.day.ago,
31
+ # time_end: Time.now)
32
+ #
33
+ # This will return the page view count for the home page for the past day.
34
+ #
35
+ # You can also just specify a `time_range` like so:
36
+ #
37
+ # DulyNoted.count("page_views",
38
+ # for: "homepage",
39
+ # time_range: 1.day.ago..Time.now)
40
+
41
+ # ##Dependency
42
+ # * Redis
43
+
44
+ require "redis"
45
+ require "uri"
46
+ require "duly_noted/helpers"
47
+ require "duly_noted/version"
48
+
49
+ # The **DulyNoted** module contains four main methods:
50
+ #
51
+ # * `track`
52
+ # * `update`
53
+ # * `query`
54
+ # * `count`
55
+
56
+ module DulyNoted
57
+ include Helpers
58
+ extend self # the following are class methods
59
+
60
+ # ##Track
61
+ #
62
+ # _parameters: `metric_name`, `for`(optional), `generated_at`(optional), `meta`(optional), `ref_id`(optional)_
63
+ #
64
+ # `metric_name`: The name of the metric to track, ex: `page_views`, `downloads`
65
+ #
66
+ # `for`_(optional)_: A name space for your metric, ex: `home_page`
67
+ #
68
+ # `generated_at`_(optional)_: If the metric was generated in the past but is just now being logged, you can set the time it was generated at
69
+ #
70
+ # `meta`_(optional)_: A hash with whatever meta data fields you might want to store, ex: `ip_address`, `file_type`
71
+ #
72
+ # `ref_id`_(optional)_: If you need to reference the metric later, perhaps to add more metadata later on, you can set a reference id that you can use to update the metric
73
+
74
+ def track(metric_name, options={})
75
+ options = {:generated_at => Time.now}.merge(options)
76
+ key = normalize(metric_name)
77
+ key << ":#{options[:for]}" if options[:for]
78
+ DulyNoted.redis.zadd key, options[:generated_at].to_f, "#{key}:#{options[:generated_at].to_f}:meta"
79
+ DulyNoted.redis.set "#{key}:#{options[:ref_id]}", "#{key}:#{options[:generated_at].to_f}:meta" if options[:ref_id] # set alias key
80
+ DulyNoted.redis.mapped_hmset "#{key}:#{options[:generated_at].to_f}:meta", options[:meta] if options[:meta] # set meta data
81
+ end
82
+
83
+ # ##Update
84
+ #
85
+ # _parameters: `metric_name`, `ref_id`, `for`(required if set when created), `meta`(optional)_
86
+ #
87
+ # The meta hash will not overwrite the old meta hash but be merged with it, with the new one overwriting conflicts.
88
+ #
89
+ # `metric_name`: The name of the metric to track, ex: `page_views`, `downloads`
90
+ #
91
+ # `ref_id`: The reference ID that you set when you called `track`
92
+ #
93
+ # `for`_(required if you set `for` when you generated the metric)_: A name space for your metric, ex: `home_page`
94
+ #
95
+ # `meta`_(optional)_: A hash with whatever meta data fields you might want to store, or update ex: `ip_address`, `file_type`, `time_left`
96
+ #
97
+ # ###Usage
98
+ #
99
+ # DulyNoted.update("page_views",
100
+ # "a_unique_id",
101
+ # for: "home_page",
102
+ # meta: { time_on_page: 30 })
103
+
104
+ def update(metric_name, ref_id, options={})
105
+ key = normalize(metric_name)
106
+ key << ":#{options[:for]}" if options[:for]
107
+ key << ":#{ref_id}"
108
+ real_key = DulyNoted.redis.get key
109
+ DulyNoted.redis.mapped_hmset real_key, options[:meta] if options[:meta]
110
+ end
111
+
112
+ # ##Query
113
+ #
114
+ # _parameters: `metric_name`, `for`(required if set when created), `time_start`(optional), `time_end`(optional)_
115
+ #
116
+ # Query will return an array of all the metadata in chronological order from a time range, or for the whole data set.
117
+ #
118
+ # `metric_name`: The name of the metric to query, ex: `page_views`, `downloads`
119
+ #
120
+ # `for`_(required if you set `for` when you generated the metric)_: A name space for your metric, ex: `home_page`
121
+ #
122
+ # `ref_id`: _(optional)_: The reference ID that you set when you called `track` (if you set this, the time restraints is ignored)
123
+ #
124
+ # `meta_fields` _(optional)_: An array of fields to retrieve from the meta hash. If not specified, the entire hash will be grabbed. Fields will be converted to strings, because redis converts all hash keys and values to strings.
125
+ #
126
+ # `time_start`_(optional)_: The start of the time range to grab the data from.
127
+ #
128
+ # `time_end`_(optional)_: The end of the time range to grab the data from.
129
+ #
130
+ # `time_range _(optional)_: Alternatively you can specify a time range, instead of `time_start` and `time_end`.
131
+ #
132
+ # ###Usage
133
+ #
134
+ # DulyNoted.query("page_views",
135
+ # for: "home_page",
136
+ # time_start: 1.day.ago,
137
+ # time_end: Time.now)
138
+ #
139
+ #
140
+ # DulyNoted.query("page_views",
141
+ # for: "home_page",
142
+ # time_range: 1.day.ago..Time.now)
143
+
144
+ def query(metric_name, options={})
145
+ key = normalize(metric_name)
146
+ parse_time_range(options)
147
+ key << ":#{options[:for]}" if options[:for]
148
+ if options[:ref_id]
149
+ key << ":#{options[:ref_id]}"
150
+ real_key = DulyNoted.redis.get key
151
+ if options[:meta_fields]
152
+ options[:meta_fields].collect! { |x| x.to_s }
153
+ result = {}
154
+ options[:meta_fields].each do |field|
155
+ result[field] = DulyNoted.redis.hget real_key, field
156
+ end
157
+ results = [result]
158
+ else
159
+ results = [DulyNoted.redis.hgetall(real_key)]
160
+ end
161
+ else
162
+ grab_results = Proc.new do |metric|
163
+ if options[:meta_fields]
164
+ options[:meta_fields].collect! { |x| x.to_s }
165
+ result = {}
166
+ options[:meta_fields].each do |field|
167
+ result[field] = DulyNoted.redis.hget metric, field
168
+ end
169
+ result
170
+ else
171
+ DulyNoted.redis.hgetall metric
172
+ end
173
+ end
174
+ if options[:time_start] && options[:time_end]
175
+ results = DulyNoted.redis.zrangebyscore(key, options[:time_start].to_f, options[:time_end].to_f).collect(&grab_results)
176
+ else
177
+ results = DulyNoted.redis.zrange(key, 0, -1).collect(&grab_results)
178
+ end
179
+ end
180
+ return results
181
+ end
182
+
183
+ # ##Count
184
+ #
185
+ # _parameters: `metric_name`, `for`(required if set when created), `time_start`(optional), `time_end`(optional)_
186
+ #
187
+ # Count will return the number of events logged in a given time range, or if no time range is given, the total count.
188
+ #
189
+ # `metric_name`: The name of the metric to query, ex: `page_views`, `downloads`
190
+ #
191
+ # `for`_(required if you set `for` when you generated the metric)_: A name space for your metric, ex: `home_page`
192
+ #
193
+ # `time_start`_(optional)_: The start of the time range to grab the data from.
194
+ #
195
+ # `time_end`_(optional)_: The end of the time range to grab the data from.
196
+ #
197
+ # `time_range _(optional)_: Alternatively you can specify a time range, instead of `time_start` and `time_end`.
198
+ #
199
+ # ###Usage
200
+ #
201
+ # DulyNoted.count("page_views",
202
+ # for: "home_page",
203
+ # time_start: Time.now,
204
+ # time_end: 1.day.ago)
205
+ #
206
+ #
207
+ # DulyNoted.count("page_views",
208
+ # for: "home_page",
209
+ # time_range: Time.now..1.day.ago)
210
+
211
+ def count(metric_name, options={})
212
+ parse_time_range(options)
213
+ key = normalize(metric_name)
214
+ keys = []
215
+ if options[:for]
216
+ key << ":#{options[:for]}"
217
+ else
218
+ keys << DulyNoted.redis.keys("#{key}*")
219
+ keys - DulyNoted.redis.keys("#{key}*:meta")
220
+ keys - DulyNoted.redis.keys("#{key}:*:")
221
+ keys.flatten!
222
+ end
223
+ if keys.empty?
224
+ if options[:time_start] && options[:time_end]
225
+ return DulyNoted.redis.zcount(key, options[:time_start].to_f, options[:time_end].to_f)
226
+ else
227
+ return DulyNoted.redis.zcard(key)
228
+ end
229
+ else
230
+ sum = 0
231
+ if options[:time_start] && options[:time_end]
232
+ keys.each do |key|
233
+ sum += DulyNoted.redis.zcount(key, options[:time_start].to_f, options[:time_end].to_f)
234
+ end
235
+ return sum
236
+ else
237
+ keys.each do |key|
238
+ sum += DulyNoted.redis.zcard(key)
239
+ end
240
+ return sum
241
+ end
242
+ end
243
+ end
244
+
245
+ # ##Redis
246
+ #
247
+ # DulyNoted will try to connect to Redis's default url and port if you don't specify a Redis connection URL. You can set the url with the method
248
+ #
249
+ # DulyNoted.redis = REDIS_URL
250
+
251
+ def redis=(url)
252
+ @redis = nil
253
+ @redis_url = url
254
+ redis
255
+ end
256
+
257
+ def redis
258
+ @redis ||= (
259
+ url = URI(@redis_url || "redis://127.0.0.1:6379/0")
260
+
261
+ Redis.new({
262
+ :host => url.host,
263
+ :port => url.port,
264
+ :db => url.path[1..-1],
265
+ :password => url.password
266
+ })
267
+ )
268
+ end
269
+
270
+ end
@@ -0,0 +1,14 @@
1
+ module DulyNoted
2
+ module Helpers
3
+ def normalize(str)
4
+ str.downcase.gsub(/[^a-z0-9 ]/i, '').strip
5
+ end
6
+
7
+ def parse_time_range(options)
8
+ if options[:time_range]
9
+ options[:time_start] = options[:time_range].first
10
+ options[:time_end] = options[:time_range].last
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module DulyNoted
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe DulyNoted do
4
+ before :each do
5
+ DulyNoted.redis.flushall
6
+ end
7
+
8
+ describe "#track" do
9
+ it "keeps an accurate count of events" do
10
+ 2.times { DulyNoted.track "page_views" }
11
+ DulyNoted.count("page_views").should eq(2)
12
+ end
13
+ it "separates by context" do
14
+ 2.times { DulyNoted.track "page_views", :for => "home" }
15
+ 5.times { DulyNoted.track "page_views", :for => "contact_us" }
16
+ DulyNoted.count("page_views", :for => "home").should eq(2)
17
+ DulyNoted.count("page_views", :for => "contact_us").should eq(5)
18
+ end
19
+ it "stores metadata" do
20
+ DulyNoted.track "page_views", :meta => {:open => true}
21
+ DulyNoted.query("page_views").should include({"open" => "true"})
22
+ end
23
+ it "can track past events" do
24
+ DulyNoted.track "page_views", :generated_at => Time.now-10
25
+ DulyNoted.count "page_views", :time_range => Time.now-11..Time.now-9
26
+ end
27
+ end
28
+
29
+ describe "#update" do
30
+ it "overwrites duplicate keys" do
31
+ DulyNoted.track "page_views", :meta => {:seconds_open => 0}, :ref_id => "unique"
32
+ DulyNoted.update "page_views", "unique", :meta => {:seconds_open => 5}
33
+ DulyNoted.query("page_views").should include({"seconds_open" => "5"})
34
+ DulyNoted.query("page_views").should_not include({"seconds_open" => "0"})
35
+ end
36
+ it "doesn't replace old hash" do
37
+ DulyNoted.track "page_views", :meta => {:seconds_open => 0}, :ref_id => "unique"
38
+ DulyNoted.update "page_views", "unique", :meta => {:ip_address => "19.27.182.32"}
39
+ DulyNoted.query("page_views").should include({"seconds_open" => "0", "ip_address" => "19.27.182.32"})
40
+ end
41
+ end
42
+
43
+ describe "#query" do
44
+ it "should grab entire meta hash" do
45
+ DulyNoted.track "page_views", :meta => {:seconds_open => 0, :browser => "chrome"}
46
+ DulyNoted.query("page_views").should include({"seconds_open" => "0", "browser" => "chrome"})
47
+ end
48
+ it "can query a certain ref_id" do
49
+ DulyNoted.track "page_views", :meta => {:seconds_open => 0, :browser => "chrome"}, :ref_id => "unique"
50
+ DulyNoted.track "page_views", :meta => {:seconds_open => 10, :browser => "firefox"}
51
+ DulyNoted.query("page_views", :ref_id => "unique").should include({"seconds_open" => "0", "browser" => "chrome"})
52
+ DulyNoted.query("page_views", :ref_id => "unique").should_not include({"seconds_open" => "10", "browser" => "firefox"})
53
+ end
54
+ it "can grab only specific fields from the hash" do
55
+ DulyNoted.track "page_views", :meta => {:seconds_open => 0, :browser => "chrome"}
56
+ DulyNoted.query("page_views", :meta_fields => [:browser]).should include({"browser" => "chrome"})
57
+ DulyNoted.query("page_views", :meta_fields => [:browser]).should_not include({"seconds_open" => "0"})
58
+ DulyNoted.track "downloads", :meta => {:file_name => "rules.pdf", :browser => "chrome"}, :ref_id => "unique"
59
+ DulyNoted.query("downloads", :ref_id => "unique", :meta_fields => [:browser]).should include({"browser" => "chrome"})
60
+ DulyNoted.query("downloads", :ref_id => "unique", :meta_fields => [:browser]).should_not include({"file_name" => "rules.pdf"})
61
+ end
62
+ it "can get only meta hashes from a certain time range" do
63
+ 5.times { DulyNoted.track "page_views", :meta => {:seconds_open => 5, :browser => "chrome"} }
64
+ sleep 0.5
65
+ 5.times { DulyNoted.track "page_views", :meta => {:seconds_open => 0, :browser => "firefox"} }
66
+ DulyNoted.query("page_views", :time_start => Time.now-0.5, :time_end => Time.now).should include({"seconds_open" => "0", "browser" => "firefox"})
67
+ DulyNoted.query("page_views", :time_range => Time.now-0.5..Time.now).should include({"seconds_open" => "0", "browser" => "firefox"})
68
+ DulyNoted.query("page_views", :time_start => Time.now-0.5, :time_end => Time.now).should_not include({"seconds_open" => "5", "browser" => "chrome"})
69
+ DulyNoted.query("page_views", :time_range => Time.now-0.5..Time.now).should_not include({"seconds_open" => "5", "browser" => "chrome"})
70
+ end
71
+ end
72
+
73
+ describe "#count" do
74
+ it "can count events in between a time range" do
75
+ 5.times { DulyNoted.track "page_views" }
76
+ sleep 0.2
77
+ 5.times { DulyNoted.track "page_views" }
78
+ DulyNoted.count("page_views", :time_start => Time.now-0.2, :time_end => Time.now).should eq(5)
79
+ DulyNoted.count("page_views", :time_range => Time.now-0.2..Time.now).should eq(5)
80
+ end
81
+ it "can count all of one type of metric" do
82
+ 5.times { DulyNoted.track "page_views", :for => "home" }
83
+ 5.times { DulyNoted.track "page_views", :for => "contact_us" }
84
+ DulyNoted.count("page_views").should eq(10)
85
+ end
86
+ it "can count all of one type between a time range" do
87
+ 5.times { DulyNoted.track "page_views", :for => "home" }
88
+ 5.times { DulyNoted.track "page_views", :for => "contact_us" }
89
+ sleep 0.2
90
+ 5.times { DulyNoted.track "page_views", :for => "home" }
91
+ 5.times { DulyNoted.track "page_views", :for => "contact_us" }
92
+ DulyNoted.count("page_views", :time_start => Time.now-0.2, :time_end => Time.now).should eq(10)
93
+ DulyNoted.count("page_views", :time_range => Time.now-0.2..Time.now).should eq(10)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,10 @@
1
+ unless Kernel.respond_to?(:require_relative)
2
+ module Kernel
3
+ def require_relative(path)
4
+ require File.join(File.dirname(caller[0]), path.to_str)
5
+ end
6
+ end
7
+ end
8
+
9
+ require_relative "../lib/duly_noted"
10
+ require 'chronic'
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: duly_noted
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Will Cosgrove
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: redis
16
+ requirement: &70245889112380 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70245889112380
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70245889111680 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70245889111680
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70245889110680 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70245889110680
47
+ - !ruby/object:Gem::Dependency
48
+ name: rb-fsevent
49
+ requirement: &70245889109440 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70245889109440
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-rspec
60
+ requirement: &70245889108340 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70245889108340
69
+ - !ruby/object:Gem::Dependency
70
+ name: growl
71
+ requirement: &70245889107640 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70245889107640
80
+ - !ruby/object:Gem::Dependency
81
+ name: guard-bundler
82
+ requirement: &70245889106880 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70245889106880
91
+ - !ruby/object:Gem::Dependency
92
+ name: chronic
93
+ requirement: &70245889106080 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *70245889106080
102
+ description: keep detailed metrics on your project with a speedy, powerful redis backend.
103
+ email:
104
+ - will@willcosgrove.com
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - .DS_Store
110
+ - .gitignore
111
+ - .travis.yml
112
+ - DOCS.md
113
+ - Gemfile
114
+ - Guardfile
115
+ - README.md
116
+ - Rakefile
117
+ - duly_noted.gemspec
118
+ - lib/duly_noted.rb
119
+ - lib/duly_noted/helpers.rb
120
+ - lib/duly_noted/version.rb
121
+ - spec/duly_noted_spec.rb
122
+ - spec/spec_helper.rb
123
+ homepage: http://github.com/willcosgrove/duly_noted
124
+ licenses: []
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 1.8.16
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: a simple redis based stat-tracker
147
+ test_files:
148
+ - spec/duly_noted_spec.rb
149
+ - spec/spec_helper.rb