fakeredis 0.1.4 → 0.2.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/.gitignore +1 -0
- data/.travis.yml +2 -0
- data/README.md +9 -2
- data/fakeredis.gemspec +3 -2
- data/lib/fakeredis.rb +3 -56
- data/lib/fakeredis/version.rb +1 -1
- data/lib/redis/connection/memory.rb +655 -0
- data/spec/compatibility_spec.rb +9 -0
- data/spec/connection_spec.rb +2 -2
- data/spec/hashes_spec.rb +1 -1
- data/spec/keys_spec.rb +2 -1
- data/spec/lists_spec.rb +1 -1
- data/spec/server_spec.rb +1 -7
- data/spec/sets_spec.rb +1 -1
- data/spec/sorted_sets_spec.rb +1 -1
- data/spec/strings_spec.rb +1 -1
- data/spec/transactions_spec.rb +2 -2
- metadata +74 -40
- data/lib/fakeredis/connection.rb +0 -25
- data/lib/fakeredis/hashes.rb +0 -98
- data/lib/fakeredis/keys.rb +0 -82
- data/lib/fakeredis/lists.rb +0 -108
- data/lib/fakeredis/server.rb +0 -65
- data/lib/fakeredis/sets.rb +0 -122
- data/lib/fakeredis/sorted_sets.rb +0 -108
- data/lib/fakeredis/strings.rb +0 -116
- data/lib/fakeredis/transactions.rb +0 -13
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -15,11 +15,11 @@ Add it to your Gemfile:
|
|
15
15
|
|
16
16
|
## Usage
|
17
17
|
|
18
|
-
You can use FakeRedis
|
18
|
+
You can use FakeRedis without any changes:
|
19
19
|
|
20
20
|
require "fakeredis"
|
21
21
|
|
22
|
-
redis =
|
22
|
+
redis = Redis.new
|
23
23
|
|
24
24
|
>> redis.set "foo", "bar"
|
25
25
|
=> "OK"
|
@@ -31,6 +31,13 @@ Read [redis-rb](https://github.com/ezmobius/redis-rb) documentation and
|
|
31
31
|
[Redis](http://redis.io) homepage for more info about commands
|
32
32
|
|
33
33
|
|
34
|
+
## Acknowledgements
|
35
|
+
|
36
|
+
* [czarneckid](https://github.com/czarneckid)
|
37
|
+
* [obrie](https://github.com/obrie)
|
38
|
+
* [Travis-CI](http://travis-ci.org/) (Travis-CI also uses Fakeredis in its tests!!!)
|
39
|
+
|
40
|
+
|
34
41
|
## Contributing to FakeRedis
|
35
42
|
|
36
43
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
data/fakeredis.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ["Guillermo Iguaran"]
|
10
10
|
s.email = ["guilleiguaran@gmail.com"]
|
11
11
|
s.homepage = "https://github.com/guilleiguaran/fakeredis"
|
12
|
-
s.summary = %q{Fake
|
13
|
-
s.description = %q{Fake
|
12
|
+
s.summary = %q{Fake (In-memory) driver for redis-rb.}
|
13
|
+
s.description = %q{Fake (In-memory) driver for redis-rb. Useful for testing environment and machines without Redis.}
|
14
14
|
|
15
15
|
s.rubyforge_project = "fakeredis"
|
16
16
|
|
@@ -19,5 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
|
22
|
+
s.add_runtime_dependency(%q<redis>, ["~> 2.2.0"])
|
22
23
|
s.add_development_dependency(%q<rspec>, [">= 2.0.0"])
|
23
24
|
end
|
data/lib/fakeredis.rb
CHANGED
@@ -1,59 +1,6 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'redis/connection/memory'
|
1
3
|
|
2
4
|
module FakeRedis
|
3
|
-
|
4
|
-
class Client
|
5
|
-
attr_accessor :host, :port, :db, :path, :password, :logger, :reconnect
|
6
|
-
def initialize(options = {})
|
7
|
-
@path = options[:path]
|
8
|
-
@host = options[:host] || "127.0.0.1"
|
9
|
-
@port = (options[:port] || 6379).to_i
|
10
|
-
@password = options[:password]
|
11
|
-
@db = (options[:db] || 0).to_i
|
12
|
-
@logger = options[:logger]
|
13
|
-
@reconnect = true
|
14
|
-
end
|
15
|
-
|
16
|
-
def id
|
17
|
-
"redis://#{@host}:#{@port}/#{@db}"
|
18
|
-
end
|
19
|
-
|
20
|
-
def connect
|
21
|
-
self
|
22
|
-
end
|
23
|
-
|
24
|
-
def connected?
|
25
|
-
true
|
26
|
-
end
|
27
|
-
|
28
|
-
def method_missing(command, *args, &block)
|
29
|
-
true
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.connect(options = {})
|
34
|
-
new(options)
|
35
|
-
end
|
36
|
-
|
37
|
-
def initialize(options = {})
|
38
|
-
@data = {}
|
39
|
-
@expires = {}
|
40
|
-
@client = Client.new(options)
|
41
|
-
end
|
42
|
-
|
43
|
-
def client
|
44
|
-
@client
|
45
|
-
end
|
46
|
-
end
|
5
|
+
Redis = ::Redis
|
47
6
|
end
|
48
|
-
|
49
|
-
require 'set'
|
50
|
-
|
51
|
-
require "fakeredis/connection"
|
52
|
-
require "fakeredis/keys"
|
53
|
-
require "fakeredis/strings"
|
54
|
-
require "fakeredis/hashes"
|
55
|
-
require "fakeredis/lists"
|
56
|
-
require "fakeredis/sets"
|
57
|
-
#require "fakeredis/sorted_sets"
|
58
|
-
require "fakeredis/transactions"
|
59
|
-
require "fakeredis/server"
|
data/lib/fakeredis/version.rb
CHANGED
@@ -0,0 +1,655 @@
|
|
1
|
+
require 'redis/connection/registry'
|
2
|
+
require 'redis/connection/command_helper'
|
3
|
+
|
4
|
+
class Redis
|
5
|
+
module Connection
|
6
|
+
class Memory
|
7
|
+
include Redis::Connection::CommandHelper
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@data = {}
|
11
|
+
@expires = {}
|
12
|
+
@connected = false
|
13
|
+
@replies = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def connected?
|
17
|
+
@connected
|
18
|
+
end
|
19
|
+
|
20
|
+
def connect(host, port, timeout)
|
21
|
+
@connected = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def connect_unix(path, timeout)
|
25
|
+
@connected = true
|
26
|
+
end
|
27
|
+
|
28
|
+
def disconnect
|
29
|
+
@connected = false
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def timeout=(usecs)
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(command)
|
37
|
+
method = command.shift
|
38
|
+
reply = send(method, *command)
|
39
|
+
|
40
|
+
if reply == true
|
41
|
+
reply = 1
|
42
|
+
elsif reply == false
|
43
|
+
reply = 0
|
44
|
+
end
|
45
|
+
|
46
|
+
@replies << reply
|
47
|
+
@buffer << reply if @buffer && method != :multi
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def read
|
52
|
+
@replies.shift
|
53
|
+
end
|
54
|
+
|
55
|
+
# NOT IMPLEMENTED:
|
56
|
+
# * blpop
|
57
|
+
# * brpop
|
58
|
+
# * brpoplpush
|
59
|
+
# * discard
|
60
|
+
# * mapped_hmset
|
61
|
+
# * mapped_hmget
|
62
|
+
# * mapped_mset
|
63
|
+
# * mapped_msetnx
|
64
|
+
# * move
|
65
|
+
# * multi
|
66
|
+
# * subscribe
|
67
|
+
# * psubscribe
|
68
|
+
# * publish
|
69
|
+
# * substr
|
70
|
+
# * unwatch
|
71
|
+
# * watch
|
72
|
+
# * zadd
|
73
|
+
# * zcard
|
74
|
+
# * zcount
|
75
|
+
# * zincrby
|
76
|
+
# * zinterstore
|
77
|
+
# * zrange
|
78
|
+
# * zrangescore
|
79
|
+
# * zrank
|
80
|
+
# * zrem
|
81
|
+
# * zremrangebyrank
|
82
|
+
# * zremrangebyscore
|
83
|
+
# * zrevrange
|
84
|
+
# * zrevrangebyscore
|
85
|
+
# * zscore
|
86
|
+
# * zunionstore
|
87
|
+
def flushdb
|
88
|
+
@data = {}
|
89
|
+
@expires = {}
|
90
|
+
end
|
91
|
+
|
92
|
+
def flushall
|
93
|
+
flushdb
|
94
|
+
end
|
95
|
+
|
96
|
+
def auth(password)
|
97
|
+
"OK"
|
98
|
+
end
|
99
|
+
|
100
|
+
def select(index) ; end
|
101
|
+
|
102
|
+
def info
|
103
|
+
{
|
104
|
+
"redis_version" => "0.07",
|
105
|
+
"connected_clients" => "1",
|
106
|
+
"connected_slaves" => "0",
|
107
|
+
"used_memory" => "3187",
|
108
|
+
"changes_since_last_save" => "0",
|
109
|
+
"last_save_time" => "1237655729",
|
110
|
+
"total_connections_received" => "1",
|
111
|
+
"total_commands_processed" => "1",
|
112
|
+
"uptime_in_seconds" => "36000",
|
113
|
+
"uptime_in_days" => 0
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
117
|
+
def monitor; end
|
118
|
+
|
119
|
+
def save; end
|
120
|
+
|
121
|
+
def bgsave ; end
|
122
|
+
|
123
|
+
def bgreriteaof ; end
|
124
|
+
|
125
|
+
def get(key)
|
126
|
+
#return if expired?(key)
|
127
|
+
@data[key]
|
128
|
+
end
|
129
|
+
|
130
|
+
def getbit(key, offset)
|
131
|
+
#return if expired?(key)
|
132
|
+
return unless @data[key]
|
133
|
+
@data[key].unpack('B8')[0].split("")[offset]
|
134
|
+
end
|
135
|
+
|
136
|
+
def getrange(key, start, ending)
|
137
|
+
return unless @data[key]
|
138
|
+
@data[key][start..ending]
|
139
|
+
end
|
140
|
+
|
141
|
+
def getset(key, value)
|
142
|
+
old_value = @data[key]
|
143
|
+
@data[key] = value
|
144
|
+
return old_value
|
145
|
+
end
|
146
|
+
|
147
|
+
def mget(*keys)
|
148
|
+
@data.values_at(*keys)
|
149
|
+
end
|
150
|
+
|
151
|
+
def append(key, value)
|
152
|
+
@data[key] = (@data[key] || "")
|
153
|
+
@data[key] = @data[key] + value.to_s
|
154
|
+
end
|
155
|
+
|
156
|
+
def strlen(key)
|
157
|
+
return unless @data[key]
|
158
|
+
@data[key].size
|
159
|
+
end
|
160
|
+
|
161
|
+
def hgetall(key)
|
162
|
+
case hash = @data[key]
|
163
|
+
when nil then {}
|
164
|
+
when Hash then hash
|
165
|
+
else fail "Not a hash"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def hget(key, field)
|
170
|
+
return unless @data[key]
|
171
|
+
fail "Not a hash" unless @data[key].is_a?(Hash)
|
172
|
+
@data[key][field]
|
173
|
+
end
|
174
|
+
|
175
|
+
def hdel(key, field)
|
176
|
+
return unless @data[key]
|
177
|
+
fail "Not a hash" unless @data[key].is_a?(Hash)
|
178
|
+
@data[key].delete(field)
|
179
|
+
end
|
180
|
+
|
181
|
+
def hkeys(key)
|
182
|
+
case hash = @data[key]
|
183
|
+
when nil then []
|
184
|
+
when Hash then hash.keys
|
185
|
+
else fail "Not a hash"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def keys(pattern = "*")
|
190
|
+
regexp = Regexp.new(pattern.split("*").map { |r| Regexp.escape(r) }.join(".*"))
|
191
|
+
@data.keys.select { |key| key =~ regexp }
|
192
|
+
end
|
193
|
+
|
194
|
+
def randomkey
|
195
|
+
@data.keys[rand(dbsize)]
|
196
|
+
end
|
197
|
+
|
198
|
+
def echo(string)
|
199
|
+
string
|
200
|
+
end
|
201
|
+
|
202
|
+
def ping
|
203
|
+
"PONG"
|
204
|
+
end
|
205
|
+
|
206
|
+
def lastsave
|
207
|
+
Time.now.to_i
|
208
|
+
end
|
209
|
+
|
210
|
+
def dbsize
|
211
|
+
@data.keys.count
|
212
|
+
end
|
213
|
+
|
214
|
+
def exists(key)
|
215
|
+
@data.key?(key)
|
216
|
+
end
|
217
|
+
|
218
|
+
def llen(key)
|
219
|
+
@data[key] ||= []
|
220
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
221
|
+
@data[key].size
|
222
|
+
end
|
223
|
+
|
224
|
+
def lrange(key, startidx, endidx)
|
225
|
+
return unless @data[key]
|
226
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
227
|
+
@data[key][startidx..endidx]
|
228
|
+
end
|
229
|
+
|
230
|
+
def ltrim(key, start, stop)
|
231
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
232
|
+
return unless @data[key]
|
233
|
+
@data[key] = @data[key][start..stop]
|
234
|
+
end
|
235
|
+
|
236
|
+
def lindex(key, index)
|
237
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
238
|
+
return unless @data[key]
|
239
|
+
@data[key][index]
|
240
|
+
end
|
241
|
+
|
242
|
+
def linsert(key, where, pivot, value)
|
243
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
244
|
+
return unless @data[key]
|
245
|
+
index = @data[key].index(pivot)
|
246
|
+
case where
|
247
|
+
when :before then @data[key].insert(index, value)
|
248
|
+
when :after then @data[key].insert(index + 1, value)
|
249
|
+
else raise ArgumentError.new
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def lset(key, index, value)
|
254
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
255
|
+
return unless @data[key]
|
256
|
+
raise RuntimeError unless index < @data[key].size
|
257
|
+
@data[key][index] = value
|
258
|
+
end
|
259
|
+
|
260
|
+
def lrem(key, count, value)
|
261
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
262
|
+
return unless @data[key]
|
263
|
+
old_size = @data[key].size
|
264
|
+
if count == 0
|
265
|
+
@data[key].delete(value)
|
266
|
+
old_size - @data[key].size
|
267
|
+
else
|
268
|
+
array = count > 0 ? @data[key].dup : @data[key].reverse
|
269
|
+
count.abs.times{ array.delete_at(array.index(value) || array.length) }
|
270
|
+
@data[key] = count > 0 ? array.dup : array.reverse
|
271
|
+
old_size - @data[key].size
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def rpush(key, value)
|
276
|
+
@data[key] ||= []
|
277
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
278
|
+
@data[key].push(value)
|
279
|
+
end
|
280
|
+
|
281
|
+
def rpushx(key, value)
|
282
|
+
return unless @data[key]
|
283
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
284
|
+
rpush(key, value)
|
285
|
+
end
|
286
|
+
|
287
|
+
def lpush(key, value)
|
288
|
+
@data[key] ||= []
|
289
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
290
|
+
@data[key] = [value] + @data[key]
|
291
|
+
@data[key].size
|
292
|
+
end
|
293
|
+
|
294
|
+
def lpushx(key, value)
|
295
|
+
return unless @data[key]
|
296
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
297
|
+
lpush(key, value)
|
298
|
+
end
|
299
|
+
|
300
|
+
def rpop(key)
|
301
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
302
|
+
@data[key].pop
|
303
|
+
end
|
304
|
+
|
305
|
+
def rpoplpush(key1, key2)
|
306
|
+
fail "Not a list" unless @data[key1].is_a?(Array)
|
307
|
+
elem = @data[key1].pop
|
308
|
+
lpush(key2, elem)
|
309
|
+
end
|
310
|
+
|
311
|
+
def lpop(key)
|
312
|
+
return unless @data[key]
|
313
|
+
fail "Not a list" unless @data[key].is_a?(Array)
|
314
|
+
@data[key].delete_at(0)
|
315
|
+
end
|
316
|
+
|
317
|
+
def smembers(key)
|
318
|
+
fail_unless_set(key)
|
319
|
+
case set = @data[key]
|
320
|
+
when nil then []
|
321
|
+
when Set then set.to_a.reverse
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def sismember(key, value)
|
326
|
+
fail_unless_set(key)
|
327
|
+
case set = @data[key]
|
328
|
+
when nil then false
|
329
|
+
when Set then set.include?(value.to_s)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def sadd(key, value)
|
334
|
+
fail_unless_set(key)
|
335
|
+
case set = @data[key]
|
336
|
+
when nil then @data[key] = Set.new([value.to_s])
|
337
|
+
when Set then set.add(value.to_s)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def srem(key, value)
|
342
|
+
fail_unless_set(key)
|
343
|
+
case set = @data[key]
|
344
|
+
when nil then return
|
345
|
+
when Set then set.delete(value.to_s)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def smove(source, destination, value)
|
350
|
+
fail_unless_set(destination)
|
351
|
+
if elem = self.srem(source, value)
|
352
|
+
self.sadd(destination, value)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def spop(key)
|
357
|
+
fail_unless_set(key)
|
358
|
+
elem = srandmember(key)
|
359
|
+
srem(key, elem)
|
360
|
+
elem
|
361
|
+
end
|
362
|
+
|
363
|
+
def scard(key)
|
364
|
+
fail_unless_set(key)
|
365
|
+
case set = @data[key]
|
366
|
+
when nil then 0
|
367
|
+
when Set then set.size
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
def sinter(*keys)
|
372
|
+
keys.each { |k| fail_unless_set(k) }
|
373
|
+
return Set.new if keys.any? { |k| @data[k].nil? }
|
374
|
+
keys = keys.map { |k| @data[k] || Set.new }
|
375
|
+
keys.inject do |set, key|
|
376
|
+
set & key
|
377
|
+
end.to_a
|
378
|
+
end
|
379
|
+
|
380
|
+
def sinterstore(destination, *keys)
|
381
|
+
fail_unless_set(destination)
|
382
|
+
result = sinter(*keys)
|
383
|
+
@data[destination] = Set.new(result)
|
384
|
+
end
|
385
|
+
|
386
|
+
def sunion(*keys)
|
387
|
+
keys.each { |k| fail_unless_set(k) }
|
388
|
+
keys = keys.map { |k| @data[k] || Set.new }
|
389
|
+
keys.inject(Set.new) do |set, key|
|
390
|
+
set | key
|
391
|
+
end.to_a
|
392
|
+
end
|
393
|
+
|
394
|
+
def sunionstore(destination, *keys)
|
395
|
+
fail_unless_set(destination)
|
396
|
+
result = sunion(*keys)
|
397
|
+
@data[destination] = Set.new(result)
|
398
|
+
end
|
399
|
+
|
400
|
+
def sdiff(key1, *keys)
|
401
|
+
[key1, *keys].each { |k| fail_unless_set(k) }
|
402
|
+
keys = keys.map { |k| @data[k] || Set.new }
|
403
|
+
keys.inject(@data[key1]) do |memo, set|
|
404
|
+
memo - set
|
405
|
+
end.to_a
|
406
|
+
end
|
407
|
+
|
408
|
+
def sdiffstore(destination, key1, *keys)
|
409
|
+
fail_unless_set(destination)
|
410
|
+
result = sdiff(key1, *keys)
|
411
|
+
@data[destination] = Set.new(result)
|
412
|
+
end
|
413
|
+
|
414
|
+
def srandmember(key)
|
415
|
+
fail_unless_set(key)
|
416
|
+
case set = @data[key]
|
417
|
+
when nil then nil
|
418
|
+
when Set then set.to_a[rand(set.size)]
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
def del(*keys)
|
423
|
+
old_count = @data.keys.size
|
424
|
+
keys.flatten.each do |key|
|
425
|
+
@data.delete(key)
|
426
|
+
@expires.delete(key)
|
427
|
+
end
|
428
|
+
deleted_count = old_count - @data.keys.size
|
429
|
+
end
|
430
|
+
|
431
|
+
def setnx(key, value)
|
432
|
+
set(key, value) unless @data.key?(key)
|
433
|
+
end
|
434
|
+
|
435
|
+
def rename(key, new_key)
|
436
|
+
return unless @data[key]
|
437
|
+
@data[new_key] = @data[key]
|
438
|
+
@expires[new_key] = @expires[key]
|
439
|
+
@data.delete(key)
|
440
|
+
@expires.delete(key)
|
441
|
+
end
|
442
|
+
|
443
|
+
def renamenx(key, new_key)
|
444
|
+
rename(key, new_key) unless exists(new_key)
|
445
|
+
end
|
446
|
+
|
447
|
+
def expire(key, ttl)
|
448
|
+
return unless @data[key]
|
449
|
+
@expires[key] = ttl
|
450
|
+
true
|
451
|
+
end
|
452
|
+
|
453
|
+
def ttl(key)
|
454
|
+
@expires[key]
|
455
|
+
end
|
456
|
+
|
457
|
+
def expireat(key, timestamp)
|
458
|
+
@expires[key] = (Time.at(timestamp) - Time.now).to_i
|
459
|
+
true
|
460
|
+
end
|
461
|
+
|
462
|
+
def persist(key)
|
463
|
+
@expires[key] = -1
|
464
|
+
end
|
465
|
+
|
466
|
+
def hset(key, field, value)
|
467
|
+
case hash = @data[key]
|
468
|
+
when nil then @data[key] = { field => value.to_s }
|
469
|
+
when Hash then hash[field] = value.to_s
|
470
|
+
else fail "Not a hash"
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
def hsetnx(key, field, value)
|
475
|
+
return if (@data[key][field] rescue false)
|
476
|
+
hset(key, field, value)
|
477
|
+
end
|
478
|
+
|
479
|
+
def hmset(key, *fields)
|
480
|
+
@data[key] ||= {}
|
481
|
+
fail "Not a hash" unless @data[key].is_a?(Hash)
|
482
|
+
fields.each_slice(2) do |field|
|
483
|
+
@data[key][field[0].to_s] = field[1].to_s
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def hmget(key, *fields)
|
488
|
+
values = []
|
489
|
+
fields.each do |field|
|
490
|
+
case hash = @data[key]
|
491
|
+
when nil then values << nil
|
492
|
+
when Hash then values << hash[field]
|
493
|
+
else fail "Not a hash"
|
494
|
+
end
|
495
|
+
end
|
496
|
+
values
|
497
|
+
end
|
498
|
+
|
499
|
+
def hlen(key)
|
500
|
+
case hash = @data[key]
|
501
|
+
when nil then 0
|
502
|
+
when Hash then hash.size
|
503
|
+
else fail "Not a hash"
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def hvals(key)
|
508
|
+
case hash = @data[key]
|
509
|
+
when nil then []
|
510
|
+
when Hash then hash.values
|
511
|
+
else fail "Not a hash"
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def hincrby(key, field, increment)
|
516
|
+
case hash = @data[key]
|
517
|
+
when nil then @data[key] = { field => value.to_s }
|
518
|
+
when Hash then hash[field] = (hash[field].to_i + increment.to_i).to_s
|
519
|
+
else fail "Not a hash"
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
def hexists(key, field)
|
524
|
+
return unless @data[key]
|
525
|
+
fail "Not a hash" unless @data[key].is_a?(Hash)
|
526
|
+
@data[key].key?(field)
|
527
|
+
end
|
528
|
+
|
529
|
+
def monitor ; end
|
530
|
+
|
531
|
+
def sync ; end
|
532
|
+
|
533
|
+
def [](key)
|
534
|
+
get(key)
|
535
|
+
end
|
536
|
+
|
537
|
+
def []=(key, value)
|
538
|
+
set(key, value)
|
539
|
+
end
|
540
|
+
|
541
|
+
def set(key, value)
|
542
|
+
@data[key] = value.to_s
|
543
|
+
"OK"
|
544
|
+
end
|
545
|
+
|
546
|
+
def setbit(key, offset, bit)
|
547
|
+
return unless @data[key]
|
548
|
+
old_val = @data[key].unpack('B*')[0].split("")
|
549
|
+
old_val[offset] = bit.to_s
|
550
|
+
new_val = ""
|
551
|
+
old_val.each_slice(8){|b| new_val = new_val + b.join("").to_i(2).chr }
|
552
|
+
@data[key] = new_val
|
553
|
+
end
|
554
|
+
|
555
|
+
def setex(key, seconds, value)
|
556
|
+
@data[key] = value
|
557
|
+
expire(key, seconds)
|
558
|
+
end
|
559
|
+
|
560
|
+
def setrange(key, offset, value)
|
561
|
+
return unless @data[key]
|
562
|
+
s = @data[key][offset,value.size]
|
563
|
+
@data[key][s] = value
|
564
|
+
end
|
565
|
+
|
566
|
+
def mset(*pairs)
|
567
|
+
pairs.each_slice(2) do |pair|
|
568
|
+
@data[pair[0].to_s] = pair[1].to_s
|
569
|
+
end
|
570
|
+
"OK"
|
571
|
+
end
|
572
|
+
|
573
|
+
def msetnx(*pairs)
|
574
|
+
keys = []
|
575
|
+
pairs.each_with_index{|item, index| keys << item.to_s if index % 2 == 0}
|
576
|
+
return if keys.any?{|key| @data.key?(key) }
|
577
|
+
mset(*pairs)
|
578
|
+
true
|
579
|
+
end
|
580
|
+
|
581
|
+
def mapped_mget(*keys)
|
582
|
+
reply = mget(*keys)
|
583
|
+
Hash[*keys.zip(reply).flatten]
|
584
|
+
end
|
585
|
+
|
586
|
+
def sort(key)
|
587
|
+
# TODO: Impleent
|
588
|
+
end
|
589
|
+
|
590
|
+
def incr(key)
|
591
|
+
@data[key] = (@data[key] || "0")
|
592
|
+
@data[key] = (@data[key].to_i + 1).to_s
|
593
|
+
end
|
594
|
+
|
595
|
+
def incrby(key, by)
|
596
|
+
@data[key] = (@data[key] || "0")
|
597
|
+
@data[key] = (@data[key].to_i + by.to_i).to_s
|
598
|
+
end
|
599
|
+
|
600
|
+
def decr(key)
|
601
|
+
@data[key] = (@data[key] || "0")
|
602
|
+
@data[key] = (@data[key].to_i - 1).to_s
|
603
|
+
end
|
604
|
+
|
605
|
+
def decrby(key, by)
|
606
|
+
@data[key] = (@data[key] || "0")
|
607
|
+
@data[key] = (@data[key].to_i - by.to_i).to_s
|
608
|
+
end
|
609
|
+
|
610
|
+
def type(key)
|
611
|
+
case value = @data[key]
|
612
|
+
when nil then "none"
|
613
|
+
when String then "string"
|
614
|
+
when Hash then "hash"
|
615
|
+
when Array then "list"
|
616
|
+
when Set then "set"
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
def quit ; end
|
621
|
+
|
622
|
+
def shutdown; end
|
623
|
+
|
624
|
+
def slaveof(host, port) ; end
|
625
|
+
|
626
|
+
def exec
|
627
|
+
buffer = @buffer
|
628
|
+
@buffer = nil
|
629
|
+
buffer
|
630
|
+
end
|
631
|
+
|
632
|
+
def multi
|
633
|
+
@buffer = []
|
634
|
+
yield if block_given?
|
635
|
+
"OK"
|
636
|
+
end
|
637
|
+
|
638
|
+
private
|
639
|
+
def is_a_set?(key)
|
640
|
+
@data[key].is_a?(Set) || @data[key].nil?
|
641
|
+
end
|
642
|
+
|
643
|
+
def fail_unless_set(key)
|
644
|
+
fail "Not a set" unless is_a_set?(key)
|
645
|
+
end
|
646
|
+
|
647
|
+
def expired?(key)
|
648
|
+
return false if @expires[key] == -1
|
649
|
+
return true if @expires[key] && @expires[key] < Time.now
|
650
|
+
end
|
651
|
+
end
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
Redis::Connection.drivers << Redis::Connection::Memory
|