ruby-redis-portertech 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ module Redis
2
+ class Config < Hash
3
+
4
+ INTEGERS = [:port, :timeout, :databases]
5
+ BOOLEANS = [:daemonize]
6
+
7
+ def initialize argf_or_hash
8
+
9
+ # defaults
10
+ self[:dir] = '.'
11
+ self[:logfile] = 'stdout'
12
+ self[:loglevel] = 'verbose'
13
+ self[:daemonize] = false
14
+ self[:pidfile] = '/var/run/redis.pid'
15
+ self[:bind] = '127.0.0.1'
16
+ self[:port] = 6379
17
+ self[:timeout] = 300
18
+ self[:databases] = 16
19
+
20
+ this = super()
21
+ if Hash === argf_or_hash
22
+ super argf_or_hash
23
+ else
24
+ super()
25
+ # load from ARGF or IO compatible interface
26
+ argf_or_hash.each do |line|
27
+ key, val = line.split ' ', 2
28
+ self[key.downcase.gsub(/-/,'_').to_sym] = val.chomp "\n"
29
+ end
30
+ end
31
+
32
+ # convert
33
+ INTEGERS.each do |key|
34
+ this[key] = this[key].to_i
35
+ end
36
+
37
+ # convert
38
+ BOOLEANS.each do |key|
39
+ next unless String===this[key]
40
+ this[key] = case this[key].downcase
41
+ when 'yes' then true
42
+ when 'no' then false
43
+ else nil
44
+ end
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,30 @@
1
+ module Redis
2
+ module Connection
3
+
4
+ def redis_AUTH password
5
+ raise 'invalid password' unless authorized password
6
+ :'+OK'
7
+ end
8
+
9
+ def redis_SELECT db_index
10
+ database = @databases[redis_i db_index]
11
+ raise 'invalid DB index' unless database
12
+ @database = database
13
+ :'+OK'
14
+ end
15
+
16
+ def redis_PING
17
+ :'+PONG'
18
+ end
19
+
20
+ def redis_ECHO str
21
+ str
22
+ end
23
+
24
+ def redis_QUIT
25
+ send_redis :'+OK'
26
+ :quit
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,133 @@
1
+ module Redis
2
+ class Database
3
+
4
+ class Watcher
5
+
6
+ attr_reader :bound
7
+
8
+ def initialize
9
+ @watched = []
10
+ @bound = true
11
+ end
12
+
13
+ def bind database, *keys
14
+ return unless @bound
15
+ keys.each do |key|
16
+ entry = [database, key]
17
+ next if @watched.include? entry
18
+ @watched << entry
19
+ (database.watchers[key] ||= []).push self
20
+ end
21
+ end
22
+
23
+ def unbind
24
+ return unless @bound
25
+ @watched.each do |database, key|
26
+ key_df_list = database.watchers[key]
27
+ next unless key_df_list
28
+ key_df_list.delete_if { |e| e == self }
29
+ end
30
+ @bound = false
31
+ end
32
+
33
+ end
34
+
35
+ # Redis databases are volatile dictionaries.
36
+
37
+ attr_reader :blocked_pops, :watchers
38
+
39
+ def initialize
40
+ @dict = {}
41
+ @expiry = {}
42
+ @blocked_pops = {}
43
+ @watchers = {}
44
+ end
45
+
46
+ def touch key
47
+ (@watchers[key]||[]).each do |watcher|
48
+ watcher.unbind
49
+ end
50
+ end
51
+
52
+ def expire key, seconds
53
+ return false unless @dict.has_key? key
54
+ touch key
55
+ @expiry[key] = Time.now + seconds
56
+ return true
57
+ end
58
+
59
+ def expire_at key, unixtime
60
+ return false unless @dict.has_key? key
61
+ touch key
62
+ @expiry[key] = Time.at unixtime
63
+ return true
64
+ end
65
+
66
+ def ttl key
67
+ check_expiry key
68
+ time = @expiry[key]
69
+ return -1 unless time
70
+ (time - Time.now).round
71
+ end
72
+
73
+ def persist key
74
+ result = @expiry.has_key? key
75
+ touch key if result
76
+ @expiry.delete key
77
+ result
78
+ end
79
+
80
+ def random_key
81
+ @dict.keys[rand @dict.size]
82
+ end
83
+
84
+ def [] key
85
+ check_expiry key
86
+ @dict[key]
87
+ end
88
+
89
+ def []= key, value
90
+ touch key
91
+ @expiry.delete key
92
+ @dict[key] = value
93
+ end
94
+
95
+ def has_key? key
96
+ check_expiry key
97
+ @dict.has_key? key
98
+ end
99
+
100
+ def delete key
101
+ touch key
102
+ @dict.delete key
103
+ @expiry.delete key
104
+ end
105
+
106
+ def reduce *args, &block
107
+ @dict.reduce *args, &block
108
+ end
109
+
110
+ def size
111
+ @dict.size
112
+ end
113
+
114
+ def clear
115
+ # We don't trigger watchers of unset records
116
+ @dict.each_key { |key| touch key }
117
+ @dict.clear
118
+ @expiry.clear
119
+ end
120
+
121
+ def empty?
122
+ @dict.empty?
123
+ end
124
+
125
+ private
126
+
127
+ def check_expiry key
128
+ expires_at = @expiry[key]
129
+ delete key if expires_at and Time.now > expires_at
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,70 @@
1
+ module Redis
2
+ module Hashes
3
+
4
+ def redis_HSET key, field, value
5
+ hash = @database[key] ||= {}
6
+ result = !hash.has_key?(field)
7
+ hash[field] = value
8
+ result
9
+ end
10
+
11
+ def redis_HEXISTS key, field
12
+ (@database[key] || {}).has_key? field
13
+ end
14
+
15
+ def redis_HSETNX key, field, value
16
+ hash = @database[key] || {}
17
+ return false if hash.has_key? field
18
+ redis_HSET key, field, value
19
+ return true
20
+ end
21
+
22
+ def redis_HKEYS key
23
+ (@database[key] || {}).keys
24
+ end
25
+
26
+ def redis_HVALS key
27
+ (@database[key] || {}).values
28
+ end
29
+
30
+ def redis_HMSET key, *args
31
+ (@database[key] ||= {}).merge! Hash[*args]
32
+ :'+OK'
33
+ end
34
+
35
+ def redis_HMGET key, *fields
36
+ hash = (@database[key] || {})
37
+ redis_t Hash, hash
38
+ fields.collect do |field|
39
+ hash[field]
40
+ end
41
+ end
42
+
43
+ def redis_HLEN key
44
+ (@database[key] || {}).size
45
+ end
46
+
47
+ def redis_HGET key, field
48
+ (@database[key] || {})[field]
49
+ end
50
+
51
+ def redis_HGETALL key
52
+ @database[key] || {}
53
+ end
54
+
55
+ def redis_HDEL key, field
56
+ hash = @database[key] || {}
57
+ result = hash.has_key? field
58
+ hash.delete field
59
+ result
60
+ end
61
+
62
+ def redis_HINCRBY key, field, increment
63
+ hash = @database[key] ||= {}
64
+ value = redis_i(hash[field] ||= 0)
65
+ value += redis_i increment
66
+ hash[field] = value
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,155 @@
1
+ module Redis
2
+ module Keys
3
+
4
+ def redis_RANDOMKEY
5
+ return nil if @database.empty?
6
+ @database.random_key
7
+ end
8
+
9
+ def redis_KEYS pattern
10
+ @database.reduce([]) do |memo, key_val|
11
+ key = key_val[0]
12
+ memo.push key if File.fnmatch(pattern, key)
13
+ memo
14
+ end
15
+ end
16
+
17
+ def redis_SORT key, *args
18
+ record = @database[key] || []
19
+ sort = 'ASC'
20
+ gets = []
21
+ alpha = false
22
+ by = by_hash = offset = count = store = nil
23
+ until args.empty?
24
+ arg = args.shift
25
+ case arg.upcase
26
+ when 'LIMIT'
27
+ offset = args.shift.to_i
28
+ count = args.shift.to_i
29
+ when 'ASC'
30
+ sort = 'ASC'
31
+ when 'DESC'
32
+ sort = 'DESC'
33
+ when 'ALPHA'
34
+ alpha = true
35
+ when 'STORE'
36
+ store = args.shift
37
+ when 'GET'
38
+ gets << args.shift
39
+ when 'BY'
40
+ by, by_hash = args.shift.split '->', 2
41
+ else
42
+ raise "#{arg} bad argument"
43
+ end
44
+ end
45
+ result = record.sort do |a, b|
46
+ if by
47
+ a = @database[by.sub /\*/, a]
48
+ a = a[by_hash] if by_hash
49
+ b = @database[by.sub /\*/, b]
50
+ b = b[by_hash] if by_hash
51
+ end
52
+ if alpha
53
+ a = a.to_s
54
+ b = b.to_s
55
+ else
56
+ a = a.to_f
57
+ b = b.to_f
58
+ end
59
+ if sort == 'DESC'
60
+ b <=> a
61
+ else
62
+ a <=> b
63
+ end
64
+ end
65
+ unless gets.empty?
66
+ original = result
67
+ result = []
68
+ original.each do |r|
69
+ gets.each do |g|
70
+ get, get_hash = g.split('->', 2)
71
+ r = @database[get.sub /\*/, r] unless get == '#'
72
+ r = r[get_hash] if get_hash
73
+ result << r
74
+ end
75
+ end
76
+ end
77
+ if count and offset
78
+ result = result[offset,count]
79
+ elsif count
80
+ result = result[0,count]
81
+ elsif offset
82
+ result = result[offset..-1]
83
+ end
84
+ if Array === result[0]
85
+ result = result.collect {|r| r.first}
86
+ end
87
+ @database[store] = result if store
88
+ result
89
+ end
90
+
91
+ def redis_DEL *keys
92
+ count = 0
93
+ keys.each do |key|
94
+ count += 1 if @database.has_key? key
95
+ @database.delete key
96
+ end
97
+ count
98
+ end
99
+
100
+ def redis_TYPE key
101
+ case @database[key]
102
+ when String, Numeric; 'string'
103
+ when Array; 'list'
104
+ when Set; 'set'
105
+ when ZSet; 'zset'
106
+ when Hash; 'hash'
107
+ else 'unknown'
108
+ end
109
+ end
110
+
111
+ def redis_EXISTS key
112
+ @database.has_key? key
113
+ end
114
+
115
+ def redis_EXPIRE key, seconds
116
+ @database.expire key, redis_pos_i(seconds)
117
+ end
118
+
119
+ def redis_EXPIREAT key, timestamp
120
+ @database.expire_at key, redis_pos_i(timestamp)
121
+ end
122
+
123
+ def redis_PERSIST key
124
+ @database.persist key
125
+ end
126
+
127
+ def redis_TTL key
128
+ @database.ttl key
129
+ end
130
+
131
+ def redis_RENAME key, newkey
132
+ raise 'key and newkey are identical' if key == newkey
133
+ raise 'key not found' unless @database.has_key? key
134
+ @database[newkey] = @database[key]
135
+ @database.delete key
136
+ end
137
+
138
+ def redis_RENAMENX key, newkey
139
+ return false if @database.has_key? newkey
140
+ redis_RENAME key, newkey
141
+ true
142
+ end
143
+
144
+ def redis_MOVE key, db
145
+ raise unless @database.has_key? key
146
+ raise if @databases[redis_i db].has_key? key
147
+ @databases[redis_i db][key] = @database[key]
148
+ @database.delete key
149
+ true
150
+ rescue
151
+ false
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,225 @@
1
+ require 'eventmachine'
2
+
3
+ module Redis
4
+ module Lists
5
+
6
+ class DeferredPop
7
+ include EventMachine::Deferrable
8
+
9
+ attr_reader :bound
10
+
11
+ def initialize database, timeout_secs, *keys
12
+ @database = database
13
+ @keys = keys
14
+ timeout timeout_secs if timeout_secs > 0
15
+ errback { unbind }
16
+ callback { unbind }
17
+ keys.each do |key|
18
+ (@database.blocked_pops[key] ||= []).push self
19
+ end
20
+ @bound = true
21
+ end
22
+
23
+ def unbind
24
+ return unless @bound
25
+ @keys.each do |key|
26
+ key_df_list = @database.blocked_pops[key]
27
+ next unless key_df_list
28
+ key_df_list.delete_if { |e| e == self }
29
+ end
30
+ @bound = false
31
+ end
32
+
33
+ end
34
+
35
+ def redis_LRANGE key, first, last
36
+ first = redis_i first
37
+ last = redis_i last
38
+ list = @database[key] || []
39
+ first = 0 if first < -list.size
40
+ list[first..last]
41
+ end
42
+
43
+ def redis_LTRIM key, start, stop
44
+ @database[key] = redis_LRANGE key, start, stop
45
+ end
46
+
47
+ def redis_BRPOP *args
48
+ timeout = redis_pos_i args.pop
49
+ args.each do |key|
50
+ list = @database[key]
51
+ if list and list.size > 0
52
+ value = list.pop
53
+ @database.delete key if list.empty?
54
+ return [key, value]
55
+ end
56
+ end
57
+ df = DeferredPop.new(@database, timeout, *args)
58
+ df.errback { send_redis :'*-1' }
59
+ df.callback { |key, value| send_redis [key, value] }
60
+ df
61
+ end
62
+
63
+ def redis_BLPOP *args
64
+ timeout = redis_pos_i args.pop
65
+ args.each do |key|
66
+ list = @database[key]
67
+ if list and list.size > 0
68
+ value = list.shift
69
+ @database.delete key if list.empty?
70
+ return [key, value]
71
+ end
72
+ end
73
+ df = DeferredPop.new(@database, timeout, *args)
74
+ df.errback { send_redis :'*-1' }
75
+ df.callback { |key, value| send_redis [key, value] }
76
+ df
77
+ end
78
+
79
+ def redis_RPOPLPUSH source, destination
80
+ source_list = @database[source]
81
+ return nil unless source_list
82
+ redis_t Array, source_list
83
+ redis_t NilClass, Array, @database[destination]
84
+ value = source_list.pop
85
+ @database.delete source if source_list.empty?
86
+ redis_LPUSH destination, value
87
+ return value
88
+ end
89
+
90
+ def redis_BRPOPLPUSH source, destination, timeout
91
+ source_list = @database[source]
92
+ if source_list
93
+ redis_t Array, source_list
94
+ value = source_list.pop
95
+ @database.delete source if source_list.empty?
96
+ redis_LPUSH destination, value
97
+ return value
98
+ end
99
+ redis_t NilClass, Array, @database[destination]
100
+ df = DeferredPop.new @database, redis_pos_i(timeout), source
101
+ df.errback {send_redis :'*-1'}
102
+ df.callback do |key, value|
103
+ redis_LPUSH destination, value
104
+ send_redis value
105
+ end
106
+ df
107
+ end
108
+
109
+ def redis_RPUSH key, value
110
+ list = @database[key]
111
+ redis_t NilClass, Array, list
112
+ (@database.blocked_pops[key] ||= []).each do |deferrable|
113
+ deferrable.succeed key, value
114
+ return 0
115
+ end
116
+ list = @database[key] = [] unless list
117
+ list.push(value).size
118
+ end
119
+
120
+ def redis_LPUSH key, value
121
+ list = @database[key]
122
+ redis_t NilClass, Array, list
123
+ (@database.blocked_pops[key] ||= []).each do |deferrable|
124
+ deferrable.succeed key, value
125
+ return 0
126
+ end
127
+ list = @database[key] = [] unless list
128
+ list.unshift(value).size
129
+ end
130
+
131
+ def redis_LPUSHX key, value
132
+ list = @database[key]
133
+ return 0 unless Array === list and list.size > 0
134
+ redis_LPUSH key, value
135
+ list.size
136
+ end
137
+
138
+ def redis_RPUSHX key, value
139
+ list = @database[key]
140
+ return 0 unless Array === list and list.size > 0
141
+ redis_RPUSH key, value
142
+ list.size
143
+ end
144
+
145
+ def redis_LINSERT key, mode, pivot, value
146
+ list = @database[key]
147
+ index = list.find_index pivot
148
+ return -1 unless index
149
+ case mode.upcase
150
+ when 'BEFORE'
151
+ list[index,0] = value
152
+ when 'AFTER'
153
+ list[index+1,0] = value
154
+ else
155
+ raise 'only BEFORE|AFTER supported'
156
+ end
157
+ list.size
158
+ end
159
+
160
+ def redis_RPOP key
161
+ list = @database[key]
162
+ return nil unless list
163
+ value = list.pop
164
+ @database.delete key if list.empty?
165
+ value
166
+ end
167
+
168
+ def redis_LPOP key
169
+ list = @database[key]
170
+ return nil unless list
171
+ value = list.shift
172
+ @database.delete key if list.empty?
173
+ value
174
+ end
175
+
176
+ def redis_LLEN key
177
+ list = @database[key] || []
178
+ redis_t Array, list
179
+ list.size
180
+ end
181
+
182
+ def redis_LINDEX key, index
183
+ list = @database[key] || []
184
+ redis_t Array, list
185
+ list[redis_i index]
186
+ end
187
+
188
+ def redis_LSET key, index, value
189
+ list = @database[key] || []
190
+ redis_t Array, list
191
+ raise 'out of range' unless list.size > redis_i(index).abs
192
+ list[redis_i index] = value
193
+ end
194
+
195
+ def redis_LREM key, count, value
196
+ list = @database[key] || []
197
+ count = redis_i count
198
+ size = list.size
199
+ if count == 0
200
+ list.delete value
201
+ elsif count < 0
202
+ i = list.size
203
+ while i > 0 and count < 0
204
+ i -= 1
205
+ if list[i] == value
206
+ list.delete_at i
207
+ count += 1
208
+ end
209
+ end
210
+ else # count > 0
211
+ i = 0
212
+ while i < list.size and count > 0
213
+ if list[i] == value
214
+ list.delete_at i
215
+ count -= 1
216
+ else
217
+ i += 1
218
+ end
219
+ end
220
+ end
221
+ size - list.size
222
+ end
223
+
224
+ end
225
+ end