simredis 0.0.2

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.
Files changed (6) hide show
  1. data/README.rdoc +58 -0
  2. data/Rakefile +14 -0
  3. data/VERSION +1 -0
  4. data/simredis.gemspec +41 -0
  5. data/simredis.rb +597 -0
  6. metadata +59 -0
data/README.rdoc ADDED
@@ -0,0 +1,58 @@
1
+ = SimRedis (Pure Ruby Redis simulator for redis-rb)
2
+
3
+ * http://github.com/peterc/simredis
4
+
5
+ == STATUS:
6
+
7
+ SimRedis is NOT READY FOR ANY SORT OF SERIOUS USE YET! Currently all of the generic and string methods are working OK, except for anything involving expiration or blocking.
8
+
9
+ == DESCRIPTION:
10
+
11
+ SimRedis is a Redis simulator for the popular redis-rb library. SimRedis lets you use the Redis Ruby client library without a Redis daemon running. Everything is stored in memory only and is very thread unsafe, but persistence and locking comes in time.
12
+
13
+ Why simulate Redis? I love Redis and some of the ideas it brings to the table, and while I can roll out onto servers stocked with working Redis installs OK, sometimes I might want to still use Redis in my app but not require its full power (or I might want to deploy somewhere that doesn't have Redis - like Dreamhost, say). I'd also like to distribute apps I make so that other people can use them without being forced to install a whole new daemon first.
14
+
15
+ == EXAMPLES:
16
+
17
+ require 'redis'
18
+ require 'simredis'
19
+
20
+ r = Redis.new
21
+ r.set("a", "hello world")
22
+ r.get("a") # => "hello world"
23
+ r.keys("*") # => ['a']
24
+ r.flushdb
25
+ r.keys("*") # => []
26
+
27
+ SimRedis comes with tests (of a sort) "baked in." Running the simredis.rb file directly runs the suite.
28
+
29
+ == Note on Patches/Pull Requests
30
+
31
+ * Fork the project.
32
+ * Make your feature addition or bug fix.
33
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
34
+ * Commit, do not mess with Rakefile, version, or history as it's handled by Jeweler (which is awesome, btw).
35
+ * Send me a pull request. I may or may not accept it (sorry, practicality rules.. but message me and we can talk!)
36
+
37
+ == COPYRIGHT AND LICENSE
38
+
39
+ Copyright (c) 2010 Peter Cooper
40
+
41
+ Permission is hereby granted, free of charge, to any person obtaining
42
+ a copy of this software and associated documentation files (the
43
+ "Software"), to deal in the Software without restriction, including
44
+ without limitation the rights to use, copy, modify, merge, publish,
45
+ distribute, sublicense, and/or sell copies of the Software, and to
46
+ permit persons to whom the Software is furnished to do so, subject to
47
+ the following conditions:
48
+
49
+ The above copyright notice and this permission notice shall be
50
+ included in all copies or substantial portions of the Software.
51
+
52
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
53
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
54
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
55
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
56
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
57
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
58
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "simredis"
5
+ gemspec.summary = "Redis 'simulator' that allows you to use redis-rb without a Redis daemon running"
6
+ gemspec.description = "Redis 'simulator' that allows you to use redis-rb without a Redis daemon running. Useful in situations where you want to deploy basic Redis-based apps quickly or to people who haven't got the ability to set up the Redis daemon."
7
+ gemspec.email = "simredis@peterc.org"
8
+ gemspec.homepage = "http://github.com/peterc/simredis"
9
+ gemspec.authors = ["Peter Cooper"]
10
+ end
11
+ Jeweler::GemcutterTasks.new
12
+ rescue LoadError
13
+ puts "Jeweler not available. Install it with: gem install jeweler"
14
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
data/simredis.gemspec ADDED
@@ -0,0 +1,41 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{simredis}
8
+ s.version = "0.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Peter Cooper"]
12
+ s.date = %q{2010-05-16}
13
+ s.description = %q{Redis 'simulator' that allows you to use redis-rb without a Redis daemon running. Useful in situations where you want to deploy basic Redis-based apps quickly or to people who haven't got the ability to set up the Redis daemon.}
14
+ s.email = %q{simredis@peterc.org}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "README.rdoc",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "simredis.gemspec",
23
+ "simredis.rb"
24
+ ]
25
+ s.homepage = %q{http://github.com/peterc/simredis}
26
+ s.rdoc_options = ["--charset=UTF-8"]
27
+ s.require_paths = ["lib"]
28
+ s.rubygems_version = %q{1.3.5}
29
+ s.summary = %q{Redis 'simulator' that allows you to use redis-rb without a Redis daemon running}
30
+
31
+ if s.respond_to? :specification_version then
32
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
33
+ s.specification_version = 3
34
+
35
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
36
+ else
37
+ end
38
+ else
39
+ end
40
+ end
41
+
data/simredis.rb ADDED
@@ -0,0 +1,597 @@
1
+ require 'rubygems'
2
+ require 'redis'
3
+
4
+ class Redis
5
+ class Emulator
6
+ PREFIX = {
7
+ :error => "-",
8
+ :status => "+",
9
+ :integer => ":",
10
+ :bulk => "$",
11
+ :multi_bulk => "*"
12
+ }
13
+
14
+ STATUS = lambda { |m| [PREFIX[:status], m.to_s] }
15
+ ERROR = lambda { |m| [PREFIX[:error], m.to_s] }
16
+ BULK = lambda { |m|
17
+ if m
18
+ [PREFIX[:bulk], m.to_s.length.to_s, m.to_s]
19
+ else
20
+ [PREFIX[:bulk], -1]
21
+ end
22
+ }
23
+ INTEGER = lambda { |m| [PREFIX[:integer], m.to_i] }
24
+ MULTI_BULK = lambda { |m|
25
+ response = [PREFIX[:multi_bulk], m.length.to_s]
26
+ m.each do |m1|
27
+ if m1
28
+ response += [PREFIX[:bulk], m1.to_s.length.to_s, m1.to_s, "\r\n"]
29
+ else
30
+ response += [PREFIX[:bulk], -1]
31
+ end
32
+ end
33
+ response
34
+ }
35
+
36
+ def initialize
37
+ flushall
38
+ select([0])
39
+ end
40
+
41
+ # ---- CONNECTION HANDLING COMMANDS
42
+
43
+ # http://code.google.com/p/redis/wiki/QuitCommand
44
+ def quit(args, data); end
45
+
46
+ # http://code.google.com/p/redis/wiki/AuthCommand
47
+ def auth(args, data)
48
+ # TODO: Actually do authentication. Ignore for now.
49
+ STATUS[:OK]
50
+ end
51
+
52
+ # ---- GENERIC COMMANDS
53
+
54
+ def exists(args)
55
+ [PREFIX[:integer], @data[args.first] ? "1" : "0"]
56
+ end
57
+
58
+ # http://code.google.com/p/redis/wiki/DelCommand
59
+ def del(args)
60
+ removed = 0
61
+ args.each do |arg|
62
+ removed += 1 if @data[arg]
63
+ @data.delete(arg)
64
+ end
65
+
66
+ INTEGER[removed]
67
+ end
68
+
69
+ def type(args)
70
+ return STATUS[:none] unless @data[args.first]
71
+ return STATUS[:string] if String === @data[args.first]
72
+ return STATUS[:list] if Array === @data[args.first]
73
+ return STATUS[:set] if Set === @data[args.first]
74
+ return STATUS[:zset] if SortedSet === @data[args.first]
75
+ return STATUS[:hash] if Hash === @data[args.first]
76
+ end
77
+
78
+ def keys(args)
79
+ regex = args.first.gsub('?', '.').gsub('*', '.*')
80
+ MULTI_BULK[@data.keys.select { |k| k =~ /#{regex}/ }]
81
+ end
82
+
83
+ def randomkey(args)
84
+ STATUS[@data.keys[rand(@data.size)]]
85
+ end
86
+
87
+ def rename(args)
88
+ from, to = args
89
+ @data[to] = @data[from]
90
+ @data.delete(from)
91
+ STATUS[:OK]
92
+ end
93
+
94
+ def renamenx(args)
95
+ from, to = args
96
+ return INTEGER[0] if @data[to]
97
+ rename(args)
98
+ INTEGER[1]
99
+ end
100
+
101
+ def dbsize(args)
102
+ INTEGER[@data.size]
103
+ end
104
+
105
+ # TODO: Implement!
106
+ def expire(args)
107
+ end
108
+
109
+ # TODO: Implement!
110
+ def ttl(args)
111
+ end
112
+
113
+ def select(args = nil, data = nil)
114
+ @current_db = args.first.to_i
115
+ @data = @datasets[@current_db]
116
+ STATUS[:OK]
117
+ end
118
+
119
+ def move(args)
120
+ key, dbindex = args
121
+ return INTEGER[0] if !@data[key]
122
+ return INTEGER[0] if @datasets[dbindex.to_i][key]
123
+ @datasets[dbindex.to_i][key] = @data[key]
124
+ @data.delete(key)
125
+ INTEGER[1]
126
+ end
127
+
128
+ def flushdb(args = nil, data = nil)
129
+ @data = {}
130
+ STATUS[:OK]
131
+ end
132
+
133
+ def flushall(args = nil, data = nil)
134
+ @datasets = Array.new(32).map { |el| Hash.new }
135
+ @data = @datasets[@current_db || 0]
136
+ STATUS[:OK]
137
+ end
138
+
139
+ # ---- STRING COMMANDS
140
+
141
+ # http://code.google.com/p/redis/wiki/SetCommand
142
+ def set(args)
143
+ @data[args.shift] = args.shift
144
+ STATUS[:OK]
145
+ end
146
+
147
+ # http://code.google.com/p/redis/wiki/GetCommand
148
+ def get(args)
149
+ if @data[args.first]
150
+ BULK[@data[args.first]]
151
+ else
152
+ [PREFIX[:bulk], -1]
153
+ end
154
+ end
155
+
156
+ def getset(args)
157
+ key, value = args
158
+ old_data = @data[args.first]
159
+ p old_data
160
+ set(args)
161
+ if old_data
162
+ BULK[old_data]
163
+ else
164
+ [PREFIX[:bulk], -1]
165
+ end
166
+ end
167
+
168
+ def mget(args)
169
+ MULTI_BULK[args.map { |a| @data[a] }]
170
+ end
171
+
172
+ def setnx(args)
173
+ return INTEGER[0] if @data[args.first]
174
+ set(args)
175
+ INTEGER[1]
176
+ end
177
+
178
+ # TODO: SETEX depends on EXPIRE and TTL
179
+ def setex(args)
180
+ end
181
+
182
+ def mset(args)
183
+ until args.empty?
184
+ key, value = args.shift, args.shift
185
+ set([key, value])
186
+ end
187
+ STATUS[:OK]
188
+ end
189
+
190
+ def msetnx(args)
191
+ to_set = {}
192
+ until args.empty?
193
+ key, value = args.shift, args.shift
194
+ return INTEGER[0] if @data[key]
195
+ to_set[key] = value
196
+ end
197
+
198
+ to_set.each do |key, value|
199
+ set([key, value])
200
+ end
201
+
202
+ INTEGER[1]
203
+ end
204
+
205
+ def incr(args)
206
+ key = args.shift
207
+
208
+ unless @data[key]
209
+ @data[key] = 1
210
+ return INTEGER[1]
211
+ end
212
+
213
+ if @data[key] && (@data[key].to_i.to_s == @data[key].to_s)
214
+ @data[key] = @data[key].to_i + 1
215
+ return INTEGER[@data[key]]
216
+ end
217
+
218
+ ERROR["ERR value is not an integer"]
219
+ end
220
+
221
+ def incrby(args)
222
+ key = args.shift
223
+ by = args.shift.to_i
224
+
225
+ unless @data[key]
226
+ @data[key] = by
227
+ return INTEGER[by]
228
+ end
229
+
230
+ if @data[key] && (@data[key].to_i.to_s == @data[key].to_s)
231
+ @data[key] = @data[key].to_i + by.to_i
232
+ return INTEGER[@data[key]]
233
+ end
234
+
235
+ ERROR["ERR value is not an integer"]
236
+ end
237
+
238
+ def decr(args)
239
+ key = args.shift
240
+
241
+ unless @data[key]
242
+ @data[key] = -1
243
+ return INTEGER[-1]
244
+ end
245
+
246
+ if @data[key] && (@data[key].to_i.to_s == @data[key].to_s)
247
+ @data[key] = @data[key].to_i - 1
248
+ return INTEGER[@data[key]]
249
+ end
250
+
251
+ ERROR["ERR value is not an integer"]
252
+ end
253
+
254
+ def decrby(args)
255
+ key = args.shift
256
+ by = args.shift.to_i
257
+
258
+ unless @data[key]
259
+ @data[key] = -by
260
+ return INTEGER[-by]
261
+ end
262
+
263
+ if @data[key] && (@data[key].to_i.to_s == @data[key].to_s)
264
+ @data[key] = @data[key].to_i + -by.to_i
265
+ return INTEGER[@data[key]]
266
+ end
267
+
268
+ ERROR["ERR value is not an integer"]
269
+ end
270
+
271
+ def append(args)
272
+ key, value = args
273
+
274
+ @data[key] ||= ''
275
+ @data[key] += value
276
+
277
+ INTEGER[@data[key].length]
278
+ end
279
+
280
+ def substr(args)
281
+ key, startp, endp = args
282
+ return BULK[[nil]] unless @data[key]
283
+ return BULK[@data[key].to_s[startp.to_i..endp.to_i]]
284
+ end
285
+
286
+ # ---- LIST COMMANDS
287
+
288
+ def rpush(args)
289
+ key, string = args
290
+ @data[key] ||= []
291
+ return ERROR["ERR Operation against a key holding the wrong kind of value"] unless Array === @data[key]
292
+ @data[key] << string
293
+ INTEGER[@data[key].length]
294
+ end
295
+
296
+ def lpush(args)
297
+ key, string = args
298
+ @data[key] ||= []
299
+ return ERROR["ERR Operation against a key holding the wrong kind of value"] unless Array === @data[key]
300
+ @data[key].unshift(string)
301
+ INTEGER[@data[key].length]
302
+ end
303
+
304
+ def llen(args)
305
+ key = args.shift
306
+ return INTEGER[0] unless @data[key] && Array === @data[key]
307
+ return INTEGER[@data[key].length]
308
+ end
309
+
310
+ def lrange(args)
311
+ key, startp, endp = args
312
+ return MULTI_BULK[[]] unless @data[key] && Array === @data[key]
313
+ return MULTI_BULK[@data[key][startp.to_i..endp.to_i]]
314
+ end
315
+
316
+ def ltrim(args)
317
+ key, startp, endp = args
318
+ if @data[key] && Array === @data[key]
319
+ @data[key] = @data[key][startp.to_i..endp.to_i]
320
+ end
321
+ return STATUS[:OK]
322
+ end
323
+
324
+ def lindex(args)
325
+ key, index = args
326
+ return BULK[[nil]] unless @data[key]
327
+ return BULK[@data[key][index.to_i]]
328
+ end
329
+
330
+ def lset(args)
331
+ key, index, value = args
332
+ return ERROR["ERR no such key"] unless @data[key]
333
+ return ERROR["ERR Operation against a key holding the wrong kind of value"] unless Array === @data[key]
334
+ return ERROR["ERR index out of range"] if index.to_i < 0 || index.to_i > @data[key].length - 1
335
+ @data[key][index.to_i] = value
336
+ STATUS[:OK]
337
+ end
338
+
339
+ def lrem(args)
340
+ key, count, value = args
341
+ return ERROR["ERR no such key"] unless @data[key]
342
+ return ERROR["ERR Operation against a key holding the wrong kind of value"] unless Array === @data[key]
343
+
344
+ old_length = @data[key].size
345
+
346
+ count.to_i.times do |i|
347
+ if posi = @data[key].index(value.to_s)
348
+ @data[key].delete_at(posi)
349
+ end
350
+ end
351
+
352
+ INTEGER[old_length - @data[key].size]
353
+ end
354
+
355
+ def lpop(args)
356
+ key = args.first
357
+ return ERROR["ERR no such key"] unless @data[key]
358
+ return ERROR["ERR Operation against a key holding the wrong kind of value"] unless Array === @data[key]
359
+ BULK[@data[key].shift]
360
+ end
361
+
362
+ def rpop(args)
363
+ key = args.first
364
+ return ERROR["ERR no such key"] unless @data[key]
365
+ return ERROR["ERR Operation against a key holding the wrong kind of value"] unless Array === @data[key]
366
+ BULK[@data[key].pop]
367
+ end
368
+
369
+ # TODO: BLocking POPs.. not sure if it's worth it given this is just an emulator, though.. :-)
370
+ #def blpop(args)
371
+ # timeout = args.pop
372
+ #end
373
+ #
374
+ #def brpop(args)
375
+ # timeout = args.pop
376
+ #end
377
+
378
+ def rpoplpush(args)
379
+ skey, dkey = args
380
+ return BULK[nil] unless @data[skey] && Array === @data[skey]
381
+ @data[dkey] ||= []
382
+ data = @data[skey].pop
383
+ @data[dkey].unshift(data)
384
+ BULK[data]
385
+ end
386
+ end
387
+
388
+
389
+
390
+
391
+ # redis-rb uses TCPSocket to communicate with the Redis daemon
392
+ # If we put another TCPSocket /closer/ to Redis, this one will take
393
+ # precedence, but not trample over other TCPSocket classes outside of redis-rb
394
+ class TCPSocket
395
+ def initialize(host, port)
396
+ @emulator = Emulator.new
397
+ @output_queue = []
398
+ end
399
+
400
+ def write(data)
401
+ puts "SEND: #{data.inspect}" if TESTING
402
+ cmd, *data = data.split("\r\n")
403
+ if cmd =~ /^\*/
404
+ data.delete_if { |d| d =~ /^\$/ }
405
+ cmd = data.shift
406
+ args = data
407
+ #p cmd
408
+ #p args
409
+ else
410
+ cmd, *args = cmd.split(' ')
411
+ end
412
+ @output_queue += @emulator.send(cmd.downcase.to_sym, args)
413
+ puts "RECV: #{@output_queue.inspect}" if TESTING
414
+ end
415
+
416
+ def read(b); @output_queue.shift; end
417
+ def gets; @output_queue.shift; end
418
+ def setsockopt(*ignored); end
419
+ end
420
+ end
421
+
422
+
423
+ if __FILE__ == $0
424
+ # Poor man's testing
425
+ def assert(c)
426
+ #$an ||= 0
427
+ #puts $an += 1
428
+ abort unless c
429
+ puts
430
+ end
431
+
432
+ TESTING = true
433
+ r = Redis.new(:db => 10)
434
+
435
+ # Clear everything
436
+ assert r.flushall
437
+
438
+ # Ensure the "a" key does not exist, set it, re-set it, then check its value, existance, and type
439
+ assert !r.exists("a")
440
+ assert r.set("a", 10)
441
+ assert r.set("a", 50)
442
+ assert r.get("a") == "50"
443
+ assert r.exists("a")
444
+ assert r.type("a") == "string"
445
+
446
+ # Pick a random key - there's only one so far, of course
447
+ assert r.randomkey == "a"
448
+
449
+ # Remove "a" and then ensure it has no type, does not exist, and is not in the keys list
450
+ assert r.del("a")
451
+ assert r.type("a") == "none"
452
+ assert !r.exists("a")
453
+ assert r.keys("*") == []
454
+
455
+ # Set keys "a" and "b" and do key lists to prove they exist
456
+ assert r.set("a", "this is a test")
457
+ assert r.keys("*") == %w{a}
458
+ assert r.set("b", "this is a test too")
459
+ assert r.keys("*") == %w{a b}
460
+
461
+ # Flush this specific database and ensure it worked
462
+ assert r.flushdb
463
+ assert r.keys("*") == []
464
+ assert r.dbsize == 0
465
+
466
+ # Test renaming features
467
+ assert r.set("a", 10)
468
+ assert r.rename("a", "b")
469
+ assert r.get("b") == "10"
470
+ assert !r.get("a")
471
+ assert r.set("c", "hello world")
472
+ assert !r.renamenx("c", "b")
473
+ assert r.renamenx("c", "a")
474
+ assert r.type("c") == "none"
475
+
476
+ # Test moving - in a basic way since redis-rb won't let us select back and forth easily
477
+ assert r.set("a", 10)
478
+ assert r.move("a", 2)
479
+ assert !r.exists("a")
480
+ assert r.flushall
481
+
482
+ # Test getset to both non-existing and pre-existing keys
483
+ assert r.getset("b", 10) == nil
484
+ assert r.get("b") == "10"
485
+ assert r.set("a", "x")
486
+ assert r.getset("a", "y") == "x"
487
+ assert r.flushdb
488
+
489
+ # Test mget (multiget)
490
+ assert r.set("a", 100)
491
+ assert r.set("b", 200)
492
+ assert r.set("c", 300)
493
+ assert r.mget("a", "b", "c") == %w{100 200 300}
494
+ assert r.del("b")
495
+ p r.mget("a", "b", "c")
496
+ assert r.mget("a", "b", "c") == ["100", nil, "300"]
497
+
498
+ # Test setnx (set but not if key already exists)
499
+ assert r.setnx("abc", 10)
500
+ assert r.get("abc") == "10"
501
+ assert !r.setnx("abc", 20)
502
+ assert r.get("abc") == "10"
503
+ assert r.flushdb
504
+
505
+ # Test mset (multi set)
506
+ assert r.mset("a", 100, "b", 200, "c", 300)
507
+ assert r.mget("a", "b", "c") == %w{100 200 300}
508
+ assert r.flushdb
509
+
510
+ # Test msetnx (multi set but not if present)
511
+ assert r.msetnx("a", 100, "b", 200, "c", 300) == 1
512
+ assert r.mget("a", "b", "c") == %w{100 200 300}
513
+ assert r.msetnx("a", 1, "b", 2, "c", 3) == 0
514
+ assert r.mget("a", "b", "c") == %w{100 200 300}
515
+ assert r.flushdb
516
+
517
+ # Test incr, decr, incrby and decrby
518
+ assert r.incr("a") == 1
519
+ assert r.incr("a") == 2
520
+ assert r.set("b", "xyz")
521
+ assert((r.incr("b") rescue :freakout) == :freakout)
522
+ assert r.incrby("a", 2) == 4
523
+ assert r.decr("a") == 3
524
+ assert r.decrby("a", 4) == -1
525
+ assert r.decrby("xxx", 10) == -10
526
+ assert r.incrby("xxx2", 10) == 10
527
+ assert r.flushdb
528
+
529
+ # Test append (test taken right from the Redis wiki.. :-)
530
+ assert !r.exists("mykey")
531
+ assert r.append("mykey", "Hello ") == 6
532
+ assert r.append("mykey", "World") == 11
533
+ assert r.get("mykey") == "Hello World"
534
+
535
+ # Test substr
536
+ assert r.set("s", "This is a string")
537
+ assert r.substr("s", 0, 3) == "This"
538
+ assert r.substr("s", -3, -1) == "ing"
539
+ assert r.substr("s", 0, -1) == "This is a string"
540
+ assert r.substr("s", 9, 100000) == " string"
541
+
542
+ # Test rpush, lpush, llen, ltrim, lrange, lindex, and lset
543
+ assert r.flushdb
544
+ assert r.lpush("a", "hello2") == 1
545
+ assert r.lpush("a", "hello1") == 2
546
+ assert r.llen("a") == 2
547
+ assert r.rpush("a", "hello3") == 3
548
+ assert r.llen("a") == 3
549
+ assert r.lrange("a", 0, -1) == %w{hello1 hello2 hello3}
550
+ assert r.lrange("a", 1, 2) == %w{hello2 hello3}
551
+ assert r.ltrim("a", 1, 2)
552
+ assert r.lrange("a", 0, -1) == %w{hello2 hello3}
553
+ assert r.lindex("a", 0) == "hello2"
554
+ assert r.lindex("a", 1) == "hello3"
555
+ assert r.lindex("a", -2) == "hello2"
556
+ assert r.lset("a", 1, "hello4")
557
+ assert r.lindex("a", 1) == "hello4"
558
+ assert((r.lset("a", 100, "hello4") rescue :freakout) == :freakout)
559
+ assert((r.lset("c", 100, "hello4") rescue :freakout) == :freakout)
560
+ assert r.flushdb
561
+
562
+ # Test lrem, lpop, rpop, blpop, brpop, and rpoplpush
563
+ assert r.rpush("a", "a")
564
+ assert r.rpush("a", "b")
565
+ assert r.rpush("a", "a")
566
+ assert r.rpush("a", "c")
567
+ assert r.rpush("a", "a")
568
+ assert r.lrem("a", 1, "a") == 1
569
+ assert r.lrange("a", 0, -1) == %w{b a c a}
570
+ assert r.lrem("a", 3, "a") == 2
571
+ assert r.lrange("a", 0, -1) == %w{b c}
572
+ assert r.rpush("a", "a")
573
+ # Do a little side test to make sure numbers don't escape the wrath of lrem
574
+ assert r.rpush("a", 20)
575
+ assert r.rpush("a", 40)
576
+ assert r.lrange("a", 0, -1) == %w{b c a 20 40}
577
+ assert r.lrem("a", 1, 20)
578
+ assert r.lrange("a", 0, -1) == %w{b c a 40}
579
+ # Resume with popping tests!
580
+ assert r.lpop("a") == "b"
581
+ assert r.lrange("a", 0, -1) == %w{c a 40}
582
+ assert r.rpop("a") == "40"
583
+ assert r.lrange("a", 0, -1) == %w{c a}
584
+ assert r.flushdb
585
+
586
+ # Test rpoplpush
587
+ assert r.rpush("a", "a")
588
+ assert r.rpush("a", "b")
589
+ assert r.rpush("a", "c")
590
+ assert r.rpoplpush("a", "b") == "c"
591
+ assert r.lrange("a", 0, -1) == %w{a b}
592
+ p r.lrange("b", 0, -1)
593
+ assert r.lrange("b", 0, -1) == %w{c}
594
+
595
+
596
+ puts "BINGO!"
597
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simredis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Peter Cooper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-05-16 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Redis 'simulator' that allows you to use redis-rb without a Redis daemon running. Useful in situations where you want to deploy basic Redis-based apps quickly or to people who haven't got the ability to set up the Redis daemon.
17
+ email: simredis@peterc.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - README.rdoc
26
+ - Rakefile
27
+ - VERSION
28
+ - simredis.gemspec
29
+ - simredis.rb
30
+ has_rdoc: true
31
+ homepage: http://github.com/peterc/simredis
32
+ licenses: []
33
+
34
+ post_install_message:
35
+ rdoc_options:
36
+ - --charset=UTF-8
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ requirements: []
52
+
53
+ rubyforge_project:
54
+ rubygems_version: 1.3.5
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Redis 'simulator' that allows you to use redis-rb without a Redis daemon running
58
+ test_files: []
59
+