redis-roc 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/redis-roc.rb +2 -0
- data/lib/roc/ext/redis_ext.rb +20 -0
- data/lib/roc/objects/base.rb +81 -0
- data/lib/roc/objects/float.rb +32 -0
- data/lib/roc/objects/hash.rb +134 -0
- data/lib/roc/objects/integer.rb +51 -0
- data/lib/roc/objects/list.rb +204 -0
- data/lib/roc/objects/lock.rb +53 -0
- data/lib/roc/objects/set.rb +148 -0
- data/lib/roc/objects/sorted_set.rb +217 -0
- data/lib/roc/objects/string.rb +116 -0
- data/lib/roc/objects/time.rb +62 -0
- data/lib/roc/objects.rb +11 -0
- data/lib/roc/store/object_initializers.rb +74 -0
- data/lib/roc/store/redis_eval.rb +7 -0
- data/lib/roc/store/redis_store.rb +76 -0
- data/lib/roc/store/roc_store.rb +11 -0
- data/lib/roc/store/transient_eval.rb +42 -0
- data/lib/roc/store/transient_store.rb +1425 -0
- data/lib/roc/store.rb +2 -0
- data/lib/roc/types/all_types.rb +41 -0
- data/lib/roc/types/array_type.rb +106 -0
- data/lib/roc/types/method_generators.rb +38 -0
- data/lib/roc/types/scalar_type.rb +58 -0
- data/lib/roc/types/sortable_type.rb +32 -0
- data/lib/roc/version.rb +3 -0
- metadata +114 -0
data/lib/redis-roc.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class Redis
|
2
|
+
|
3
|
+
def zrevrangebyscore(key, max, min, options = {})
|
4
|
+
command = CommandOptions.new(options) do |c|
|
5
|
+
c.splat :limit
|
6
|
+
c.bool :with_scores
|
7
|
+
end
|
8
|
+
|
9
|
+
@client.call(:zrevrangebyscore, key, max, min, *command.to_a)
|
10
|
+
end
|
11
|
+
|
12
|
+
def del(*keys)
|
13
|
+
_bool @client.call(:del, *keys)
|
14
|
+
end
|
15
|
+
|
16
|
+
def hdel(key, field)
|
17
|
+
_bool @client.call(:hdel, key, field)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
if RUBY_VERSION.match(/^1\.8/)
|
2
|
+
require 'rubygems'
|
3
|
+
end
|
4
|
+
require 'cim_attributes'
|
5
|
+
|
6
|
+
require 'roc/types/all_types'
|
7
|
+
|
8
|
+
module ROC
|
9
|
+
class Base
|
10
|
+
include ROC::Types::AllTypes
|
11
|
+
include CIMAttributes
|
12
|
+
|
13
|
+
attr_reader :key, :options
|
14
|
+
|
15
|
+
cim_attr_accessor :storage
|
16
|
+
|
17
|
+
# key, [storage], [seed_data], [opts]
|
18
|
+
def initialize(key, *args)
|
19
|
+
@key = key
|
20
|
+
|
21
|
+
if args.last.is_a?(Hash)
|
22
|
+
@options = args.pop
|
23
|
+
end
|
24
|
+
|
25
|
+
if args.first.is_a?(ROC::Store::ROCStore)
|
26
|
+
@storage = args.shift
|
27
|
+
end
|
28
|
+
|
29
|
+
if !self.storage
|
30
|
+
raise ArgumentError, 'no class-level storage set, so must initialize with a Store'
|
31
|
+
end
|
32
|
+
|
33
|
+
if args.size > 1
|
34
|
+
raise ArgumentError, 'new(key, [storage], [seed_data], [opts])'
|
35
|
+
end
|
36
|
+
|
37
|
+
if !(seed_data = args.pop).nil?
|
38
|
+
seed(seed_data)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def seed(data)
|
43
|
+
if self.exists?
|
44
|
+
raise "#{self.key} already exists -- can't seed it"
|
45
|
+
else
|
46
|
+
self.clobber(data)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def clobber(data)
|
51
|
+
raise "clobber must be overriden in subclasses"
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.delegate_methods(options)
|
55
|
+
raise ":on and :to required to delegate methods" unless options.has_key?(:on) && options.has_key?(:to)
|
56
|
+
self.const_set('DELEGATE_OPTIONS', options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def respond_to?(method_name)
|
60
|
+
self.methods.include?(method_name) || (self.class.const_get('DELEGATE_OPTIONS') && self.class.const_get('DELEGATE_OPTIONS')[:on].respond_to?(method_name))
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_missing(method_name, *args, &block)
|
64
|
+
if self.class.const_get('DELEGATE_OPTIONS') &&
|
65
|
+
(delegate_type = self.class.const_get('DELEGATE_OPTIONS')[:on]) &&
|
66
|
+
delegate_type.respond_to?(method_name) &&
|
67
|
+
!['!', '='].include?(method_name.to_s[method_name.to_s.length - 1]) # we won't delegate modifying methods
|
68
|
+
self.send(self.class.const_get('DELEGATE_OPTIONS')[:to]).send(method_name, *args, &block)
|
69
|
+
else
|
70
|
+
super(method_name, *args, &block)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
def call(remote_method_name, *args)
|
77
|
+
self.storage.call(remote_method_name, self.key, *args)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'roc/objects/base'
|
2
|
+
module ROC
|
3
|
+
class Float < Base
|
4
|
+
include ROC::Types::ScalarType
|
5
|
+
|
6
|
+
delegate_methods :on => 0.0, :to => :value
|
7
|
+
|
8
|
+
def to_float
|
9
|
+
self.value.to_f
|
10
|
+
end
|
11
|
+
alias to_f to_float
|
12
|
+
|
13
|
+
## implementing scalar type required methods ##
|
14
|
+
|
15
|
+
def serialize(val)
|
16
|
+
val.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def deserialize(val)
|
20
|
+
if val.nil?
|
21
|
+
nil
|
22
|
+
elsif 'Infinity' == val
|
23
|
+
1.0 / 0
|
24
|
+
elsif '-Infinity' == val
|
25
|
+
-1.0 / 0
|
26
|
+
else
|
27
|
+
val.to_f
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'roc/objects/base'
|
2
|
+
module ROC
|
3
|
+
class Hash < Base
|
4
|
+
extend ROC::Types::MethodGenerators
|
5
|
+
|
6
|
+
delegate_methods :on => {}, :to => :to_hash
|
7
|
+
|
8
|
+
nonserializing_method :hdel
|
9
|
+
|
10
|
+
nonserializing_method :hexists
|
11
|
+
alias key? hexists
|
12
|
+
alias has_key? hexists
|
13
|
+
alias member? hexists
|
14
|
+
alias include? hexists
|
15
|
+
|
16
|
+
nonserializing_method :hget
|
17
|
+
alias get hget
|
18
|
+
alias [] hget
|
19
|
+
|
20
|
+
zero_arg_method :hgetall
|
21
|
+
alias getall hgetall
|
22
|
+
|
23
|
+
def hincrby(field, increment)
|
24
|
+
self.call :hincrby, field, increment
|
25
|
+
end
|
26
|
+
alias incrby hincrby
|
27
|
+
|
28
|
+
zero_arg_method :hkeys
|
29
|
+
alias keys hkeys
|
30
|
+
|
31
|
+
zero_arg_method :hlen
|
32
|
+
alias len hlen
|
33
|
+
alias length hlen
|
34
|
+
alias size hlen
|
35
|
+
|
36
|
+
def hmget(*fields)
|
37
|
+
self.call :hmget, *fields
|
38
|
+
end
|
39
|
+
alias mget hmget
|
40
|
+
|
41
|
+
def hmset(*pairs)
|
42
|
+
self.call :hmset, *pairs
|
43
|
+
end
|
44
|
+
alias mset hmset
|
45
|
+
|
46
|
+
def hset(field, val)
|
47
|
+
self.call :hset, field, val
|
48
|
+
end
|
49
|
+
alias set hset
|
50
|
+
alias []= hset
|
51
|
+
alias store hset
|
52
|
+
|
53
|
+
def hsetnx(field, val)
|
54
|
+
self.call :hsetnx, field, val
|
55
|
+
end
|
56
|
+
alias setnx hsetnx
|
57
|
+
|
58
|
+
zero_arg_method :hvals
|
59
|
+
alias vals hvals
|
60
|
+
alias values hvals
|
61
|
+
|
62
|
+
# shortcuts/helpers
|
63
|
+
|
64
|
+
alias values_at hmget
|
65
|
+
|
66
|
+
def has_value?(val)
|
67
|
+
self.values.include?(val)
|
68
|
+
end
|
69
|
+
alias value? has_value?
|
70
|
+
|
71
|
+
def empty?
|
72
|
+
0 == self.hlen
|
73
|
+
end
|
74
|
+
|
75
|
+
def decrby(field, by)
|
76
|
+
self.hincrby field, -by
|
77
|
+
end
|
78
|
+
|
79
|
+
def increment(field, by=nil)
|
80
|
+
self.hincrby field, (by || 1)
|
81
|
+
end
|
82
|
+
|
83
|
+
def decrement(field, by=nil)
|
84
|
+
self.hincrby field, -(by || 1)
|
85
|
+
end
|
86
|
+
|
87
|
+
## implement (if posible) destructive methods that would otherwise raise
|
88
|
+
|
89
|
+
def merge!(hsh)
|
90
|
+
raise ArgumentError, 'block version not supported' if block_given?
|
91
|
+
self.hmset(*hsh.to_a.flatten)
|
92
|
+
self
|
93
|
+
end
|
94
|
+
alias update merge!
|
95
|
+
|
96
|
+
def delete(field)
|
97
|
+
val = self.hget(field)
|
98
|
+
self.hdel(field)
|
99
|
+
val
|
100
|
+
end
|
101
|
+
|
102
|
+
def replace(hsh)
|
103
|
+
self.clobber(hsh)
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
def clear
|
108
|
+
self.replace({})
|
109
|
+
end
|
110
|
+
|
111
|
+
def delete_if
|
112
|
+
raise NotImplementedError
|
113
|
+
end
|
114
|
+
|
115
|
+
def shift
|
116
|
+
raise NotImplementedError
|
117
|
+
end
|
118
|
+
|
119
|
+
## implementing for delegate
|
120
|
+
|
121
|
+
alias to_hash getall
|
122
|
+
alias to_h getall
|
123
|
+
|
124
|
+
def clobber(data)
|
125
|
+
self.storage.multi do
|
126
|
+
self.forget
|
127
|
+
if data.size > 0
|
128
|
+
self.merge!(data)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'roc/objects/base'
|
2
|
+
module ROC
|
3
|
+
class Integer < Base
|
4
|
+
include ROC::Types::ScalarType
|
5
|
+
|
6
|
+
delegate_methods :on => 0, :to => :value
|
7
|
+
|
8
|
+
def to_integer
|
9
|
+
self.value.to_i
|
10
|
+
end
|
11
|
+
alias to_int to_integer
|
12
|
+
alias to_i to_integer
|
13
|
+
|
14
|
+
## implemeting redis methods ##
|
15
|
+
|
16
|
+
def increment(by=nil)
|
17
|
+
if by.nil?
|
18
|
+
self.call :incr
|
19
|
+
else
|
20
|
+
self.call :incrby, by
|
21
|
+
end
|
22
|
+
end
|
23
|
+
alias incr increment
|
24
|
+
alias incrby increment
|
25
|
+
|
26
|
+
def decrement(by=nil)
|
27
|
+
if by.nil?
|
28
|
+
self.call :decr
|
29
|
+
else
|
30
|
+
self.call :decrby, by
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias decr decrement
|
34
|
+
alias decrby decrement
|
35
|
+
|
36
|
+
## implementing scalar type required methods ##
|
37
|
+
|
38
|
+
def serialize(val)
|
39
|
+
val.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def deserialize(val)
|
43
|
+
if val.nil?
|
44
|
+
nil
|
45
|
+
else
|
46
|
+
val.to_i
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'roc/objects/base'
|
2
|
+
require 'roc/types/array_type'
|
3
|
+
require 'roc/types/sortable_type'
|
4
|
+
|
5
|
+
module ROC
|
6
|
+
class List < Base
|
7
|
+
include ROC::Types::ArrayType
|
8
|
+
include ROC::Types::SortableType
|
9
|
+
extend ROC::Types::MethodGenerators
|
10
|
+
|
11
|
+
def lrange(start_index, stop_index)
|
12
|
+
self.call :lrange, start_index, stop_index
|
13
|
+
end
|
14
|
+
alias range lrange
|
15
|
+
|
16
|
+
zero_arg_method :llen
|
17
|
+
alias len llen
|
18
|
+
|
19
|
+
nonserializing_method :rpush
|
20
|
+
|
21
|
+
nonserializing_method :rpushx
|
22
|
+
alias pushx rpushx
|
23
|
+
|
24
|
+
nonserializing_method :lpush
|
25
|
+
|
26
|
+
nonserializing_method :lpushx
|
27
|
+
alias unshiftx lpushx
|
28
|
+
|
29
|
+
zero_arg_method :rpop
|
30
|
+
|
31
|
+
zero_arg_method :lpop
|
32
|
+
|
33
|
+
def lset(index, val)
|
34
|
+
self.call :lset, index, val
|
35
|
+
end
|
36
|
+
alias set lset
|
37
|
+
|
38
|
+
nonserializing_method :lindex
|
39
|
+
alias index lindex
|
40
|
+
|
41
|
+
def lrem(count, val)
|
42
|
+
self.call :lrem, count, val
|
43
|
+
end
|
44
|
+
alias rem lrem
|
45
|
+
|
46
|
+
def ltrim(start_index, stop_index)
|
47
|
+
self.call :ltrim, start_index, stop_index
|
48
|
+
end
|
49
|
+
alias trim ltrim
|
50
|
+
|
51
|
+
def rpoplpush(other_list=self)
|
52
|
+
self.call :rpoplpush, other_list.key
|
53
|
+
end
|
54
|
+
|
55
|
+
def linsert(where, pivot, value)
|
56
|
+
self.call :linsert, where, pivot, value
|
57
|
+
end
|
58
|
+
|
59
|
+
def linsert_before(pivot, value)
|
60
|
+
self.linsert('before', pivot, value)
|
61
|
+
end
|
62
|
+
alias insert_before linsert_before
|
63
|
+
|
64
|
+
def linsert_after(pivot, value)
|
65
|
+
self.linsert('after', pivot, value)
|
66
|
+
end
|
67
|
+
alias insert_after linsert_after
|
68
|
+
|
69
|
+
|
70
|
+
## shortcut methods
|
71
|
+
|
72
|
+
def [](range_or_num, num=nil)
|
73
|
+
if range_or_num.is_a?(::Integer)
|
74
|
+
if num.nil?
|
75
|
+
self.lindex(range_or_num)
|
76
|
+
elsif num >= 0
|
77
|
+
self.lrange(range_or_num, range_or_num + num - 1)
|
78
|
+
else
|
79
|
+
raise ArgumentError, 'second arg to [] must be a non-neg integer'
|
80
|
+
end
|
81
|
+
elsif range_or_num.is_a?(Range)
|
82
|
+
self.lrange(range_or_num.first, (range_or_num.exclude_end? ? range_or_num.last - 1 : range_or_num.last))
|
83
|
+
else
|
84
|
+
if num.nil?
|
85
|
+
self.values.slice(range_or_num)
|
86
|
+
else
|
87
|
+
self.values.slice(range_or_num, num)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
alias slice []
|
92
|
+
|
93
|
+
def first
|
94
|
+
self.lindex(0)
|
95
|
+
end
|
96
|
+
|
97
|
+
def last
|
98
|
+
self.lindex(-1)
|
99
|
+
end
|
100
|
+
|
101
|
+
def []=(*args)
|
102
|
+
case args.size
|
103
|
+
when 1
|
104
|
+
raise ArgumentError, 'index required'
|
105
|
+
when 2
|
106
|
+
if args[0].is_a?(::Integer)
|
107
|
+
self.lset(*args)
|
108
|
+
else
|
109
|
+
raise ArgumentError, 'range assignment not supported in []='
|
110
|
+
end
|
111
|
+
when 3
|
112
|
+
raise ArgumentError, 'multiple index assignment not supported in []='
|
113
|
+
else
|
114
|
+
raise ArgumentError, 'wrong number of args'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
## implement (if posible) destructive methods that would otherwise raise
|
119
|
+
|
120
|
+
def delete(val)
|
121
|
+
count = self.lrem(0, val)
|
122
|
+
if count > 0
|
123
|
+
val
|
124
|
+
else
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def push(*objs)
|
130
|
+
if 1 == objs.size
|
131
|
+
self.rpush(objs[0])
|
132
|
+
elsif objs.size > 1
|
133
|
+
self.storage.multi do
|
134
|
+
objs.each do |obj|
|
135
|
+
self.rpush(obj)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
def <<(obj)
|
143
|
+
self.push(obj)
|
144
|
+
end
|
145
|
+
|
146
|
+
def unshift(*objs)
|
147
|
+
if 1 == objs.size
|
148
|
+
self.lpush(objs[0])
|
149
|
+
elsif objs.size > 1
|
150
|
+
self.storage.multi do
|
151
|
+
objs.reverse.each do |obj|
|
152
|
+
self.lpush(obj)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
def pop(*args)
|
160
|
+
if 0 == args.size
|
161
|
+
self.rpop
|
162
|
+
elsif 1 == args.size
|
163
|
+
(self.storage.multi do
|
164
|
+
args[0].times do
|
165
|
+
self.rpop
|
166
|
+
end
|
167
|
+
end).reverse
|
168
|
+
else
|
169
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def shift(*args)
|
174
|
+
if 0 == args.size
|
175
|
+
self.lpop
|
176
|
+
elsif 1 == args.size
|
177
|
+
(self.storage.multi do
|
178
|
+
args[0].times do
|
179
|
+
self.lpop
|
180
|
+
end
|
181
|
+
end).reverse
|
182
|
+
else
|
183
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
## implementing ArrayType ##
|
188
|
+
|
189
|
+
def clobber(vals)
|
190
|
+
self.storage.multi do
|
191
|
+
self.forget
|
192
|
+
vals.each{|v| self << v}
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def values
|
197
|
+
self.lrange(0, -1)
|
198
|
+
end
|
199
|
+
|
200
|
+
alias size llen
|
201
|
+
alias length llen
|
202
|
+
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ROC
|
2
|
+
class Lock < Time
|
3
|
+
|
4
|
+
def lock(expires_time)
|
5
|
+
aquired_lock = false
|
6
|
+
if self.setnx(expires_time)
|
7
|
+
aquired_lock = true
|
8
|
+
else
|
9
|
+
locked_until = self.value
|
10
|
+
if locked_until.nil? || (locked_until < ::Time.now) ##ttl of 0 is not yet expired
|
11
|
+
# only say we got the lock if we manage to update it first
|
12
|
+
if self.getset(expires_time) == locked_until
|
13
|
+
aquired_lock = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
aquired_lock
|
18
|
+
end
|
19
|
+
|
20
|
+
def locked?
|
21
|
+
locked_until = self.value
|
22
|
+
!locked_until.nil? && (locked_until >= ::Time.now) ##ttl of 0 is not yet expired
|
23
|
+
end
|
24
|
+
|
25
|
+
def unlock
|
26
|
+
self.forget
|
27
|
+
end
|
28
|
+
|
29
|
+
def when_locked(expires_time, poll_ms=100)
|
30
|
+
until self.lock(expires_time)
|
31
|
+
sleep(poll_ms.to_f / 1000)
|
32
|
+
end
|
33
|
+
begin
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
self.unlock
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def locking_if_necessary(expires_time)
|
41
|
+
obtained_lock = self.lock(expires_time)
|
42
|
+
begin
|
43
|
+
yield
|
44
|
+
ensure
|
45
|
+
if obtained_lock
|
46
|
+
self.unlock
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|