redistat 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -4
- data/Gemfile.lock +13 -4
- data/README.md +58 -3
- data/Rakefile +5 -3
- data/VERSION +1 -1
- data/lib/redistat.rb +0 -20
- data/lib/redistat/model.rb +1 -1
- data/spec/db/.emptydir +0 -0
- data/spec/finder_spec.rb +0 -9
- data/spec/summary_spec.rb +0 -23
- metadata +206 -33
- data/.gitignore +0 -26
data/Gemfile
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
source 'http://rubygems.org/'
|
2
2
|
|
3
3
|
gem 'activesupport', '>= 2.3.0'
|
4
|
-
gem 'json', '>= 1.
|
5
|
-
gem 'redis', '>= 2.
|
4
|
+
gem 'json', '>= 1.4.6'
|
5
|
+
gem 'redis', '>= 2.1.1'
|
6
|
+
gem 'system_timer', '>= 1.0.0'
|
6
7
|
gem 'time_ext', '>= 0.2.6'
|
7
8
|
|
8
9
|
group :development do
|
9
|
-
gem '
|
10
|
-
gem '
|
10
|
+
gem 'jeweler', '>= 1.5.1'
|
11
|
+
gem 'rspec', '>= 2.1.0'
|
12
|
+
gem 'yard', '>= 0.6.3'
|
11
13
|
gem 'i18n'
|
12
14
|
end
|
data/Gemfile.lock
CHANGED
@@ -3,8 +3,14 @@ GEM
|
|
3
3
|
specs:
|
4
4
|
activesupport (3.0.3)
|
5
5
|
diff-lcs (1.1.2)
|
6
|
+
git (1.2.5)
|
6
7
|
i18n (0.4.2)
|
8
|
+
jeweler (1.5.1)
|
9
|
+
bundler (~> 1.0.0)
|
10
|
+
git (>= 1.2.5)
|
11
|
+
rake
|
7
12
|
json (1.4.6)
|
13
|
+
rake (0.8.7)
|
8
14
|
redis (2.1.1)
|
9
15
|
rspec (2.1.0)
|
10
16
|
rspec-core (~> 2.1.0)
|
@@ -14,6 +20,7 @@ GEM
|
|
14
20
|
rspec-expectations (2.1.0)
|
15
21
|
diff-lcs (~> 1.1.2)
|
16
22
|
rspec-mocks (2.1.0)
|
23
|
+
system_timer (1.0)
|
17
24
|
time_ext (0.2.6)
|
18
25
|
activesupport (>= 2.3.0)
|
19
26
|
yard (0.6.3)
|
@@ -24,8 +31,10 @@ PLATFORMS
|
|
24
31
|
DEPENDENCIES
|
25
32
|
activesupport (>= 2.3.0)
|
26
33
|
i18n
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
jeweler (>= 1.5.1)
|
35
|
+
json (>= 1.4.6)
|
36
|
+
redis (>= 2.1.1)
|
37
|
+
rspec (>= 2.1.0)
|
38
|
+
system_timer (>= 1.0.0)
|
30
39
|
time_ext (>= 0.2.6)
|
31
|
-
yard (>= 0.6.
|
40
|
+
yard (>= 0.6.3)
|
data/README.md
CHANGED
@@ -2,11 +2,66 @@
|
|
2
2
|
|
3
3
|
A Redis-backed statistics storage and querying library written in Ruby.
|
4
4
|
|
5
|
-
|
5
|
+
Redistat was originally created to replace a small hacked together statistics collection solution which was MySQL-based. When I started I had a short list of requirements:
|
6
6
|
|
7
|
-
|
7
|
+
* Store and increment/decrement integer values (counters, etc)
|
8
|
+
* Up to the second statistics available at all times
|
9
|
+
* Screamingly fast
|
8
10
|
|
9
|
-
|
11
|
+
Redis fits perfectly with all of these requirements. It has atomic operations like increment, and it's lightning fast, meaning if the data is structured well, the initial stats reporting call will store data in a format that's instantly retrievable just as fast.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
gem install redistat
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
The simplest way to use Redistat is through the model wrapper.
|
20
|
+
|
21
|
+
class VisitorStats
|
22
|
+
include Redistat::Model
|
23
|
+
end
|
24
|
+
|
25
|
+
Before any of you Rails-purists start complaining about the model name being plural, I want to point out that it makes sense with Redistat, cause a model doesn't exactly return a specific row or object. But I'm getting ahead of myself.
|
26
|
+
|
27
|
+
To store statistics we essentially tell Redistat that an event has occurred with a label of X, and statistics of Y. So let's say we want to store a page view event on the `/about` page on a site:
|
28
|
+
|
29
|
+
VisitorStats.store('/about', {:views => 1})
|
30
|
+
|
31
|
+
In the above case "`/about`" is the label under which the stats are grouped, and the statistics associated with the event is simply a normal Ruby hash, except all values need to be integers, or Redis' increment calls won't work.
|
32
|
+
|
33
|
+
To later retrieve statistics, we use the `fetch` method:
|
34
|
+
|
35
|
+
stats = VisitorStats.fetch('/about', 2.hour.ago, Time.now)
|
36
|
+
# stats => [{:views => 1}]
|
37
|
+
# stats.total => {:views => 1}
|
38
|
+
|
39
|
+
The fetch method requires 3 arguments, a label, a start time, and an end time. Fetch returns a `Redistat::Collection` object, which is normal Ruby Array with a couple of added methods, like total shown above.
|
40
|
+
|
41
|
+
For more detailed usage, please check spec files, and source code till I have time to write up a complete readme.
|
42
|
+
|
43
|
+
|
44
|
+
## Some Technical Details
|
45
|
+
|
46
|
+
To give a brief look into how Redistat works internally to store statistics, I'm going to use the examples above. The store method accepts a Ruby Hash with statistics to store. Redistat stores all statistics as hashes in Redis. It stores summaries of the stats for the specific time when it happened and all it's parent time groups (second, minute, hour, day, month, year). The default depth Redistat goes to is hour, unless the `depth` option is passed to `store` or `fetch`.
|
47
|
+
|
48
|
+
In short, the above call to `store` creates the following keys in Redis:
|
49
|
+
|
50
|
+
VisitorStats//about:2010
|
51
|
+
VisitorStats//about:201011
|
52
|
+
VisitorStats//about:20101124
|
53
|
+
VisitorStats//about:2010112401
|
54
|
+
|
55
|
+
Each of these keys in Redis are a hash, containing the sums of each statistic point reported for the time frame the key represents. In this case there's two slashes, cause the label we used was “`/about`”, and the scope (class name when used through the model wrapper) and the label are separated with a slash.
|
56
|
+
|
57
|
+
When retrieving statistics for a given date range, Redistat figures out how to do the least number of calls to Redis to fetch all relevant data. For example, if you want the sum of stats from the 4th till the last of November, the full month of November would first be fetched, then the first 3 days of November would be fetched and removed from the full month stats.
|
58
|
+
|
59
|
+
|
60
|
+
## Todo
|
61
|
+
|
62
|
+
* Proper/complete readme.
|
63
|
+
* Documentation.
|
64
|
+
* Anything else that becomes apparent after real-world use.
|
10
65
|
|
11
66
|
|
12
67
|
## Note on Patches/Pull Requests
|
data/Rakefile
CHANGED
@@ -11,10 +11,12 @@ begin
|
|
11
11
|
gem.homepage = 'http://github.com/jimeh/redistat'
|
12
12
|
gem.authors = ['Jim Myhrberg']
|
13
13
|
gem.add_dependency 'activesupport', '>= 2.3.0'
|
14
|
-
gem.add_dependency 'json', '>= 1.
|
15
|
-
gem.add_dependency 'redis', '>= 2.
|
14
|
+
gem.add_dependency 'json', '>= 1.4.6'
|
15
|
+
gem.add_dependency 'redis', '>= 2.1.1'
|
16
|
+
gem.add_dependency 'system_timer', '>= 1.0.0'
|
16
17
|
gem.add_dependency 'time_ext', '>= 0.2.6'
|
17
|
-
gem.add_development_dependency '
|
18
|
+
gem.add_development_dependency 'jeweler', '>= 1.5.1'
|
19
|
+
gem.add_development_dependency 'rspec', '>= 2.1.0'
|
18
20
|
gem.add_development_dependency 'yard', '>= 0.6.1'
|
19
21
|
end
|
20
22
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
data/lib/redistat.rb
CHANGED
data/lib/redistat/model.rb
CHANGED
data/spec/db/.emptydir
ADDED
File without changes
|
data/spec/finder_spec.rb
CHANGED
data/spec/summary_spec.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redistat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jim Myhrberg
|
@@ -15,13 +15,14 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-11-
|
18
|
+
date: 2010-11-24 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
name: activesupport
|
23
22
|
prerelease: false
|
24
|
-
|
23
|
+
type: :runtime
|
24
|
+
name: activesupport
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
25
26
|
none: false
|
26
27
|
requirements:
|
27
28
|
- - ">="
|
@@ -32,12 +33,44 @@ dependencies:
|
|
32
33
|
- 3
|
33
34
|
- 0
|
34
35
|
version: 2.3.0
|
35
|
-
|
36
|
-
version_requirements: *id001
|
36
|
+
requirement: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
+
prerelease: false
|
39
|
+
type: :runtime
|
38
40
|
name: json
|
41
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 11
|
47
|
+
segments:
|
48
|
+
- 1
|
49
|
+
- 4
|
50
|
+
- 6
|
51
|
+
version: 1.4.6
|
52
|
+
requirement: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
prerelease: false
|
55
|
+
type: :runtime
|
56
|
+
name: redis
|
57
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 9
|
63
|
+
segments:
|
64
|
+
- 2
|
65
|
+
- 1
|
66
|
+
- 1
|
67
|
+
version: 2.1.1
|
68
|
+
requirement: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
39
70
|
prerelease: false
|
40
|
-
|
71
|
+
type: :runtime
|
72
|
+
name: system_timer
|
73
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
41
74
|
none: false
|
42
75
|
requirements:
|
43
76
|
- - ">="
|
@@ -48,28 +81,154 @@ dependencies:
|
|
48
81
|
- 0
|
49
82
|
- 0
|
50
83
|
version: 1.0.0
|
84
|
+
requirement: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
prerelease: false
|
51
87
|
type: :runtime
|
52
|
-
|
88
|
+
name: time_ext
|
89
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 27
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
- 2
|
98
|
+
- 6
|
99
|
+
version: 0.2.6
|
100
|
+
requirement: *id005
|
53
101
|
- !ruby/object:Gem::Dependency
|
54
|
-
name: redis
|
55
102
|
prerelease: false
|
56
|
-
|
103
|
+
type: :development
|
104
|
+
name: jeweler
|
105
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
57
106
|
none: false
|
58
107
|
requirements:
|
59
108
|
- - ">="
|
60
109
|
- !ruby/object:Gem::Version
|
61
|
-
hash:
|
110
|
+
hash: 1
|
111
|
+
segments:
|
112
|
+
- 1
|
113
|
+
- 5
|
114
|
+
- 1
|
115
|
+
version: 1.5.1
|
116
|
+
requirement: *id006
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
prerelease: false
|
119
|
+
type: :development
|
120
|
+
name: rspec
|
121
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 11
|
62
127
|
segments:
|
63
128
|
- 2
|
129
|
+
- 1
|
64
130
|
- 0
|
131
|
+
version: 2.1.0
|
132
|
+
requirement: *id007
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
prerelease: false
|
135
|
+
type: :development
|
136
|
+
name: yard
|
137
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
hash: 1
|
143
|
+
segments:
|
144
|
+
- 0
|
145
|
+
- 6
|
146
|
+
- 3
|
147
|
+
version: 0.6.3
|
148
|
+
requirement: *id008
|
149
|
+
- !ruby/object:Gem::Dependency
|
150
|
+
prerelease: false
|
151
|
+
type: :development
|
152
|
+
name: i18n
|
153
|
+
version_requirements: &id009 !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
hash: 3
|
159
|
+
segments:
|
65
160
|
- 0
|
66
|
-
version:
|
161
|
+
version: "0"
|
162
|
+
requirement: *id009
|
163
|
+
- !ruby/object:Gem::Dependency
|
164
|
+
prerelease: false
|
67
165
|
type: :runtime
|
68
|
-
|
166
|
+
name: activesupport
|
167
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
168
|
+
none: false
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
hash: 3
|
173
|
+
segments:
|
174
|
+
- 2
|
175
|
+
- 3
|
176
|
+
- 0
|
177
|
+
version: 2.3.0
|
178
|
+
requirement: *id010
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
prerelease: false
|
181
|
+
type: :runtime
|
182
|
+
name: json
|
183
|
+
version_requirements: &id011 !ruby/object:Gem::Requirement
|
184
|
+
none: false
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
hash: 11
|
189
|
+
segments:
|
190
|
+
- 1
|
191
|
+
- 4
|
192
|
+
- 6
|
193
|
+
version: 1.4.6
|
194
|
+
requirement: *id011
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
prerelease: false
|
197
|
+
type: :runtime
|
198
|
+
name: redis
|
199
|
+
version_requirements: &id012 !ruby/object:Gem::Requirement
|
200
|
+
none: false
|
201
|
+
requirements:
|
202
|
+
- - ">="
|
203
|
+
- !ruby/object:Gem::Version
|
204
|
+
hash: 9
|
205
|
+
segments:
|
206
|
+
- 2
|
207
|
+
- 1
|
208
|
+
- 1
|
209
|
+
version: 2.1.1
|
210
|
+
requirement: *id012
|
69
211
|
- !ruby/object:Gem::Dependency
|
70
|
-
name: time_ext
|
71
212
|
prerelease: false
|
72
|
-
|
213
|
+
type: :runtime
|
214
|
+
name: system_timer
|
215
|
+
version_requirements: &id013 !ruby/object:Gem::Requirement
|
216
|
+
none: false
|
217
|
+
requirements:
|
218
|
+
- - ">="
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
hash: 23
|
221
|
+
segments:
|
222
|
+
- 1
|
223
|
+
- 0
|
224
|
+
- 0
|
225
|
+
version: 1.0.0
|
226
|
+
requirement: *id013
|
227
|
+
- !ruby/object:Gem::Dependency
|
228
|
+
prerelease: false
|
229
|
+
type: :runtime
|
230
|
+
name: time_ext
|
231
|
+
version_requirements: &id014 !ruby/object:Gem::Requirement
|
73
232
|
none: false
|
74
233
|
requirements:
|
75
234
|
- - ">="
|
@@ -80,28 +239,44 @@ dependencies:
|
|
80
239
|
- 2
|
81
240
|
- 6
|
82
241
|
version: 0.2.6
|
83
|
-
|
84
|
-
version_requirements: *id004
|
242
|
+
requirement: *id014
|
85
243
|
- !ruby/object:Gem::Dependency
|
86
|
-
name: rspec
|
87
244
|
prerelease: false
|
88
|
-
|
245
|
+
type: :development
|
246
|
+
name: jeweler
|
247
|
+
version_requirements: &id015 !ruby/object:Gem::Requirement
|
89
248
|
none: false
|
90
249
|
requirements:
|
91
250
|
- - ">="
|
92
251
|
- !ruby/object:Gem::Version
|
93
|
-
hash:
|
252
|
+
hash: 1
|
94
253
|
segments:
|
95
|
-
- 2
|
96
|
-
- 0
|
97
254
|
- 1
|
98
|
-
|
255
|
+
- 5
|
256
|
+
- 1
|
257
|
+
version: 1.5.1
|
258
|
+
requirement: *id015
|
259
|
+
- !ruby/object:Gem::Dependency
|
260
|
+
prerelease: false
|
99
261
|
type: :development
|
100
|
-
|
262
|
+
name: rspec
|
263
|
+
version_requirements: &id016 !ruby/object:Gem::Requirement
|
264
|
+
none: false
|
265
|
+
requirements:
|
266
|
+
- - ">="
|
267
|
+
- !ruby/object:Gem::Version
|
268
|
+
hash: 11
|
269
|
+
segments:
|
270
|
+
- 2
|
271
|
+
- 1
|
272
|
+
- 0
|
273
|
+
version: 2.1.0
|
274
|
+
requirement: *id016
|
101
275
|
- !ruby/object:Gem::Dependency
|
102
|
-
name: yard
|
103
276
|
prerelease: false
|
104
|
-
|
277
|
+
type: :development
|
278
|
+
name: yard
|
279
|
+
version_requirements: &id017 !ruby/object:Gem::Requirement
|
105
280
|
none: false
|
106
281
|
requirements:
|
107
282
|
- - ">="
|
@@ -112,8 +287,7 @@ dependencies:
|
|
112
287
|
- 6
|
113
288
|
- 1
|
114
289
|
version: 0.6.1
|
115
|
-
|
116
|
-
version_requirements: *id006
|
290
|
+
requirement: *id017
|
117
291
|
description: A Redis-backed statistics storage and querying library written in Ruby.
|
118
292
|
email: contact@jimeh.me
|
119
293
|
executables: []
|
@@ -125,7 +299,6 @@ extra_rdoc_files:
|
|
125
299
|
- README.md
|
126
300
|
files:
|
127
301
|
- .document
|
128
|
-
- .gitignore
|
129
302
|
- .rspec
|
130
303
|
- Gemfile
|
131
304
|
- Gemfile.lock
|
@@ -170,8 +343,8 @@ homepage: http://github.com/jimeh/redistat
|
|
170
343
|
licenses: []
|
171
344
|
|
172
345
|
post_install_message:
|
173
|
-
rdoc_options:
|
174
|
-
|
346
|
+
rdoc_options: []
|
347
|
+
|
175
348
|
require_paths:
|
176
349
|
- lib
|
177
350
|
required_ruby_version: !ruby/object:Gem::Requirement
|
data/.gitignore
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
## MAC OS
|
2
|
-
.DS_Store
|
3
|
-
|
4
|
-
## TEXTMATE
|
5
|
-
*.tmproj
|
6
|
-
tmtags
|
7
|
-
|
8
|
-
## EMACS
|
9
|
-
*~
|
10
|
-
\#*
|
11
|
-
.\#*
|
12
|
-
|
13
|
-
## VIM
|
14
|
-
*.swp
|
15
|
-
|
16
|
-
## PROJECT::GENERAL
|
17
|
-
coverage
|
18
|
-
rdoc
|
19
|
-
pkg
|
20
|
-
|
21
|
-
## PROJECT::SPECIFIC
|
22
|
-
.bundle/*
|
23
|
-
.yardoc/*
|
24
|
-
spec/db/*
|
25
|
-
doc
|
26
|
-
redistat.gemspec
|