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