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.
- data/LICENSE +9 -0
- data/README.md +80 -0
- data/bin/ruby-redis +12 -0
- data/lib/redis.rb +13 -0
- data/lib/redis/bin.rb +127 -0
- data/lib/redis/client.rb +213 -0
- data/lib/redis/config.rb +49 -0
- data/lib/redis/connection.rb +30 -0
- data/lib/redis/database.rb +133 -0
- data/lib/redis/hashes.rb +70 -0
- data/lib/redis/keys.rb +155 -0
- data/lib/redis/lists.rb +225 -0
- data/lib/redis/logger.rb +69 -0
- data/lib/redis/protocol.rb +107 -0
- data/lib/redis/pubsub.rb +150 -0
- data/lib/redis/reader.rb +175 -0
- data/lib/redis/sender.rb +47 -0
- data/lib/redis/server.rb +60 -0
- data/lib/redis/sets.rb +104 -0
- data/lib/redis/strict.rb +65 -0
- data/lib/redis/strings.rb +142 -0
- data/lib/redis/synchrony.rb +51 -0
- data/lib/redis/version.rb +5 -0
- data/lib/redis/zsets.rb +279 -0
- metadata +120 -0
data/lib/redis/config.rb
ADDED
@@ -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
|
data/lib/redis/hashes.rb
ADDED
@@ -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
|
data/lib/redis/keys.rb
ADDED
@@ -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
|
data/lib/redis/lists.rb
ADDED
@@ -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
|