fakeredis 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  .rvmrc
6
+ *.rbc
data/.travis.yml CHANGED
@@ -4,4 +4,6 @@ rvm:
4
4
  - ree
5
5
  - rbx
6
6
  - jruby
7
+ - rbx-head
7
8
  - ruby-head
9
+ - rbx-2.0.0pre
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::Redis similary as you use redis gem:
18
+ You can use FakeRedis without any changes:
19
19
 
20
20
  require "fakeredis"
21
21
 
22
- redis = FakeRedis::Redis.new
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 redis-rb for your tests}
13
- s.description = %q{Fake implementation of redis-rb for machines without Redis or for testing purposes}
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
- class Redis
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"
@@ -1,3 +1,3 @@
1
1
  module FakeRedis
2
- VERSION = "0.1.4"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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