rudis 0.1

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -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.
@@ -0,0 +1,7 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ Spec::Rake::SpecTask.new('spec') do |t|
5
+ t.spec_files = FileList['spec/**/*_spec.rb']
6
+ t.ruby_opts = ['-r init']
7
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'rudis'))
@@ -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'
@@ -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
@@ -0,0 +1,5 @@
1
+ require_local 'types/default'
2
+ require_local 'types/integer'
3
+ require_local 'types/json'
4
+ require_local 'types/symbol'
5
+ require_local 'types/time'
@@ -0,0 +1,12 @@
1
+ class Rudis
2
+ #sorta boring. nothing to see here.
3
+ class DefaultType
4
+ def self.put(val)
5
+ val
6
+ end
7
+
8
+ def self.get(val)
9
+ val
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ class Rudis
2
+ module IntegerType
3
+ def self.put(val)
4
+ val.to_s
5
+ end
6
+
7
+ def self.get(val)
8
+ val.to_i
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+
2
+ class Rudis
3
+ module JSONType
4
+
5
+ def self.put(val)
6
+ require 'rubygems'
7
+ require 'json'
8
+ val.to_json
9
+ end
10
+
11
+ def self.get(val)
12
+ require 'rubygems'
13
+ require 'json'
14
+ JSON.load(val)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ class Rudis
2
+ module SymbolType
3
+ def self.put(val)
4
+ val.to_s
5
+ end
6
+
7
+ def self.get(val)
8
+ val.to_sym
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ class Rudis
2
+ module TimeType
3
+ def self.put(val)
4
+ val.to_i
5
+ end
6
+
7
+ def self.get(val)
8
+ Time.at(val.to_i)
9
+ end
10
+ end
11
+ end
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,7 @@
1
+ PROJECT_DIR = File.expand_path(File.dirname(__FILE__))
2
+ $:.unshift PROJECT_DIR
3
+
4
+ Dir[File.join(PROJECT_DIR, 'lib/**')].each do |f|
5
+ require f
6
+ end
7
+ $:.shift
@@ -0,0 +1,9 @@
1
+ class Class
2
+ def soft_alias(new, target)
3
+ class_eval <<-code
4
+ def #{new}(*args, &blk)
5
+ #{target}(*args, &blk)
6
+ end
7
+ code
8
+ end
9
+ 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,11 @@
1
+ # I fixed ruby loadpaths
2
+ def require_local(filename)
3
+ require File.expand_path(
4
+ File.join(
5
+ File.dirname(
6
+ caller.first.split(':').first
7
+ ),
8
+ *filename.split('/')
9
+ )
10
+ )
11
+ end
@@ -0,0 +1,11 @@
1
+ class Object
2
+ def metaclass
3
+ class << self
4
+ self
5
+ end
6
+ end
7
+
8
+ def meta_eval(&blk)
9
+ self.metaclass.class_eval(&blk)
10
+ end
11
+ end
@@ -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
+