counter 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -67,11 +67,15 @@ See Counter docs for detail.
67
67
  c.increment('another-key')
68
68
  end
69
69
 
70
- # Contribute to totals.
70
+ # Both contribute to totals grouped by category.
71
71
  PageView.totals
72
72
  => [['a-key',3],['another-key',1]]
73
73
 
74
- See MovingCount docs for detail.
74
+ # Get the grand total across all categories.
75
+ PageView.grand_total
76
+ => 4
77
+
78
+ See MovingCount docs for detail (filters available on totals to limit results).
75
79
 
76
80
  == Author
77
81
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.2
1
+ 0.5.3
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{counter}
8
- s.version = "0.5.2"
8
+ s.version = "0.5.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ben Koski"]
12
- s.date = %q{2010-07-29}
12
+ s.date = %q{2010-08-11}
13
13
  s.description = %q{count things, either as a one-off or aggregated over time}
14
14
  s.email = %q{bkoski@nytimes.com}
15
15
  s.extra_rdoc_files = [
@@ -88,10 +88,49 @@ class MovingCount < ActiveRecord::Base
88
88
  true
89
89
  end
90
90
 
91
- # Returns totals across entire history. Optional limit param restricts resultset.
92
- def self.totals limit=nil
93
- values = self.connection.select_rows("SELECT category, SUM(count) AS cnt FROM #{self.table_name} GROUP BY category ORDER BY cnt DESC #{"LIMIT #{limit}" if limit};")
91
+ # Returns single sum across all categories, limited by options. Use this to get, say, total across all categories matching "http://myhost..."
92
+ # Optional filters:
93
+ # * <tt>:window</tt> limits totaled to samples to those in the past <em>n</em> seconds (can of course specify as 1.hour with ActiveSupport)
94
+ # * <tt>:category_like</tt> run a LIKE match against categories before totaling, useful for limiting scope of totals. '%' wildcards are allowed.
95
+ def self.grand_total opts={}
96
+ latest_sample = self.maximum(:sample_time)
97
+ return 0 if latest_sample.nil? # don't try to run counts against empty db
98
+
99
+ q = "SELECT SUM(count) FROM #{self.table_name}"
100
+
101
+ where = []
102
+ where << self.sanitize_sql(['category LIKE ?', opts[:category_like]]) if opts[:category_like]
103
+ where << self.sanitize_sql(['sample_time > ?', latest_sample - opts[:window]]) if opts[:window]
104
+
105
+ q += " WHERE #{where.join(' AND ')}" unless where.empty?
106
+
107
+ self.connection.select_value(q).to_i
108
+ end
109
+
110
+ # Returns totals grouped by category across entire history.
111
+ # Optional filters can be used to filter totals:
112
+ # * <tt>:limit</tt> limits results to top <em>n</em>
113
+ # * <tt>:window</tt> limits totaled to samples to those in the past <em>n</em> seconds (can of course specify as 1.hour with ActiveSupport)
114
+ # * <tt>:category_like</tt> run a LIKE match against categories before totaling, useful for limiting scope of totals. '%' wildcards are allowed.
115
+ def self.totals opts={}
116
+ latest_sample = self.maximum(:sample_time)
117
+ return [] if latest_sample.nil? # don't try to run counts against empty db
118
+
119
+ q = "SELECT category, SUM(count) AS cnt FROM #{self.table_name}"
120
+
121
+ where = []
122
+ where << self.sanitize_sql(['category LIKE ?', opts[:category_like]]) if opts[:category_like]
123
+ where << self.sanitize_sql(['sample_time > ?', latest_sample - opts[:window]]) if opts[:window]
124
+
125
+ q += " WHERE #{where.join(' AND ')}" unless where.empty?
126
+
127
+ q += ' GROUP BY category'
128
+ q += ' ORDER BY cnt DESC'
129
+ q += " LIMIT #{opts[:limit]}" if opts[:limit]
130
+
131
+ values = self.connection.select_rows(q)
94
132
  values.map { |v| v[1] = v[1].to_i }
133
+
95
134
  return values
96
135
  end
97
136
 
@@ -111,12 +111,61 @@ class MovingCountTest < Test::Unit::TestCase
111
111
  assert_equal [['http://www.nytimes.com',3],['http://www.nytimes.com/article.html',1]], PageView.totals
112
112
  end
113
113
 
114
+ should "match against category_like if provided" do
115
+ setup_existing_counts(10.minutes)
116
+ setup_existing_counts(5.minutes)
117
+ PageView.record_counts { |c| c.saw('http://www.nytimes.com'); c.saw('http://www.nytimes.com/article.html') }
118
+
119
+ assert_equal [['http://www.nytimes.com/article.html',1]],
120
+ PageView.totals(:category_like => 'http://www.nytimes.com/article%')
121
+ end
122
+
123
+ should "filter to window if provided" do
124
+ setup_existing_counts(10.minutes)
125
+ setup_existing_counts(5.minutes)
126
+
127
+ assert_equal [['http://www.nytimes.com',1]], PageView.totals(:window => 5.minutes)
128
+ end
129
+
114
130
  should "respect limit if provided" do
115
131
  setup_existing_counts(10.minutes)
116
132
  setup_existing_counts(5.minutes)
117
133
  PageView.record_counts { |c| c.saw('http://www.nytimes.com'); c.saw('http://www.nytimes.com/article.html') }
118
134
 
119
- assert_equal [['http://www.nytimes.com',3]], PageView.totals(1)
135
+ assert_equal [['http://www.nytimes.com',3]], PageView.totals(:limit => 1)
136
+ end
137
+
138
+ should "return an empty array if no data has been recorded" do
139
+ assert_equal [], PageView.totals
140
+ end
141
+ end
142
+
143
+ context "grand total" do
144
+
145
+ should "return total sum across all samples" do
146
+ setup_existing_counts(10.minutes)
147
+ setup_existing_counts(5.minutes)
148
+
149
+ assert_equal 2, PageView.grand_total
150
+ end
151
+
152
+ should "match against category_like if providedd" do
153
+ setup_existing_counts(10.minutes)
154
+ setup_existing_counts(5.minutes)
155
+ PageView.record_counts { |c| c.saw('http://www.nytimes.com'); c.saw('http://www.nytimes.com/article.html') }
156
+
157
+ assert_equal 1, PageView.grand_total(:category_like => 'http://www.nytimes.com/article%')
158
+ end
159
+
160
+ should "filter to window if provided" do
161
+ setup_existing_counts(10.minutes)
162
+ setup_existing_counts(5.minutes)
163
+
164
+ assert_equal 1, PageView.grand_total(:window => 5.minutes)
165
+ end
166
+
167
+ should "return 0 if no data has been recorded" do
168
+ assert_equal 0, PageView.grand_total
120
169
  end
121
170
  end
122
171
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: counter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Koski
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-07-29 00:00:00 -04:00
12
+ date: 2010-08-11 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency