redis-roc 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|