redisrank 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +297 -0
- data/Rakefile +69 -0
- data/lib/redisrank.rb +106 -0
- data/lib/redisrank/buffer.rb +110 -0
- data/lib/redisrank/collection.rb +20 -0
- data/lib/redisrank/connection.rb +89 -0
- data/lib/redisrank/core_ext.rb +5 -0
- data/lib/redisrank/core_ext/bignum.rb +8 -0
- data/lib/redisrank/core_ext/date.rb +8 -0
- data/lib/redisrank/core_ext/fixnum.rb +8 -0
- data/lib/redisrank/core_ext/hash.rb +20 -0
- data/lib/redisrank/core_ext/time.rb +3 -0
- data/lib/redisrank/date.rb +88 -0
- data/lib/redisrank/event.rb +98 -0
- data/lib/redisrank/finder.rb +245 -0
- data/lib/redisrank/finder/date_set.rb +99 -0
- data/lib/redisrank/key.rb +84 -0
- data/lib/redisrank/label.rb +69 -0
- data/lib/redisrank/mixins/database.rb +11 -0
- data/lib/redisrank/mixins/date_helper.rb +8 -0
- data/lib/redisrank/mixins/options.rb +41 -0
- data/lib/redisrank/mixins/synchronize.rb +52 -0
- data/lib/redisrank/model.rb +77 -0
- data/lib/redisrank/result.rb +18 -0
- data/lib/redisrank/scope.rb +18 -0
- data/lib/redisrank/summary.rb +90 -0
- data/lib/redisrank/version.rb +3 -0
- data/redisrank.gemspec +31 -0
- data/spec/Find Results +3349 -0
- data/spec/buffer_spec.rb +104 -0
- data/spec/collection_spec.rb +20 -0
- data/spec/connection_spec.rb +67 -0
- data/spec/core_ext/hash_spec.rb +26 -0
- data/spec/database_spec.rb +10 -0
- data/spec/date_spec.rb +95 -0
- data/spec/event_spec.rb +86 -0
- data/spec/finder/date_set_spec.rb +527 -0
- data/spec/finder_spec.rb +205 -0
- data/spec/key_spec.rb +129 -0
- data/spec/label_spec.rb +86 -0
- data/spec/model_helper.rb +31 -0
- data/spec/model_spec.rb +191 -0
- data/spec/options_spec.rb +36 -0
- data/spec/redis-test.conf +9 -0
- data/spec/result_spec.rb +23 -0
- data/spec/scope_spec.rb +27 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/summary_spec.rb +177 -0
- data/spec/synchronize_spec.rb +125 -0
- data/spec/thread_safety_spec.rb +39 -0
- metadata +235 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 750cd449e44719036541e0fd53226339bc06da26
|
4
|
+
data.tar.gz: 808d6302c856ae4f132da7420e6c45434b94819f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 412112104821d68d5603ff6df2a629b519c61f4286cbecd10896b273bbea978b334df366b63e77cf77a613cbfbc68cc27f890f63623abf62b29a4b19a474fa96
|
7
|
+
data.tar.gz: 3e35841285088e42ffc6ba54b753aa17d9e147fef79d0639384c0dfeb072dc1f58a6c88e4419e51a7ed0129d896c880d56a7aef8d02d4ec69a553f420dfd57d4
|
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,27 @@
|
|
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
|
+
*.gem
|
21
|
+
.bundle
|
22
|
+
Gemfile.lock
|
23
|
+
|
24
|
+
## PROJECT::SPECIFIC
|
25
|
+
.yardoc/*
|
26
|
+
spec/db/*
|
27
|
+
doc/*
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Jim Myhrberg.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
A Redis-backed statistics storage and querying library written in Ruby.
|
2
|
+
|
3
|
+
Redisrank was created taking as reference a Gem called Redistat by Jimeh.
|
4
|
+
The motivations for the gem creation were similar to the Redistat too, I had a
|
5
|
+
collection solution which was MySQL-based with the following requirements.
|
6
|
+
|
7
|
+
* Fetch the top most ...
|
8
|
+
* This ranks should be fetched in any time-range
|
9
|
+
* Screamingly fast
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
gem install redisrank
|
14
|
+
|
15
|
+
If you are using Ruby 1.8.x, it's recommended you also install the
|
16
|
+
`SystemTimer` gem, as the Redis gem will otherwise complain.
|
17
|
+
|
18
|
+
## Usage (Crash Course)
|
19
|
+
|
20
|
+
view\_stats.rb:
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
require 'redisrank'
|
24
|
+
|
25
|
+
class ViewRank
|
26
|
+
include Redisrank::Model
|
27
|
+
end
|
28
|
+
|
29
|
+
# if using Redisrank in multiple threads set this
|
30
|
+
# somewhere in the beginning of the execution stack
|
31
|
+
Redisrank.thread_safe = true
|
32
|
+
```
|
33
|
+
|
34
|
+
|
35
|
+
### Simple Example
|
36
|
+
|
37
|
+
Store:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
ViewRank.store('hello', {:world => 4})
|
41
|
+
ViewRank.store('hello', {:world => 2}, 2.hours.ago)
|
42
|
+
```
|
43
|
+
|
44
|
+
Fetch:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
ViewRank.find('hello', 1.hour.ago, 1.hour.from_now).all
|
48
|
+
#=> [{'world' => 4}]
|
49
|
+
ViewRank.find('hello', 3.hour.ago, 1.hour.from_now).rank
|
50
|
+
#=> {'world' => 4}
|
51
|
+
ViewRank.find('hello', 3.hour.ago, 1.hour.ago).rank
|
52
|
+
#=> {'world' => 2}
|
53
|
+
```
|
54
|
+
|
55
|
+
### Other usefull Use Cases
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
## Terminology
|
60
|
+
|
61
|
+
### Scope
|
62
|
+
|
63
|
+
A type of global-namespace for storing data. When using the `Redisrank::Model`
|
64
|
+
wrapper, the scope is automatically set to the class name. In the examples
|
65
|
+
above, the scope is `ViewRank`. Can be overridden by calling the `#scope`
|
66
|
+
class method on your model class.
|
67
|
+
|
68
|
+
### Label
|
69
|
+
|
70
|
+
Identifier string to separate different types and groups of statistics from
|
71
|
+
each other. The first argument of the `#store`, `#find`, and `#fetch` methods
|
72
|
+
is the label that you're storing to, or fetching from.
|
73
|
+
|
74
|
+
Labels support multiple grouping levels by splitting the label string with `/`
|
75
|
+
and storing the same stats for each level. For example, when storing data to a
|
76
|
+
label called `views/product/44`, the data is stored for the label you specify,
|
77
|
+
and also for `views/product` and `views`. You may also configure a different
|
78
|
+
group separator using the `Redisrank.group_separator=` method. For example:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
Redisrank.group_separator = '|'
|
82
|
+
```
|
83
|
+
|
84
|
+
A word of caution: Don't use a crazy number of group levels. As two levels
|
85
|
+
causes twice as many `hincrby` calls to Redis as not using the grouping
|
86
|
+
feature. Hence using 10 grouping levels, causes 10 times as many write calls
|
87
|
+
to Redis.
|
88
|
+
|
89
|
+
### Input Statistics Data
|
90
|
+
|
91
|
+
You provide Redisrank with the data you want to store using a Ruby Hash. This
|
92
|
+
data is then stored in a corresponding Redis hash with identical key/field
|
93
|
+
names.
|
94
|
+
|
95
|
+
Key names in the hash also support grouping features similar to those
|
96
|
+
available for Labels. Again, the more levels you use, the more write calls to
|
97
|
+
Redis, so avoid using 10-15 levels.
|
98
|
+
|
99
|
+
### Depth (Storage Accuracy)
|
100
|
+
|
101
|
+
Define how accurately data should be stored, and how accurately it's looked up
|
102
|
+
when fetching it again. By default Redisrank uses a depth value of `:hour`,
|
103
|
+
which means it's impossible to separate two events which were stored at 10:18
|
104
|
+
and 10:23. In Redis they are both stored within a date key of `2011031610`.
|
105
|
+
|
106
|
+
You can set depth within your model using the `#depth` class method. Available
|
107
|
+
depths are: `:year`, `:month`, `:day`, `:hour`, `:min`, `:sec`
|
108
|
+
|
109
|
+
### Time Ranges
|
110
|
+
|
111
|
+
When you fetch data, you need to specify a start and an end time. The
|
112
|
+
selection behavior can seem a bit weird at first when, but makes sense when
|
113
|
+
you understand how Redisrank works internally.
|
114
|
+
|
115
|
+
For example, if we are using a Depth value of `:hour`, and we trigger a fetch
|
116
|
+
call starting at `1.hour.ago` (13:34), till `Time.now` (14:34), only stats
|
117
|
+
from 13:00:00 till 13:59:59 are returned, as they were all stored within the
|
118
|
+
key for the 13th hour. If both 13:00 and 14:00 was returned, you would get
|
119
|
+
results from two whole hours. Hence if you want up to the second data, use an
|
120
|
+
end time of `1.hour.from_now`.
|
121
|
+
|
122
|
+
### The Finder Object
|
123
|
+
|
124
|
+
Calling the `#find` method on a Redisrank model class returns a
|
125
|
+
`Redisrank::Finder` object. The finder is a lazy-loaded gateway to your
|
126
|
+
data. Meaning you can create a new finder, and modify instantiated finder's
|
127
|
+
label, scope, dates, and more. It does not call Redis and fetch the data until
|
128
|
+
you call `#total`, `#all`, `#map`, `#each`, or `#each_with_index` on the
|
129
|
+
finder.
|
130
|
+
|
131
|
+
This section does need further expanding as there's a lot to cover when it
|
132
|
+
comes to the finder.
|
133
|
+
|
134
|
+
|
135
|
+
## Key Expiry
|
136
|
+
|
137
|
+
Support for expiring keys from Redis is available, allowing you too keep
|
138
|
+
varying levels of details for X period of time. This allows you easily keep
|
139
|
+
things nice and tidy by only storing varying levels detailed stats only for as
|
140
|
+
long as you need.
|
141
|
+
|
142
|
+
In the below example we define how long Redis keys for varying depths are
|
143
|
+
stored. Second by second stats are available for 10 minutes, minute by minute
|
144
|
+
stats for 6 hours, hourly stats for 3 months, daily stats for 2 years, and
|
145
|
+
yearly stats are retained forever.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
class ViewRank
|
149
|
+
include Redisrank::Model
|
150
|
+
|
151
|
+
depth :sec
|
152
|
+
|
153
|
+
expire \
|
154
|
+
:sec => 10.minutes.to_i,
|
155
|
+
:min => 6.hours.to_i,
|
156
|
+
:hour => 3.months.to_i,
|
157
|
+
:day => 2.years.to_i
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
Keep in mind that when storing stats for a custom date in the past for
|
162
|
+
example, the expiry time for the keys will be relative to now. The values you
|
163
|
+
specify are simply passed to the `Redis#expire` method.
|
164
|
+
|
165
|
+
|
166
|
+
## Internals
|
167
|
+
|
168
|
+
### Storing / Writing
|
169
|
+
|
170
|
+
Redisrank stores all data into a Redis hash keys. The Redis key name the used
|
171
|
+
consists of three parts. The scope, label, and datetime:
|
172
|
+
|
173
|
+
{scope}/{label}:{datetime}
|
174
|
+
|
175
|
+
For example, this...
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
ViewRank.store('views/product/44', {'count/chrome/11' => 1})
|
179
|
+
```
|
180
|
+
|
181
|
+
...would store the follow hash of data...
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
{ 'count' => 1, 'count/chrome' => 1, 'count/chrome/11' => 1 }
|
185
|
+
```
|
186
|
+
|
187
|
+
...to all 12 of these Redis hash keys...
|
188
|
+
|
189
|
+
ViewRank/views:2011
|
190
|
+
ViewRank/views:201103
|
191
|
+
ViewRank/views:20110315
|
192
|
+
ViewRank/views:2011031510
|
193
|
+
ViewRank/views/product:2011
|
194
|
+
ViewRank/views/product:201103
|
195
|
+
ViewRank/views/product:20110315
|
196
|
+
ViewRank/views/product:2011031510
|
197
|
+
ViewRank/views/product/44:2011
|
198
|
+
ViewRank/views/product/44:201103
|
199
|
+
ViewRank/views/product/44:20110315
|
200
|
+
ViewRank/views/product/44:2011031510
|
201
|
+
|
202
|
+
...by creating the Redis key, and/or hash field if needed, otherwise it simply
|
203
|
+
increments the already existing data.
|
204
|
+
|
205
|
+
It would also create the following Redis sets to keep track of which child
|
206
|
+
labels are available:
|
207
|
+
|
208
|
+
ViewRank.label_index:
|
209
|
+
ViewRank.label_index:views
|
210
|
+
ViewRank.label_index:views/product
|
211
|
+
|
212
|
+
It should now be more obvious to you why you should think about how you use
|
213
|
+
the grouping capabilities so you don't go crazy and use 10-15 levels. Storing
|
214
|
+
is done through Redis' `hincrby` call, which only supports a single key/field
|
215
|
+
combo. Meaning the above example would call `hincrby` a total of 36 times to
|
216
|
+
store the data, and `sadd` a total of 3 times to ensure the label index is
|
217
|
+
accurate. 39 calls is however not a problem for Redis, most calls happen in
|
218
|
+
less than 0.15ms (0.00015 seconds) on my local machine.
|
219
|
+
|
220
|
+
|
221
|
+
### Fetching / Reading
|
222
|
+
|
223
|
+
By default when fetching statistics, Redisrank will figure out how to do the
|
224
|
+
least number of reads from Redis. First it checks how long range you're
|
225
|
+
fetching. If whole days, months or years for example fit within the start and
|
226
|
+
end dates specified, it will fetch the one key for the day/month/year in
|
227
|
+
question. It further drills down to the smaller units.
|
228
|
+
|
229
|
+
It is also intelligent enough to not fetch each day from 3-31 of a month,
|
230
|
+
instead it would fetch the data for the whole month and the first two days,
|
231
|
+
which are then removed from the summary of the whole month. This means three
|
232
|
+
calls to `hgetall` instead of 29 if each whole day was fetched.
|
233
|
+
|
234
|
+
### Buffer
|
235
|
+
|
236
|
+
The buffer is a new, still semi-beta, feature aimed to reduce the number of
|
237
|
+
Redis `hincrby` that Redisrank sends. This should only really be useful when
|
238
|
+
you're hitting north of 30,000 Redis requests per second, if your Redis server
|
239
|
+
has limited resources, or against my recommendation you've opted to use 10,
|
240
|
+
20, or more label grouping levels.
|
241
|
+
|
242
|
+
Buffering tries to fold together multiple `store` calls into as few as
|
243
|
+
possible by merging the statistics hashes from all calls and groups them based
|
244
|
+
on scope, label, date depth, and more. You configure the the buffer by setting
|
245
|
+
`Redisrank.buffer_size` to an integer higher than 1. This basically tells
|
246
|
+
Redisrank how many `store` calls to buffer in memory before writing all data to
|
247
|
+
Redis.
|
248
|
+
|
249
|
+
|
250
|
+
## Todo
|
251
|
+
|
252
|
+
* More details in Readme.
|
253
|
+
* Documentation.
|
254
|
+
* Anything else that becomes apparent after real-world use.
|
255
|
+
|
256
|
+
|
257
|
+
## Credits
|
258
|
+
|
259
|
+
[Encore Alert](http://encorealert.com/) that allowed me to spend some
|
260
|
+
company time to further develop the project. @jimeh for creating the Redistat
|
261
|
+
that was not a inspiration but the base version of Redisrank.
|
262
|
+
|
263
|
+
|
264
|
+
## Note on Patches/Pull Requests
|
265
|
+
|
266
|
+
* Fork the project.
|
267
|
+
* Make your feature addition or bug fix.
|
268
|
+
* Add tests for it. This is important so I don't break it in a
|
269
|
+
future version unintentionally.
|
270
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to
|
271
|
+
have your own version, that is fine but bump version in a commit by itself I
|
272
|
+
can ignore when I pull)
|
273
|
+
* Send me a pull request. Bonus points for topic branches.
|
274
|
+
|
275
|
+
|
276
|
+
## License and Copyright
|
277
|
+
|
278
|
+
Copyright (c) 2011 Jim Myhrberg.
|
279
|
+
|
280
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
281
|
+
a copy of this software and associated documentation files (the
|
282
|
+
"Software"), to deal in the Software without restriction, including
|
283
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
284
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
285
|
+
permit persons to whom the Software is furnished to do so, subject to
|
286
|
+
the following conditions:
|
287
|
+
|
288
|
+
The above copyright notice and this permission notice shall be
|
289
|
+
included in all copies or substantial portions of the Software.
|
290
|
+
|
291
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
292
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
293
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
294
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
295
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
296
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
297
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
|
5
|
+
#
|
6
|
+
# Rspec
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
11
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
15
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
16
|
+
spec.rcov = true
|
17
|
+
spec.rcov_opts = ['--exclude', 'spec']
|
18
|
+
end
|
19
|
+
|
20
|
+
task :default => [:start, :spec, :stop]
|
21
|
+
|
22
|
+
|
23
|
+
#
|
24
|
+
# Start/stop Redis test server
|
25
|
+
#
|
26
|
+
|
27
|
+
REDIS_DIR = File.expand_path(File.join("..", "spec"), __FILE__)
|
28
|
+
REDIS_CNF = File.join(REDIS_DIR, "redis-test.conf")
|
29
|
+
REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
|
30
|
+
|
31
|
+
desc "Start the Redis test server"
|
32
|
+
task :start do
|
33
|
+
unless File.exists?(REDIS_PID)
|
34
|
+
system "redis-server #{REDIS_CNF}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Stop the Redis test server"
|
39
|
+
task :stop do
|
40
|
+
if File.exists?(REDIS_PID)
|
41
|
+
system "kill #{File.read(REDIS_PID)}"
|
42
|
+
system "rm #{REDIS_PID}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
#
|
48
|
+
# Yard
|
49
|
+
#
|
50
|
+
|
51
|
+
begin
|
52
|
+
require 'yard'
|
53
|
+
YARD::Rake::YardocTask.new
|
54
|
+
rescue LoadError
|
55
|
+
task :yardoc do
|
56
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
#
|
62
|
+
# Misc.
|
63
|
+
#
|
64
|
+
|
65
|
+
desc "Start an irb console with Redisrank pre-loaded."
|
66
|
+
task :console do
|
67
|
+
exec "irb -r spec/spec_helper"
|
68
|
+
end
|
69
|
+
task :c => :console
|
data/lib/redisrank.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'date'
|
4
|
+
require 'time'
|
5
|
+
require 'digest/sha1'
|
6
|
+
require 'monitor'
|
7
|
+
|
8
|
+
# Active Support 2.x or 3.x
|
9
|
+
require 'active_support'
|
10
|
+
if !{}.respond_to?(:with_indifferent_access)
|
11
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
12
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'time_ext'
|
16
|
+
require 'redis'
|
17
|
+
require 'json'
|
18
|
+
|
19
|
+
require 'redisrank/mixins/options'
|
20
|
+
require 'redisrank/mixins/synchronize'
|
21
|
+
require 'redisrank/mixins/database'
|
22
|
+
require 'redisrank/mixins/date_helper'
|
23
|
+
|
24
|
+
require 'redisrank/connection'
|
25
|
+
require 'redisrank/buffer'
|
26
|
+
require 'redisrank/collection'
|
27
|
+
require 'redisrank/date'
|
28
|
+
require 'redisrank/event'
|
29
|
+
require 'redisrank/finder'
|
30
|
+
require 'redisrank/key'
|
31
|
+
require 'redisrank/label'
|
32
|
+
require 'redisrank/model'
|
33
|
+
require 'redisrank/result'
|
34
|
+
require 'redisrank/scope'
|
35
|
+
require 'redisrank/summary'
|
36
|
+
require 'redisrank/version'
|
37
|
+
|
38
|
+
require 'redisrank/core_ext'
|
39
|
+
|
40
|
+
|
41
|
+
module Redisrank
|
42
|
+
|
43
|
+
KEY_NEXT_ID = ".next_id"
|
44
|
+
KEY_EVENT = ".event:"
|
45
|
+
KEY_LABELS = "Redisrank.labels:" # used for reverse label hash lookup
|
46
|
+
KEY_EVENT_IDS = ".event_ids"
|
47
|
+
LABEL_INDEX = ".label_index:"
|
48
|
+
GROUP_SEPARATOR = "/"
|
49
|
+
|
50
|
+
class InvalidOptions < ArgumentError; end
|
51
|
+
class RedisServerIsTooOld < Exception; end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
|
55
|
+
def buffer
|
56
|
+
Buffer.instance
|
57
|
+
end
|
58
|
+
|
59
|
+
def buffer_size
|
60
|
+
buffer.size
|
61
|
+
end
|
62
|
+
|
63
|
+
def buffer_size=(size)
|
64
|
+
buffer.size = size
|
65
|
+
end
|
66
|
+
|
67
|
+
def thread_safe
|
68
|
+
Synchronize.thread_safe
|
69
|
+
end
|
70
|
+
|
71
|
+
def thread_safe=(value)
|
72
|
+
Synchronize.thread_safe = value
|
73
|
+
end
|
74
|
+
|
75
|
+
def connection(ref = nil)
|
76
|
+
Connection.get(ref)
|
77
|
+
end
|
78
|
+
alias :redis :connection
|
79
|
+
|
80
|
+
def connection=(connection)
|
81
|
+
Connection.add(connection)
|
82
|
+
end
|
83
|
+
alias :redis= :connection=
|
84
|
+
|
85
|
+
def connect(options)
|
86
|
+
Connection.create(options)
|
87
|
+
end
|
88
|
+
|
89
|
+
def flush
|
90
|
+
puts "WARNING: Redisrank.flush is deprecated. Use Redisrank.redis.flushdb instead."
|
91
|
+
connection.flushdb
|
92
|
+
end
|
93
|
+
|
94
|
+
def group_separator
|
95
|
+
@group_separator ||= GROUP_SEPARATOR
|
96
|
+
end
|
97
|
+
attr_writer :group_separator
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
# ensure buffer is flushed on program exit
|
104
|
+
Kernel.at_exit do
|
105
|
+
Redisrank.buffer.flush(true)
|
106
|
+
end
|