rudis 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +21 -0
- data/README.md +52 -0
- data/Rakefile +7 -0
- data/init.rb +1 -0
- data/lib/rudis.rb +19 -0
- data/lib/rudis/base.rb +54 -0
- data/lib/rudis/structure.rb +63 -0
- data/lib/rudis/structures/counter.rb +27 -0
- data/lib/rudis/structures/hash.rb +86 -0
- data/lib/rudis/structures/list.rb +71 -0
- data/lib/rudis/structures/lock.rb +61 -0
- data/lib/rudis/structures/set.rb +52 -0
- data/lib/rudis/structures/zset.rb +103 -0
- data/lib/rudis/type.rb +5 -0
- data/lib/rudis/types/default.rb +12 -0
- data/lib/rudis/types/integer.rb +11 -0
- data/lib/rudis/types/json.rb +17 -0
- data/lib/rudis/types/symbol.rb +11 -0
- data/lib/rudis/types/time.rb +11 -0
- data/spec/base_spec.rb +22 -0
- data/spec/counter_spec.rb +17 -0
- data/spec/hash_spec.rb +32 -0
- data/spec/list_spec.rb +23 -0
- data/spec/set_spec.rb +27 -0
- data/spec/zset_spec.rb +40 -0
- data/vendor/core_ext/init.rb +7 -0
- data/vendor/core_ext/lib/class.rb +9 -0
- data/vendor/core_ext/lib/enumerable.rb +23 -0
- data/vendor/core_ext/lib/file.rb +17 -0
- data/vendor/core_ext/lib/hash.rb +46 -0
- data/vendor/core_ext/lib/infinity.rb +1 -0
- data/vendor/core_ext/lib/main.rb +11 -0
- data/vendor/core_ext/lib/object.rb +11 -0
- data/vendor/core_ext/lib/string.rb +32 -0
- metadata +115 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#MIT license. See http://www.opensource.org/licenses/mit-license.php
|
2
|
+
|
3
|
+
Copyright (c) 2010 Philotic, Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
Rudis
|
2
|
+
=====
|
3
|
+
|
4
|
+
Rudis is a simple framework for implementing your favorite Redis recipes in Ruby. There are only two concepts to Rudis:
|
5
|
+
|
6
|
+
Types
|
7
|
+
-----
|
8
|
+
|
9
|
+
A type consists of any object that responds to `put` and `get`.These are used to transparently serialize and unserialize elements of your Redis sets, lists, zsets, and hashes. For example:
|
10
|
+
|
11
|
+
>> s = Rudis::Set.new(:type => Rudis::JSONType)
|
12
|
+
>> s.add [1,2,3,4] # actually adds [1,2,3,4].to_json to the set
|
13
|
+
>> s.add {'foo' => 'bar'} # => '{foo:"bar"}' is added
|
14
|
+
>> s.include? {'foo' => 'bar'}
|
15
|
+
true
|
16
|
+
>> s.to_a
|
17
|
+
[[1,2,3,4], {'foo' => 'bar'}]
|
18
|
+
|
19
|
+
You can write your own types, too!
|
20
|
+
|
21
|
+
class ActiveRecordType
|
22
|
+
def initialize(model)
|
23
|
+
@model = model
|
24
|
+
end
|
25
|
+
def self.put(val)
|
26
|
+
val.id
|
27
|
+
end
|
28
|
+
def self.get(val)
|
29
|
+
@model.find(val.to_i)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Recipes
|
34
|
+
-------
|
35
|
+
|
36
|
+
A recipe is a subclass of `Rudis::Base`. Rudis provides two handy-dandy methods: `key` and `redis`. `redis` is both an instance method and a class method that gives you an instance of Redis (writable, with sensible defaults). `key` is really handy:
|
37
|
+
|
38
|
+
class Foo < Rudis::Base
|
39
|
+
end
|
40
|
+
|
41
|
+
>> Foo.new('foo').key
|
42
|
+
=> "foo"
|
43
|
+
>> Foo.new('foo').key('bar', 'baz')
|
44
|
+
=> "foo:bar:baz"
|
45
|
+
>> Foo.key_sep = '/'
|
46
|
+
>> Foo.new('foo').key('bar', 'baz')
|
47
|
+
=> "foo/bar/baz"
|
48
|
+
>> Foo.key_base = ['foo', 'bar']
|
49
|
+
>> Foo.new('zot').key('zongo')
|
50
|
+
=> "foo/bar/zot/zongo"
|
51
|
+
|
52
|
+
Enjoy! I have lots of TODOs, including better SORT integration, more builtin types (like Marshall), and always more examples. Checkout `examples/` for a few neat examples.
|
data/Rakefile
ADDED
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'rudis'))
|
data/lib/rudis.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#only once
|
2
|
+
require begin
|
3
|
+
File.expand_path(
|
4
|
+
File.join(
|
5
|
+
File.dirname(__FILE__),
|
6
|
+
'..',
|
7
|
+
'vendor',
|
8
|
+
'core_ext',
|
9
|
+
'init'
|
10
|
+
)
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Rudis
|
15
|
+
end
|
16
|
+
|
17
|
+
require_local 'rudis/base'
|
18
|
+
require_local 'rudis/type'
|
19
|
+
require_local 'rudis/structure'
|
data/lib/rudis/base.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
class Rudis
|
2
|
+
class << self
|
3
|
+
attr_writer :redis
|
4
|
+
def redis
|
5
|
+
@redis ||= begin
|
6
|
+
require 'rubygems'
|
7
|
+
require 'redis'
|
8
|
+
Redis.new
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_writer :key_base
|
13
|
+
def key_base
|
14
|
+
@key_base ||= ['rudis']
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_writer :key_sep
|
18
|
+
def key_sep
|
19
|
+
@key_sep ||= ':'
|
20
|
+
end
|
21
|
+
|
22
|
+
def key(*args)
|
23
|
+
([key_base].flatten + args).join(key_sep)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
class Base < Rudis
|
29
|
+
class << self
|
30
|
+
attr_writer :redis
|
31
|
+
def self.redis
|
32
|
+
@redis ||= super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def redis
|
37
|
+
@redis ||= self.class.redis
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(key, options={})
|
41
|
+
@key = key
|
42
|
+
@options = options
|
43
|
+
@options.rmerge!(default_options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_options
|
47
|
+
{}
|
48
|
+
end
|
49
|
+
|
50
|
+
def key(*args)
|
51
|
+
self.class.key(@key, *args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Rudis
|
2
|
+
class Structure < Base
|
3
|
+
def type
|
4
|
+
@options[:type]
|
5
|
+
end
|
6
|
+
|
7
|
+
def default_options
|
8
|
+
{:type => DefaultType}
|
9
|
+
end
|
10
|
+
|
11
|
+
def exists?
|
12
|
+
redis.exists(key)
|
13
|
+
end
|
14
|
+
alias exist? exists?
|
15
|
+
|
16
|
+
def del
|
17
|
+
redis.del(key)
|
18
|
+
end
|
19
|
+
alias delete! del
|
20
|
+
|
21
|
+
def rename(new_key)
|
22
|
+
redis.rename(key, self.class.key(new_key))
|
23
|
+
@key = new_key
|
24
|
+
end
|
25
|
+
|
26
|
+
def redis_type
|
27
|
+
redis.type(key)
|
28
|
+
end
|
29
|
+
|
30
|
+
def expire(time)
|
31
|
+
redis.expire(key, time)
|
32
|
+
end
|
33
|
+
|
34
|
+
def expire_at(time)
|
35
|
+
redis.expire_at(key, time)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ttl
|
39
|
+
redis.ttl(key)
|
40
|
+
end
|
41
|
+
|
42
|
+
def watch(tries=0)
|
43
|
+
return redis.watch(key) unless block_given?
|
44
|
+
|
45
|
+
begin
|
46
|
+
redis.watch(key)
|
47
|
+
c = 0
|
48
|
+
while yield.nil? && (tries==0 || (c+=1) <= tries)
|
49
|
+
puts "Optimistic lock failed for #{key}, retrying #{c} time(s)"
|
50
|
+
end
|
51
|
+
ensure
|
52
|
+
redis.unwatch(key)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
require_local 'structures/counter'
|
59
|
+
require_local 'structures/list'
|
60
|
+
require_local 'structures/lock'
|
61
|
+
require_local 'structures/hash'
|
62
|
+
require_local 'structures/set'
|
63
|
+
require_local 'structures/zset'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Rudis
|
2
|
+
class Counter < Structure
|
3
|
+
def incr
|
4
|
+
redis.incr(key)
|
5
|
+
end
|
6
|
+
|
7
|
+
def decr
|
8
|
+
redis.decr(key)
|
9
|
+
end
|
10
|
+
|
11
|
+
def incrby(i)
|
12
|
+
redis.incrby(key, i.to_i)
|
13
|
+
end
|
14
|
+
|
15
|
+
def decrby(i)
|
16
|
+
redis.decrby(key, i.to_i)
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_i
|
20
|
+
redis.get(key).to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def zero?
|
24
|
+
to_i.zero?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class Rudis
|
2
|
+
class Hash < Structure
|
3
|
+
def default_options
|
4
|
+
{
|
5
|
+
:type => DefaultType,
|
6
|
+
:key_type => DefaultType
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def key_type
|
11
|
+
@options[:key_type]
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(k)
|
15
|
+
e = redis.hget(key, key_type.put(k))
|
16
|
+
e && type.get(e)
|
17
|
+
end
|
18
|
+
alias [] get
|
19
|
+
|
20
|
+
def set(k,v)
|
21
|
+
redis.hset(key, key_type.put(k), type.put(v))
|
22
|
+
end
|
23
|
+
alias []= set
|
24
|
+
|
25
|
+
def mget(*ks)
|
26
|
+
ks.zip(redis.hmget(key, ks.map { |k|
|
27
|
+
key_type.put(k)
|
28
|
+
}).map { |v|
|
29
|
+
type.get(v)
|
30
|
+
}).to_h
|
31
|
+
end
|
32
|
+
alias slice mget
|
33
|
+
|
34
|
+
def mset(hsh)
|
35
|
+
hsh = hsh.dup
|
36
|
+
hsh.map! {|k,v| [key_type.put(k), type.put(v)]}
|
37
|
+
redis.hmset(key, *hsh.to_a.flatten)
|
38
|
+
end
|
39
|
+
alias merge! mset
|
40
|
+
|
41
|
+
def keys
|
42
|
+
redis.hkeys(key).map { |k| key_type.get(k) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def vals
|
46
|
+
redis.hvals(key).map { |v| type.get(v) }
|
47
|
+
end
|
48
|
+
alias values vals
|
49
|
+
|
50
|
+
def all
|
51
|
+
redis.hgetall(key).map! do |k,v|
|
52
|
+
[key_type.get(k), type.get(v)]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
alias to_h all
|
56
|
+
|
57
|
+
def len
|
58
|
+
redis.hlen(key)
|
59
|
+
end
|
60
|
+
alias length len
|
61
|
+
alias count len
|
62
|
+
alias size len
|
63
|
+
|
64
|
+
def empty?
|
65
|
+
len == 0
|
66
|
+
end
|
67
|
+
|
68
|
+
def del(k)
|
69
|
+
redis.hdel(key, key_type.put(k))
|
70
|
+
end
|
71
|
+
|
72
|
+
def has_key?(k)
|
73
|
+
redis.hexists(key, key_type.put(k))
|
74
|
+
end
|
75
|
+
alias include? has_key?
|
76
|
+
|
77
|
+
def incrby(k, i)
|
78
|
+
redis.hincrby(key, key_type.put(k), i.to_i)
|
79
|
+
end
|
80
|
+
|
81
|
+
def incr(k)
|
82
|
+
incrby(k, 1)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class Rudis
|
2
|
+
class List < Structure
|
3
|
+
def len
|
4
|
+
redis.llen(key)
|
5
|
+
end
|
6
|
+
alias length len
|
7
|
+
alias size len
|
8
|
+
alias count len
|
9
|
+
|
10
|
+
def empty?
|
11
|
+
len == 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def index(i)
|
15
|
+
type.get(redis.lindex(key, i.to_i))
|
16
|
+
end
|
17
|
+
|
18
|
+
def range(range)
|
19
|
+
redis.lrange(key, range.first.to_i, range.last.to_i).map do |e|
|
20
|
+
type.get(e)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def all
|
25
|
+
range 0..-1
|
26
|
+
end
|
27
|
+
alias to_a all
|
28
|
+
|
29
|
+
def [](thing)
|
30
|
+
if thing.is_a? Fixnum
|
31
|
+
index thing
|
32
|
+
elsif thing.is_a? Range
|
33
|
+
range thing
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def set(i, val)
|
38
|
+
redis.lset(key, i.to_i, type.put(val))
|
39
|
+
end
|
40
|
+
alias []= set
|
41
|
+
|
42
|
+
def rpush(val)
|
43
|
+
redis.rpush(key, type.put(val))
|
44
|
+
end
|
45
|
+
alias push rpush
|
46
|
+
alias << rpush
|
47
|
+
|
48
|
+
def rpop
|
49
|
+
e = redis.rpop(key)
|
50
|
+
e && type.get(e)
|
51
|
+
end
|
52
|
+
alias pop rpop
|
53
|
+
|
54
|
+
def lpush(val)
|
55
|
+
redis.lpush(key, type.put(val))
|
56
|
+
end
|
57
|
+
alias unshift lpush
|
58
|
+
alias >> lpush
|
59
|
+
|
60
|
+
def lpop
|
61
|
+
e = redis.lpop(key)
|
62
|
+
e && type.get(e)
|
63
|
+
end
|
64
|
+
alias shift lpop
|
65
|
+
|
66
|
+
def trim(range)
|
67
|
+
redis.trim(key, range.first.to_i, range.last.to_i)
|
68
|
+
end
|
69
|
+
alias trim! trim
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Rudis
|
2
|
+
class Lock < Structure
|
3
|
+
class LockFailed < Exception; end
|
4
|
+
|
5
|
+
def acquire(options={})
|
6
|
+
options.rmerge!(
|
7
|
+
:tries => 1,
|
8
|
+
:sleep => 1
|
9
|
+
)
|
10
|
+
|
11
|
+
return set(options) unless block_given?
|
12
|
+
|
13
|
+
1.upto options[:tries] do
|
14
|
+
if set(options)
|
15
|
+
return begin
|
16
|
+
yield
|
17
|
+
ensure
|
18
|
+
clear
|
19
|
+
end
|
20
|
+
end
|
21
|
+
sleep options[:sleep]
|
22
|
+
end
|
23
|
+
|
24
|
+
# oops, we couldn't get the lock
|
25
|
+
raise LockFailed, <<-msg.squish
|
26
|
+
Unable to acquire lock after #{options[:tries]} time(s)
|
27
|
+
msg
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
# implements the SETNX locking algorithm from
|
32
|
+
# http://code.google.com/p/redis/wiki/SetnxCommand
|
33
|
+
def set(options={})
|
34
|
+
options.rmerge!(
|
35
|
+
:timeout => 30
|
36
|
+
)
|
37
|
+
if redis.setnx(key, timestamp(options[:timeout]))
|
38
|
+
return true
|
39
|
+
else
|
40
|
+
# check the timestamp
|
41
|
+
old = redis.getset(key, timestamp(options[:timeout]))
|
42
|
+
if old < timestamp
|
43
|
+
# expired lock, we're good
|
44
|
+
return true
|
45
|
+
else
|
46
|
+
# lock is not expired, put it back
|
47
|
+
redis.set(key, old)
|
48
|
+
return false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
alias clear del
|
54
|
+
|
55
|
+
private
|
56
|
+
def timestamp(timeout=0)
|
57
|
+
Time.now.to_i + timeout
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Rudis
|
2
|
+
class Set < Structure
|
3
|
+
def members
|
4
|
+
mems = redis.smembers(key)
|
5
|
+
mems.map! do |k|
|
6
|
+
type.get(k)
|
7
|
+
end
|
8
|
+
mems
|
9
|
+
end
|
10
|
+
alias all members
|
11
|
+
alias to_a members
|
12
|
+
|
13
|
+
def add(val)
|
14
|
+
redis.sadd(key, type.put(val))
|
15
|
+
end
|
16
|
+
alias << add
|
17
|
+
|
18
|
+
def is_member?(val)
|
19
|
+
redis.sismember(key, type.put(val))
|
20
|
+
end
|
21
|
+
alias member? is_member?
|
22
|
+
alias include? is_member?
|
23
|
+
|
24
|
+
def card
|
25
|
+
redis.scard(key)
|
26
|
+
end
|
27
|
+
alias count card
|
28
|
+
alias size card
|
29
|
+
alias length card
|
30
|
+
|
31
|
+
def rem(val)
|
32
|
+
redis.srem(key, type.put(val))
|
33
|
+
end
|
34
|
+
alias remove rem
|
35
|
+
alias delete rem
|
36
|
+
|
37
|
+
def randmember
|
38
|
+
e = redis.srandmember(key)
|
39
|
+
e && type.get(e)
|
40
|
+
end
|
41
|
+
alias rand randmember
|
42
|
+
|
43
|
+
def pop
|
44
|
+
e = redis.spop(key)
|
45
|
+
e && type.get(e)
|
46
|
+
end
|
47
|
+
|
48
|
+
def sort(*args)
|
49
|
+
#TODO
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class Rudis
|
2
|
+
class ZSet < Structure
|
3
|
+
def default_options
|
4
|
+
{
|
5
|
+
:type => DefaultType,
|
6
|
+
:score_type => IntegerType
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def score_type
|
11
|
+
@options[:score_type]
|
12
|
+
end
|
13
|
+
|
14
|
+
def add(member, score=1)
|
15
|
+
redis.zadd(key, score_type.put(score), type.put(member))
|
16
|
+
end
|
17
|
+
alias << add
|
18
|
+
|
19
|
+
def rem(member)
|
20
|
+
redis.zrem(key, type.put(member))
|
21
|
+
end
|
22
|
+
|
23
|
+
def incrby(member, i)
|
24
|
+
redis.zincrby(key, i.to_i, member)
|
25
|
+
end
|
26
|
+
|
27
|
+
def incr(member)
|
28
|
+
incrby(member, 1)
|
29
|
+
end
|
30
|
+
|
31
|
+
def rank(member)
|
32
|
+
i = redis.zrank(key, member)
|
33
|
+
i && i.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
def card
|
37
|
+
redis.zcard(key)
|
38
|
+
end
|
39
|
+
alias size card
|
40
|
+
alias length card
|
41
|
+
alias count card
|
42
|
+
|
43
|
+
def empty?
|
44
|
+
card == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def range(ran)
|
48
|
+
redis.zrange(key, ran.first.to_i, ran.last.to_i).map do |e|
|
49
|
+
type.get(e)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def revrange(ran)
|
54
|
+
redis.zrevrange(key, ran.first.to_i, ran.last.to_i).map do |e|
|
55
|
+
type.get(e)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
alias rev_range revrange
|
59
|
+
|
60
|
+
def rangebyscore(min, max)
|
61
|
+
redis.zrangebyscore(key,
|
62
|
+
score_type.put(min),
|
63
|
+
score_type.put(max)
|
64
|
+
).map do |e|
|
65
|
+
type.get(e)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
alias range_by_score rangebyscore
|
69
|
+
|
70
|
+
def [](val)
|
71
|
+
if val.is_a? Range
|
72
|
+
range(val)
|
73
|
+
else
|
74
|
+
self[val..val]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
alias slice []
|
78
|
+
|
79
|
+
def all
|
80
|
+
range(0..-1)
|
81
|
+
end
|
82
|
+
alias to_a all
|
83
|
+
|
84
|
+
def first
|
85
|
+
self[0..0].first
|
86
|
+
end
|
87
|
+
|
88
|
+
def last
|
89
|
+
self[-1..-1].first
|
90
|
+
end
|
91
|
+
|
92
|
+
def score(member)
|
93
|
+
s = redis.zscore(key, type.put(member))
|
94
|
+
s && score_type.get(s)
|
95
|
+
end
|
96
|
+
|
97
|
+
def member?(val)
|
98
|
+
!score(val).nil?
|
99
|
+
end
|
100
|
+
alias include? member?
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
data/lib/rudis/type.rb
ADDED
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
describe Rudis::Base do
|
2
|
+
before :all do
|
3
|
+
class Foo < Rudis::Base
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
it "has a writable base key" do
|
8
|
+
foo = Foo.new('foo_key')
|
9
|
+
Foo.key.should == 'rudis'
|
10
|
+
foo.key.should == 'rudis:foo_key'
|
11
|
+
Foo.key_base = ['Foo']
|
12
|
+
Foo.key.should == 'Foo'
|
13
|
+
foo.key.should == 'Foo:foo_key'
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has a writable key separator" do
|
17
|
+
Foo.key_sep = '/'
|
18
|
+
Foo.key_base = ['Foo']
|
19
|
+
foo = Foo.new(:a)
|
20
|
+
foo.key(:b, "c:d:e").should == 'Foo/a/b/c:d:e'
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
describe Rudis::Counter do
|
2
|
+
before :each do
|
3
|
+
Rudis::Counter.redis.flushdb
|
4
|
+
@c = Rudis::Counter.new('my_counter')
|
5
|
+
end
|
6
|
+
|
7
|
+
it "counts!" do
|
8
|
+
@c.to_i.should == 0
|
9
|
+
@c.should be_zero
|
10
|
+
@c.incr.should == 1
|
11
|
+
@c.incr.should == 2
|
12
|
+
@c.decr.should == 1
|
13
|
+
@c.incrby(4).should == 5
|
14
|
+
@c.decrby(2).should == 3
|
15
|
+
@c.incrby(-1).should == 2
|
16
|
+
end
|
17
|
+
end
|
data/spec/hash_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
describe Rudis::Hash do
|
2
|
+
before :each do
|
3
|
+
Rudis::Hash.redis.flushdb
|
4
|
+
@hash = Rudis::Hash.new('myhash',
|
5
|
+
:key_type => Rudis::SymbolType,
|
6
|
+
:type => Rudis::IntegerType
|
7
|
+
)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "implements the hash commands" do
|
11
|
+
@hash.should be_empty
|
12
|
+
@hash.length.should == 0
|
13
|
+
@hash[:foo] = 3
|
14
|
+
@hash.should_not be_empty
|
15
|
+
@hash[:foo].should == 3
|
16
|
+
@hash.all.should == {:foo => 3}
|
17
|
+
@hash[:bar] = 4
|
18
|
+
@hash.keys.to_set.should == Set.new([:foo, :bar])
|
19
|
+
@hash.values.sort.should == [3,4]
|
20
|
+
@hash.count.should == 2
|
21
|
+
@hash.to_h.should == {:foo => 3, :bar => 4}
|
22
|
+
@hash[:foo] = 5
|
23
|
+
@hash[:foo].should == 5
|
24
|
+
@hash.size.should == 2
|
25
|
+
@hash.slice(:foo).should == {:foo => 5}
|
26
|
+
@hash.merge!(:bar => 6, :baz => 7)
|
27
|
+
@hash.count.should == 3
|
28
|
+
@hash.to_h.should == {:foo => 5, :bar => 6, :baz => 7}
|
29
|
+
@hash.get(:baz).should == 7
|
30
|
+
@hash.get(:idontexist).should be_nil
|
31
|
+
end
|
32
|
+
end
|
data/spec/list_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
describe Rudis::List do
|
2
|
+
before :each do
|
3
|
+
Rudis::List.redis.flushdb
|
4
|
+
@list = Rudis::List.new('mylist', :type => Rudis::IntegerType)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "implements the list commands" do
|
8
|
+
@list.size.should == 0
|
9
|
+
@list << 1
|
10
|
+
@list.to_a.should == [1]
|
11
|
+
@list.unshift 2
|
12
|
+
@list.count.should == 2
|
13
|
+
@list.all.should == [2,1]
|
14
|
+
@list.lpush 3
|
15
|
+
@list.shift.should == 3
|
16
|
+
@list.pop.should == 1
|
17
|
+
@list.lpop.should == 2
|
18
|
+
@list.length.should == 0
|
19
|
+
@list.rpop.should be_nil
|
20
|
+
@list.should be_empty
|
21
|
+
@list.to_a.should == []
|
22
|
+
end
|
23
|
+
end
|
data/spec/set_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
describe Rudis::Set do
|
2
|
+
before :each do
|
3
|
+
@key = (1..rand(5)).map { Time.now.hash.to_s }
|
4
|
+
@set = Rudis::Set.new(@key)
|
5
|
+
end
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
Rudis::Set.redis.flushdb
|
9
|
+
end
|
10
|
+
|
11
|
+
it "implements the set commands" do
|
12
|
+
@set.key.should == "rudis:#{@key.join(':')}"
|
13
|
+
@set.card.should == 0
|
14
|
+
@set.add "foo"
|
15
|
+
@set.size.should == 1
|
16
|
+
@set << "bar"
|
17
|
+
@set.count.should == 2
|
18
|
+
@set.delete "foo"
|
19
|
+
@set.size.should == 1
|
20
|
+
@set.to_a.should == ["bar"]
|
21
|
+
@set.rand.should == "bar"
|
22
|
+
@set.pop.should == "bar"
|
23
|
+
@set.to_a.should == []
|
24
|
+
@set.pop.should be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/spec/zset_spec.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
describe Rudis::ZSet do
|
2
|
+
before :each do
|
3
|
+
Rudis::ZSet.redis.flushdb
|
4
|
+
@zset = Rudis::ZSet.new('my_zset',
|
5
|
+
:type => Rudis::JSONType,
|
6
|
+
:score_type => Rudis::TimeType
|
7
|
+
)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "implements the zset commands" do
|
11
|
+
now = Time.now
|
12
|
+
yesterday = now - 60*60*24
|
13
|
+
tomorrow = now + 60*60*24
|
14
|
+
@zset.should be_empty
|
15
|
+
@zset.count.should == 0
|
16
|
+
@zset.add([1,2,3], now)
|
17
|
+
@zset.length.should == 1
|
18
|
+
@zset.should_not be_empty
|
19
|
+
@zset.first.should == [1,2,3]
|
20
|
+
@zset.add({'four' => 4}, yesterday)
|
21
|
+
@zset.size.should == 2
|
22
|
+
@zset.add(['five' => 5, 'six' => 6], tomorrow)
|
23
|
+
@zset.all.should == [
|
24
|
+
{'four' => 4},
|
25
|
+
[1,2,3],
|
26
|
+
['five' => 5, 'six' => 6]
|
27
|
+
]
|
28
|
+
@zset.revrange(0..-2).should == [
|
29
|
+
['five' => 5, 'six' => 6],
|
30
|
+
[1,2,3]
|
31
|
+
]
|
32
|
+
@zset.range_by_score(yesterday, now).should == [
|
33
|
+
{'four' => 4},
|
34
|
+
[1,2,3]
|
35
|
+
]
|
36
|
+
@zset.score("idontexist").should be_nil
|
37
|
+
@zset.should_not include("idontexist")
|
38
|
+
@zset.score({'four' => 4}).to_i.should == yesterday.to_i
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Enumerable
|
2
|
+
def to_set
|
3
|
+
require 'set'
|
4
|
+
Set.new(self)
|
5
|
+
end
|
6
|
+
|
7
|
+
# assumes a collection of k-v pairs
|
8
|
+
def to_h
|
9
|
+
Hash[self]
|
10
|
+
end
|
11
|
+
|
12
|
+
def histogram
|
13
|
+
hist = Hash.new(0)
|
14
|
+
self.each do |e|
|
15
|
+
hist[e] += 1
|
16
|
+
end
|
17
|
+
hist
|
18
|
+
end
|
19
|
+
|
20
|
+
def hashmap
|
21
|
+
map { [self, yield(self)] }.to_h
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class File
|
2
|
+
def self.write(filename, content)
|
3
|
+
open(filename, "w") do |f|
|
4
|
+
f.write(content)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.append(filename, content)
|
9
|
+
open(filename, "a") do |f|
|
10
|
+
f << content
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.add_line(filename, content)
|
15
|
+
self.append(filename, content.chomp + $/)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Hash
|
2
|
+
def rmerge!(hsh)
|
3
|
+
hsh.each do |k,v|
|
4
|
+
self[k] = v unless self.has_key? k
|
5
|
+
end
|
6
|
+
self
|
7
|
+
end
|
8
|
+
|
9
|
+
def rmerge(hsh)
|
10
|
+
self.dup.rmerge!(hsh)
|
11
|
+
end
|
12
|
+
|
13
|
+
def accept_options!(hsh)
|
14
|
+
opts_diff = self.keys - hash.keys
|
15
|
+
raise ArgumentError <<-msg.squish unless opts_diff.empty?
|
16
|
+
Unrecognized options #{opts_diff.inspect}
|
17
|
+
msg
|
18
|
+
options.rmerge!(hsh)
|
19
|
+
end
|
20
|
+
|
21
|
+
def accept_options(hsh)
|
22
|
+
self.dup.accept_options!(hsh)
|
23
|
+
end
|
24
|
+
|
25
|
+
def map_keys!
|
26
|
+
keys.each do |k|
|
27
|
+
self[yield(k)] = self.delete(k)
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def map_values!
|
33
|
+
self.each do |k,v|
|
34
|
+
self[k] = v
|
35
|
+
end
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def map!
|
40
|
+
self.keys.each do |k|
|
41
|
+
new_k, new_v = yield(k, self.delete(k))
|
42
|
+
self[new_k] = new_v
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
::Infinity = 1.0/0
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class String
|
2
|
+
def match?(regex)
|
3
|
+
self.match(regex) ? true : false
|
4
|
+
end
|
5
|
+
|
6
|
+
def squish!
|
7
|
+
self.strip!.gsub!(/\s+/,' ')
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def squish
|
12
|
+
self.dup.squish!
|
13
|
+
end
|
14
|
+
|
15
|
+
def lines
|
16
|
+
self.split($/)
|
17
|
+
end
|
18
|
+
|
19
|
+
def map_parts(delim=$/, &blk)
|
20
|
+
self.split(delim).map(&blk).join(delim)
|
21
|
+
end
|
22
|
+
|
23
|
+
def unchomp!(ch=$/)
|
24
|
+
self.chomp!(ch)
|
25
|
+
self << ch
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def unchomp(ch)
|
30
|
+
self.dup.unchomp!(ch)
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rudis
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Jay Adkisson
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-20 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: redis
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 0
|
32
|
+
version: "2.0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: " Rudis wraps redis-rb in objects that keep track of their own\n redis instances and keys.\n"
|
36
|
+
email: j4yferd@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/rudis/structures/set.rb
|
45
|
+
- lib/rudis/structures/counter.rb
|
46
|
+
- lib/rudis/structures/zset.rb
|
47
|
+
- lib/rudis/structures/list.rb
|
48
|
+
- lib/rudis/structures/hash.rb
|
49
|
+
- lib/rudis/structures/lock.rb
|
50
|
+
- lib/rudis/base.rb
|
51
|
+
- lib/rudis/structure.rb
|
52
|
+
- lib/rudis/type.rb
|
53
|
+
- lib/rudis/types/default.rb
|
54
|
+
- lib/rudis/types/integer.rb
|
55
|
+
- lib/rudis/types/symbol.rb
|
56
|
+
- lib/rudis/types/json.rb
|
57
|
+
- lib/rudis/types/time.rb
|
58
|
+
- lib/rudis.rb
|
59
|
+
- spec/base_spec.rb
|
60
|
+
- spec/counter_spec.rb
|
61
|
+
- spec/set_spec.rb
|
62
|
+
- spec/list_spec.rb
|
63
|
+
- spec/hash_spec.rb
|
64
|
+
- spec/zset_spec.rb
|
65
|
+
- vendor/core_ext/init.rb
|
66
|
+
- vendor/core_ext/lib/string.rb
|
67
|
+
- vendor/core_ext/lib/file.rb
|
68
|
+
- vendor/core_ext/lib/object.rb
|
69
|
+
- vendor/core_ext/lib/enumerable.rb
|
70
|
+
- vendor/core_ext/lib/infinity.rb
|
71
|
+
- vendor/core_ext/lib/main.rb
|
72
|
+
- vendor/core_ext/lib/hash.rb
|
73
|
+
- vendor/core_ext/lib/class.rb
|
74
|
+
- init.rb
|
75
|
+
- Rakefile
|
76
|
+
- README.md
|
77
|
+
- LICENSE
|
78
|
+
has_rdoc: true
|
79
|
+
homepage: http://github.com/jayferd/rudis
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 57
|
93
|
+
segments:
|
94
|
+
- 1
|
95
|
+
- 8
|
96
|
+
- 7
|
97
|
+
version: 1.8.7
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
requirements: []
|
108
|
+
|
109
|
+
rubyforge_project:
|
110
|
+
rubygems_version: 1.3.7
|
111
|
+
signing_key:
|
112
|
+
specification_version: 3
|
113
|
+
summary: An extensible OO redis client for ruby
|
114
|
+
test_files: []
|
115
|
+
|