ruby-redis-portertech 0.0.3

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.
@@ -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