ryansch-mock_redis 0.2.0.1

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 (138) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +1 -0
  3. data/CHANGELOG.md +20 -0
  4. data/Gemfile +9 -0
  5. data/LICENSE +19 -0
  6. data/README.md +94 -0
  7. data/Rakefile +10 -0
  8. data/lib/mock_redis.rb +32 -0
  9. data/lib/mock_redis/assertions.rb +13 -0
  10. data/lib/mock_redis/database.rb +432 -0
  11. data/lib/mock_redis/exceptions.rb +3 -0
  12. data/lib/mock_redis/expire_wrapper.rb +25 -0
  13. data/lib/mock_redis/hash_methods.rb +102 -0
  14. data/lib/mock_redis/list_methods.rb +187 -0
  15. data/lib/mock_redis/multi_db_wrapper.rb +86 -0
  16. data/lib/mock_redis/set_methods.rb +125 -0
  17. data/lib/mock_redis/string_methods.rb +195 -0
  18. data/lib/mock_redis/transaction_wrapper.rb +80 -0
  19. data/lib/mock_redis/undef_redis_methods.rb +11 -0
  20. data/lib/mock_redis/utility_methods.rb +22 -0
  21. data/lib/mock_redis/version.rb +3 -0
  22. data/lib/mock_redis/zset.rb +110 -0
  23. data/lib/mock_redis/zset_methods.rb +209 -0
  24. data/mock_redis.gemspec +24 -0
  25. data/spec/cloning_spec.rb +96 -0
  26. data/spec/commands/append_spec.rb +24 -0
  27. data/spec/commands/auth_spec.rb +7 -0
  28. data/spec/commands/bgrewriteaof_spec.rb +7 -0
  29. data/spec/commands/bgsave_spec.rb +7 -0
  30. data/spec/commands/blpop_spec.rb +55 -0
  31. data/spec/commands/brpop_spec.rb +54 -0
  32. data/spec/commands/brpoplpush_spec.rb +53 -0
  33. data/spec/commands/dbsize_spec.rb +18 -0
  34. data/spec/commands/decr_spec.rb +34 -0
  35. data/spec/commands/decrby_spec.rb +34 -0
  36. data/spec/commands/del_spec.rb +20 -0
  37. data/spec/commands/echo_spec.rb +11 -0
  38. data/spec/commands/exists_spec.rb +14 -0
  39. data/spec/commands/expire_spec.rb +83 -0
  40. data/spec/commands/expireat_spec.rb +48 -0
  41. data/spec/commands/flushall_spec.rb +38 -0
  42. data/spec/commands/flushdb_spec.rb +38 -0
  43. data/spec/commands/get_spec.rb +23 -0
  44. data/spec/commands/getbit_spec.rb +34 -0
  45. data/spec/commands/getrange_spec.rb +22 -0
  46. data/spec/commands/getset_spec.rb +23 -0
  47. data/spec/commands/hdel_spec.rb +35 -0
  48. data/spec/commands/hexists_spec.rb +22 -0
  49. data/spec/commands/hget_spec.rb +23 -0
  50. data/spec/commands/hgetall_spec.rb +22 -0
  51. data/spec/commands/hincrby_spec.rb +52 -0
  52. data/spec/commands/hkeys_spec.rb +19 -0
  53. data/spec/commands/hlen_spec.rb +19 -0
  54. data/spec/commands/hmget_spec.rb +30 -0
  55. data/spec/commands/hmset_spec.rb +43 -0
  56. data/spec/commands/hset_spec.rb +23 -0
  57. data/spec/commands/hsetnx_spec.rb +39 -0
  58. data/spec/commands/hvals_spec.rb +19 -0
  59. data/spec/commands/incr_spec.rb +34 -0
  60. data/spec/commands/incrby_spec.rb +44 -0
  61. data/spec/commands/info_spec.rb +13 -0
  62. data/spec/commands/keys_spec.rb +87 -0
  63. data/spec/commands/lastsave_spec.rb +8 -0
  64. data/spec/commands/lindex_spec.rb +39 -0
  65. data/spec/commands/linsert_spec.rb +68 -0
  66. data/spec/commands/llen_spec.rb +16 -0
  67. data/spec/commands/lpop_spec.rb +34 -0
  68. data/spec/commands/lpush_spec.rb +30 -0
  69. data/spec/commands/lpushx_spec.rb +33 -0
  70. data/spec/commands/lrange_spec.rb +35 -0
  71. data/spec/commands/lrem_spec.rb +79 -0
  72. data/spec/commands/lset_spec.rb +38 -0
  73. data/spec/commands/ltrim_spec.rb +35 -0
  74. data/spec/commands/mget_spec.rb +34 -0
  75. data/spec/commands/move_spec.rb +147 -0
  76. data/spec/commands/mset_spec.rb +29 -0
  77. data/spec/commands/msetnx_spec.rb +40 -0
  78. data/spec/commands/persist_spec.rb +49 -0
  79. data/spec/commands/ping_spec.rb +7 -0
  80. data/spec/commands/quit_spec.rb +7 -0
  81. data/spec/commands/randomkey_spec.rb +20 -0
  82. data/spec/commands/rename_spec.rb +31 -0
  83. data/spec/commands/renamenx_spec.rb +36 -0
  84. data/spec/commands/rpop_spec.rb +34 -0
  85. data/spec/commands/rpoplpush_spec.rb +45 -0
  86. data/spec/commands/rpush_spec.rb +30 -0
  87. data/spec/commands/rpushx_spec.rb +33 -0
  88. data/spec/commands/sadd_spec.rb +22 -0
  89. data/spec/commands/save_spec.rb +7 -0
  90. data/spec/commands/scard_spec.rb +18 -0
  91. data/spec/commands/sdiff_spec.rb +47 -0
  92. data/spec/commands/sdiffstore_spec.rb +58 -0
  93. data/spec/commands/select_spec.rb +53 -0
  94. data/spec/commands/set_spec.rb +7 -0
  95. data/spec/commands/setbit_spec.rb +46 -0
  96. data/spec/commands/setex_spec.rb +22 -0
  97. data/spec/commands/setnx_spec.rb +25 -0
  98. data/spec/commands/setrange_spec.rb +30 -0
  99. data/spec/commands/sinter_spec.rb +41 -0
  100. data/spec/commands/sinterstore_spec.rb +53 -0
  101. data/spec/commands/sismember_spec.rb +29 -0
  102. data/spec/commands/smembers_spec.rb +18 -0
  103. data/spec/commands/smove_spec.rb +41 -0
  104. data/spec/commands/spop_spec.rb +25 -0
  105. data/spec/commands/srandmember_spec.rb +25 -0
  106. data/spec/commands/srem_spec.rb +35 -0
  107. data/spec/commands/strlen_spec.rb +19 -0
  108. data/spec/commands/sunion_spec.rb +40 -0
  109. data/spec/commands/sunionstore_spec.rb +53 -0
  110. data/spec/commands/ttl_spec.rb +36 -0
  111. data/spec/commands/type_spec.rb +36 -0
  112. data/spec/commands/unwatch_spec.rb +7 -0
  113. data/spec/commands/watch_spec.rb +7 -0
  114. data/spec/commands/zadd_spec.rb +29 -0
  115. data/spec/commands/zcard_spec.rb +19 -0
  116. data/spec/commands/zcount_spec.rb +23 -0
  117. data/spec/commands/zincrby_spec.rb +24 -0
  118. data/spec/commands/zinterstore_spec.rb +96 -0
  119. data/spec/commands/zrange_spec.rb +31 -0
  120. data/spec/commands/zrangebyscore_spec.rb +68 -0
  121. data/spec/commands/zrank_spec.rb +23 -0
  122. data/spec/commands/zrem_spec.rb +25 -0
  123. data/spec/commands/zremrangebyrank_spec.rb +22 -0
  124. data/spec/commands/zremrangebyscore_spec.rb +28 -0
  125. data/spec/commands/zrevrange_spec.rb +31 -0
  126. data/spec/commands/zrevrangebyscore_spec.rb +47 -0
  127. data/spec/commands/zrevrank_spec.rb +23 -0
  128. data/spec/commands/zscore_spec.rb +16 -0
  129. data/spec/commands/zunionstore_spec.rb +104 -0
  130. data/spec/spec_helper.rb +44 -0
  131. data/spec/support/redis_multiplexer.rb +91 -0
  132. data/spec/support/shared_examples/only_operates_on_hashes.rb +13 -0
  133. data/spec/support/shared_examples/only_operates_on_lists.rb +13 -0
  134. data/spec/support/shared_examples/only_operates_on_sets.rb +13 -0
  135. data/spec/support/shared_examples/only_operates_on_strings.rb +13 -0
  136. data/spec/support/shared_examples/only_operates_on_zsets.rb +57 -0
  137. data/spec/transactions_spec.rb +96 -0
  138. metadata +361 -0
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ -c -d
@@ -0,0 +1,20 @@
1
+ ### 0.2.0
2
+ * Support passing a block to `#multi`.
3
+
4
+ ### 0.1.2
5
+ * Fixes for 1.9.2; no functionality changes.
6
+
7
+ ### 0.1.1
8
+ * Fix handling of -inf, +inf, and exclusive endpoints (e.g. "(3") in
9
+ zrangebyscore, zrevrangebyscore, and zremrangebyscore. ("Fix" here
10
+ means "write", as it's something that was completely forgotten the
11
+ first time around.)
12
+
13
+ ### 0.1.0
14
+ * Support `move(key, db)` to move keys between databases.
15
+
16
+ ### 0.0.2
17
+ * Fix gem homepage.
18
+
19
+ ### 0.0.1
20
+ Initial release.
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in mock_redis.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem "ruby-debug", :platforms => :mri_18
8
+ gem "ruby-debug19", :platforms => :mri_19
9
+ end
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Philotic, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,94 @@
1
+ # MockRedis
2
+
3
+ A mock redis gem.
4
+
5
+ MockRedis provides the same interface as redis-rb, but it stores its
6
+ data in memory instead of talking to a Redis server. It is intended
7
+ for use in tests.
8
+
9
+ ## Getting Started
10
+
11
+ It's as easy as `require 'mock_redis'; mr = MockRedis.new`. Then you can
12
+ call the same methods on it as you can call on a real `Redis` object.
13
+
14
+ For example:
15
+
16
+ >> require 'mock_redis'
17
+ >> mr = MockRedis.new
18
+ >> mr.set('some key', 'some value')
19
+ => "OK"
20
+ >> mr.get('some key')
21
+ => "some value"
22
+
23
+ ## Supported Features
24
+
25
+ mock_redis supports most of the methods that redis-rb does. Examples
26
+ of supported methods:
27
+
28
+ * String methods: `get`, `set`, `append`, `incr`, etc.
29
+ * List methods: `lpush`, `lpop`, `lrange`, `rpoplpush`, etc.
30
+ * Set methods: `sadd`, `sinter`, `sismember`, etc.
31
+ * Hash methods: `hset`, `hget`, `hgetall`, `hmget`, etc.
32
+ * Sorted set methods: `zadd`, `zrank`, `zunionstore`, etc.
33
+ * Expirations: `expire`, `ttl`, etc.
34
+ * Transactions: `multi`, `exec`, `discard`
35
+
36
+ ## Mostly-Supported Commands
37
+
38
+ A MockRedis object can't do everything that a real Redis client can
39
+ since it's an in-memory object confined to a single process. MockRedis
40
+ makes every attempt to be Redis-compatible, but there are some
41
+ necessary exceptions.
42
+
43
+ * Blocking list commands (`#blpop`, `#brpop`, and `#brpoplpush`) work
44
+ as expected if there is data for them to retrieve. If you use one of
45
+ these commands with a nonzero timeout and there is no data for it to
46
+ retrieve, then the command returns immediately. However, if you ask
47
+ one of these commands for data with a 0 timeout (means "wait
48
+ forever") and there is no data available, then a
49
+ `MockRedis::WouldBlock` exception is raised. It's not what a real
50
+ Redis client would do, but it beats hanging your test run forever.
51
+
52
+ * `#info` just returns canned values; they don't update over time.
53
+
54
+ ## Unsupported Commands
55
+
56
+ Some stuff, we just can't do with a single Ruby object in a single
57
+ Ruby process.
58
+
59
+ * Debugging commands (`#debug('object', key)` and
60
+ `#debug('segfault')`) aren't available.
61
+
62
+ * `#object` isn't available since we don't have any Redis internals to
63
+ poke at.
64
+
65
+ * `#monitor` isn't available; there's no place for requests to come
66
+ from, so there's nothing to receive.
67
+
68
+ * Pubsub commands (`#psubscribe`, `#publish`, `#punsubscribe`) aren't
69
+ available.
70
+
71
+ * `#slowlog` isn't available.
72
+
73
+ ## Remaining Work
74
+
75
+ There are some things we want to have in here, but that we just
76
+ haven't gotten to doing yet. If you're interested in helping out,
77
+ please submit a pull request with your (tested!) implementation.
78
+
79
+ * `#config(:get|:set|:resetstat)` isn't done. They can just return
80
+ canned values.
81
+
82
+ * `#sort` isn't done. We just haven't gotten to it yet. The input
83
+ validation will be a pain in the butt, but other than that, it
84
+ shouldn't be too bad.
85
+
86
+ ## Running the Tests
87
+
88
+ If you want to work on this, you'll probably want to run the
89
+ tests. (Just kidding! There's no probably about it.) These tests were
90
+ written with Redis 2.2.11 running on localhost without any passwords
91
+ required. If you're using a different version of Redis, you may see
92
+ failures due to error message text being different. If you're running
93
+ a really old version of Redis, you'll definitely see failures due to
94
+ stuff that doesn't work!
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |t|
6
+ t.rspec_opts = %w(-fs --color)
7
+ t.pattern = "spec/**/*_spec.rb"
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,32 @@
1
+ require 'set'
2
+
3
+ require 'mock_redis/assertions'
4
+ require 'mock_redis/database'
5
+ require 'mock_redis/expire_wrapper'
6
+ require 'mock_redis/multi_db_wrapper'
7
+ require 'mock_redis/transaction_wrapper'
8
+ require 'mock_redis/undef_redis_methods'
9
+
10
+ class MockRedis
11
+ include UndefRedisMethods
12
+
13
+ def initialize(*args)
14
+ @db = TransactionWrapper.new(
15
+ ExpireWrapper.new(
16
+ MultiDbWrapper.new(
17
+ Database.new(*args))))
18
+ end
19
+
20
+ def respond_to?(method, include_private=false)
21
+ super || @db.respond_to?(method, include_private)
22
+ end
23
+
24
+ def method_missing(method, *args, &block)
25
+ @db.send(method, *args, &block)
26
+ end
27
+
28
+ def initialize_copy(source)
29
+ super
30
+ @db = @db.clone
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ class MockRedis
2
+ module Assertions
3
+ private
4
+
5
+ def assert_has_args(args, command)
6
+ unless args.any?
7
+ raise RuntimeError,
8
+ "ERR wrong number of arguments for '#{command}' command"
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,432 @@
1
+ require 'mock_redis/assertions'
2
+ require 'mock_redis/exceptions'
3
+ require 'mock_redis/hash_methods'
4
+ require 'mock_redis/list_methods'
5
+ require 'mock_redis/set_methods'
6
+ require 'mock_redis/string_methods'
7
+ require 'mock_redis/zset_methods'
8
+
9
+ class MockRedis
10
+ class Database
11
+ include HashMethods
12
+ include ListMethods
13
+ include SetMethods
14
+ include StringMethods
15
+ include ZsetMethods
16
+
17
+ attr_reader :data, :expire_times
18
+
19
+ def initialize(*args)
20
+ @data = {}
21
+ @expire_times = []
22
+ end
23
+
24
+ def initialize_copy(source)
25
+ @data = @data.clone
26
+ @data.keys.each {|k| @data[k] = @data[k].clone}
27
+ @expire_times = @expire_times.map{|x| x.clone}
28
+ end
29
+
30
+ # Redis commands go below this line and above 'private'
31
+
32
+ def auth(_) 'OK' end
33
+
34
+ def bgrewriteaof() "Background append only file rewriting started" end
35
+
36
+ def bgsave() "Background saving started" end
37
+
38
+ def dbsize
39
+ data.keys.length
40
+ end
41
+
42
+ def del(*keys)
43
+ keys.
44
+ find_all{|key| data[key]}.
45
+ each {|k| persist(k)}.
46
+ each {|k| data.delete(k)}.
47
+ length
48
+ end
49
+
50
+ def echo(msg)
51
+ msg.to_s
52
+ end
53
+
54
+ def expire(key, seconds)
55
+ expireat(key, Time.now.to_i + seconds.to_i)
56
+ end
57
+
58
+ def expireat(key, timestamp)
59
+ unless looks_like_integer?(timestamp.to_s)
60
+ raise RuntimeError, "ERR value is not an integer or out of range"
61
+ end
62
+
63
+ if exists(key)
64
+ set_expiration(key, Time.at(timestamp.to_i))
65
+ true
66
+ else
67
+ false
68
+ end
69
+ end
70
+
71
+ def exists(key)
72
+ data.has_key?(key)
73
+ end
74
+
75
+ def flushdb
76
+ data.keys.each {|k| del(k)}
77
+ 'OK'
78
+ end
79
+
80
+ def info
81
+ astats = [
82
+ ["2", "2699"],
83
+ ["6", "1"],
84
+ ["7", "1"],
85
+ ["8", "17197"],
86
+ ["9", "109875"],
87
+ ["10", "94348"],
88
+ ["11", "32580"],
89
+ ["12", "52347"],
90
+ ["13", "86475"],
91
+ ["14", "58175"],
92
+ ["15", "53408"],
93
+ ["16", "876949"],
94
+ ["17", "71157"],
95
+ ["18", "5104"],
96
+ ["19", "2705"],
97
+ ["20", "2903"],
98
+ ["21", "1024"],
99
+ ["22", "2546"],
100
+ ["23", "952"],
101
+ ["24", "186080"],
102
+ ["25", "611"],
103
+ ["26", "40936"],
104
+ ["27", "960"],
105
+ ["28", "1323"],
106
+ ["29", "14216"],
107
+ ["30", "52412"],
108
+ ["31", "21130"],
109
+ ["32", "47959"],
110
+ ["33", "6891"],
111
+ ["34", "9712"],
112
+ ["35", "3366"],
113
+ ["36", "5737"],
114
+ ["37", "11274"],
115
+ ["38", "8057"],
116
+ ["39", "2957"],
117
+ ["40", "51200"],
118
+ ["42", "8220"],
119
+ ["43", "8278"],
120
+ ["44", "6539"],
121
+ ["45", "764"],
122
+ ["47", "1018"],
123
+ ["48", "19250"],
124
+ ["49", "713"],
125
+ ["51", "51"],
126
+ ["53", "2"],
127
+ ["55", "3922"],
128
+ ["56", "153"],
129
+ ["57", "614"],
130
+ ["58", "1"],
131
+ ["59", "1775"],
132
+ ["61", "32865"],
133
+ ["63", "2530"],
134
+ ["64", "565"],
135
+ ["65", "1322"],
136
+ ["67", "1572"],
137
+ ["69", "1421"],
138
+ ["71", "1220"],
139
+ ["72", "241"],
140
+ ["73", "5432"],
141
+ ["74", "1122"],
142
+ ["75", "2555"],
143
+ ["77", "1539"],
144
+ ["78", "612"],
145
+ ["79", "902"],
146
+ ["81", "1678"],
147
+ ["83", "51"],
148
+ ["84", "612"],
149
+ ["85", "706"],
150
+ ["87", "410"],
151
+ ["88", "5435"],
152
+ ["89", "813"],
153
+ ["90", "612"],
154
+ ["93", "153"],
155
+ ["94", "612"],
156
+ ["96", "159"],
157
+ ["97", "306"],
158
+ ["99", "153"],
159
+ ["101", "456"],
160
+ ["103", "741"],
161
+ ["105", "447"],
162
+ ["107", "754"],
163
+ ["109", "414"],
164
+ ["111", "475"],
165
+ ["113", "757"],
166
+ ["115", "287"],
167
+ ["117", "420"],
168
+ ["118", "765"],
169
+ ["119", "642"],
170
+ ["120", "159"],
171
+ ["121", "926"],
172
+ ["122", "612"],
173
+ ["123", "251"],
174
+ ["125", "390"],
175
+ ["127", "354"],
176
+ ["128", "617"],
177
+ ["129", "528"],
178
+ ["131", "298"],
179
+ ["132", "612"],
180
+ ["133", "809"],
181
+ ["135", "244"],
182
+ ["136", "306"],
183
+ ["137", "504"],
184
+ ["139", "201"],
185
+ ["141", "1124"],
186
+ ["143", "139"],
187
+ ["144", "159"],
188
+ ["145", "1322"],
189
+ ["147", "410"],
190
+ ["149", "253"],
191
+ ["151", "304"],
192
+ ["153", "312"],
193
+ ["155", "249"],
194
+ ["157", "306"],
195
+ ["159", "348"],
196
+ ["161", "255"],
197
+ ["163", "458"],
198
+ ["165", "5"],
199
+ ["167", "306"],
200
+ ["168", "47"],
201
+ ["169", "214"],
202
+ ["171", "250"],
203
+ ["173", "5"],
204
+ ["177", "10"],
205
+ ["179", "158"],
206
+ ["181", "5"],
207
+ ["183", "10"],
208
+ ["185", "51"],
209
+ ["187", "49"],
210
+ ["191", "5"],
211
+ ["192", "47"],
212
+ ["193", "51"],
213
+ ["197", "112"],
214
+ ["199", "5"],
215
+ ["201", "5"],
216
+ ["203", "5"],
217
+ ["209", "5"],
218
+ ["213", "51"],
219
+ ["217", "102"],
220
+ ["225", "357"],
221
+ ["229", "51"],
222
+ ["233", "204"],
223
+ ["237", "51"],
224
+ ["239", "1"],
225
+ ["247", "46"],
226
+ ["255", "102"],
227
+ [">=256", "6201"],
228
+ ]
229
+
230
+ {
231
+ "allocation_stats" => astats.map {|(a,b)| "#{a}=#{b}"}.join(','),
232
+ "aof_enabled" => "0",
233
+ "arch_bits" => "64",
234
+ "bgrewriteaof_in_progress" => "0",
235
+ "bgsave_in_progress" => "0",
236
+ "blocked_clients" => "0",
237
+ "changes_since_last_save" => "0",
238
+ "client_biggest_input_buf" => "0",
239
+ "client_longest_output_list" => "0",
240
+ "connected_clients" => "1",
241
+ "connected_slaves" => "0",
242
+ "db0" => "keys=8,expires=0",
243
+ "evicted_keys" => "0",
244
+ "expired_keys" => "0",
245
+ "hash_max_zipmap_entries" => "512",
246
+ "hash_max_zipmap_value" => "64",
247
+ "keyspace_hits" => "62645",
248
+ "keyspace_misses" => "29757",
249
+ "last_save_time" => "1310596333",
250
+ "loading" => "0",
251
+ "lru_clock" => "1036434",
252
+ "mem_fragmentation_ratio" => "2.04",
253
+ "multiplexing_api" => "kqueue",
254
+ "process_id" => "14508",
255
+ "pubsub_channels" => "0",
256
+ "pubsub_patterns" => "0",
257
+ "redis_git_dirty" => "0",
258
+ "redis_git_sha1" => "00000000",
259
+ "redis_version" => "2.2.11",
260
+ "role" => "master",
261
+ "total_commands_processed" => "196800",
262
+ "total_connections_received" => "4359",
263
+ "uptime_in_days" => "0",
264
+ "uptime_in_seconds" => "84215",
265
+ "use_tcmalloc" => "0",
266
+ "used_cpu_sys" => "5.54",
267
+ "used_cpu_sys_childrens" => "0.00",
268
+ "used_cpu_user" => "7.65",
269
+ "used_cpu_user_childrens" => "0.02",
270
+ "used_memory" => "931456",
271
+ "used_memory_human" => "909.62K",
272
+ "used_memory_rss" => "1904640",
273
+ "vm_enabled" => "0",
274
+ }
275
+ end
276
+
277
+ def keys(format)
278
+ data.keys.grep(redis_pattern_to_ruby_regex(format))
279
+ end
280
+
281
+ def lastsave
282
+ Time.now.to_i
283
+ end
284
+
285
+ def persist(key)
286
+ if exists(key) && has_expiration?(key)
287
+ remove_expiration(key)
288
+ true
289
+ else
290
+ false
291
+ end
292
+ end
293
+
294
+ def ping
295
+ 'PONG'
296
+ end
297
+
298
+ def quit
299
+ 'OK'
300
+ end
301
+
302
+ def randomkey
303
+ data.keys[rand(data.length)]
304
+ end
305
+
306
+ def rename(key, newkey)
307
+ if key == newkey
308
+ raise RuntimeError, "ERR source and destination objects are the same"
309
+ end
310
+ data[newkey] = data.delete(key)
311
+ 'OK'
312
+ end
313
+
314
+ def renamenx(key, newkey)
315
+ if key == newkey
316
+ raise RuntimeError, "ERR source and destination objects are the same"
317
+ end
318
+ if exists(newkey)
319
+ false
320
+ else
321
+ rename(key, newkey)
322
+ true
323
+ end
324
+ end
325
+
326
+ def save
327
+ 'OK'
328
+ end
329
+
330
+ def ttl(key)
331
+ if has_expiration?(key)
332
+ (expiration(key) - Time.now).to_i
333
+ else
334
+ -1
335
+ end
336
+ end
337
+
338
+ def type(key)
339
+ if !exists(key)
340
+ 'none'
341
+ elsif hashy?(key)
342
+ 'hash'
343
+ elsif stringy?(key)
344
+ 'string'
345
+ elsif listy?(key)
346
+ 'list'
347
+ elsif sety?(key)
348
+ 'set'
349
+ elsif zsety?(key)
350
+ 'zset'
351
+ else
352
+ raise ArgumentError, "Not sure how #{data[key].inspect} got in here"
353
+ end
354
+ end
355
+
356
+ private
357
+
358
+ def assert_valid_timeout(timeout)
359
+ if !looks_like_integer?(timeout.to_s)
360
+ raise RuntimeError, "ERR timeout is not an integer or out of range"
361
+ elsif timeout < 0
362
+ raise RuntimeError, "ERR timeout is negative"
363
+ end
364
+ timeout
365
+ end
366
+
367
+ def can_incr?(value)
368
+ value.nil? || looks_like_integer?(value)
369
+ end
370
+
371
+ def extract_timeout(arglist)
372
+ timeout = assert_valid_timeout(arglist.last)
373
+ [arglist[0..-2], arglist.last]
374
+ end
375
+
376
+ def expiration(key)
377
+ expire_times.find {|(_,k)| k == key}.first
378
+ end
379
+
380
+ def has_expiration?(key)
381
+ expire_times.any? {|(_,k)| k == key}
382
+ end
383
+
384
+ def looks_like_integer?(str)
385
+ str =~ /^-?\d+$/
386
+ end
387
+
388
+ def redis_pattern_to_ruby_regex(pattern)
389
+ Regexp.new(
390
+ "^#{pattern}$".
391
+ gsub(/([^\\])\?/, "\\1.").
392
+ gsub(/([^\\])\*/, "\\1.+"))
393
+ end
394
+
395
+ def remove_expiration(key)
396
+ expire_times.delete_if do |(t, k)|
397
+ key == k
398
+ end
399
+ end
400
+
401
+ def set_expiration(key, time)
402
+ remove_expiration(key)
403
+
404
+ expire_times << [time, key]
405
+ expire_times.sort! do |a, b|
406
+ a.first <=> b.first
407
+ end
408
+ end
409
+
410
+ def zero_pad(string, desired_length)
411
+ padding = "\000" * [(desired_length - string.length), 0].max
412
+ string + padding
413
+ end
414
+
415
+ public
416
+ # This method isn't private, but it also isn't a Redis command, so
417
+ # it doesn't belong up above with all the Redis commands.
418
+ def expire_keys
419
+ now = Time.now
420
+
421
+ to_delete = expire_times.take_while do |(time, key)|
422
+ time <= now
423
+ end
424
+
425
+ to_delete.each do |(time, key)|
426
+ del(key)
427
+ end
428
+
429
+ expire_times.slice!(0, to_delete.length)
430
+ end
431
+ end
432
+ end