redis-namespace 1.6.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: '08244a1c35f2f0164e72385ebd64aae7c57354b6'
4
- data.tar.gz: dd76131303f0cbb576780146d9c5e3a759302e57
2
+ SHA256:
3
+ metadata.gz: e003f4015162bfd465e79dd90e54b44ae46f0ec269118e10c08215d639166296
4
+ data.tar.gz: 16078b51e1bdab24977aa2b6fb7d0cd4761a7c41c340fcdacfb96dd32216c061
5
5
  SHA512:
6
- metadata.gz: 4ca1e49aaf2c9e213a0859d1a0bda67f988ddf3a5af08bbf08388a8582298659d6a6869b7d8ee035501d8abf35779be20164dff5b849110f75f173cafb3444e5
7
- data.tar.gz: ca1151681a327f2206370a12f63729ee4f9a0891830274fc0e219353137f09216757421cfb4d4d29c18f73ca2f6e2b7bcf8ff4a0dc55a88324cd0aa7957f2993
6
+ metadata.gz: fb965b575fe3ec2f2f45b99a71839f569f647681939b16576c053ab3e25bf85db1db030941bbe6eaeecc624eeeaf8c0497c58c53c4231440896e9e116bc189e3
7
+ data.tar.gz: 07b2b2e20c4a152b3e3a4cfbf6abad773f335e098067a9b213e29dd80cfc4519f3d27c43febe6cf8261ffe95e73184e23a1b9ff557007eb7a8308e43a398bbb1
data/LICENSE CHANGED
@@ -1,20 +1,21 @@
1
+ MIT License
2
+
1
3
  Copyright (c) 2009 Chris Wanstrath
2
4
 
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
10
11
 
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
13
14
 
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -3,13 +3,13 @@ redis-namespace
3
3
 
4
4
  Redis::Namespace provides an interface to a namespaced subset of your [redis][] keyspace (e.g., keys with a common beginning), and requires the [redis-rb][] gem.
5
5
 
6
- ~~~ irb
6
+ ```ruby
7
7
  require 'redis-namespace'
8
8
  # => true
9
9
 
10
10
  redis_connection = Redis.new
11
11
  # => #<Redis client v3.1.0 for redis://127.0.0.1:6379/0>
12
- namespaced_redis = Redis::Namespace.new(:ns, :redis => redis_connection)
12
+ namespaced_redis = Redis::Namespace.new(:ns, redis: redis_connection)
13
13
  # => #<Redis::Namespace v1.5.0 with client v3.1.0 for redis://127.0.0.1:6379/0/ns>
14
14
 
15
15
  namespaced_redis.set('foo', 'bar') # redis_connection.set('ns:foo', 'bar')
@@ -20,6 +20,8 @@ namespaced_redis.set('foo', 'bar') # redis_connection.set('ns:foo', 'bar')
20
20
 
21
21
  namespaced_redis.get('foo')
22
22
  # => "bar"
23
+ redis_connection.get('foo')
24
+ # => nil
23
25
  redis_connection.get('ns:foo')
24
26
  # => "bar"
25
27
 
@@ -29,7 +31,14 @@ namespaced_redis.get('foo')
29
31
  # => nil
30
32
  redis_connection.get('ns:foo')
31
33
  # => nil
32
- ~~~
34
+ ```
35
+
36
+ Redis::Namespace also supports `Proc` as a namespace and will take the result string as namespace at runtime.
37
+
38
+ ```ruby
39
+ redis_connection = Redis.new
40
+ namespaced_redis = Redis::Namespace.new(Proc.new { Tenant.current_tenant }, redis: redis_connection)
41
+ ```
33
42
 
34
43
  Installation
35
44
  ============
@@ -42,20 +51,18 @@ From the command line:
42
51
 
43
52
  Or in your Gemfile:
44
53
 
45
- ~~~ ruby
54
+ ```ruby
46
55
  gem 'redis-namespace'
47
- ~~~
56
+ ```
48
57
 
49
58
  Caveats
50
59
  =======
51
60
 
52
- `Redis::Namespace` provides a namespaced interface to `Redis` by keeping an internal registry of the method signatures in `Redis` provided by the redis-rb gem;
53
- we keep track of which arguments need the namespace added, and which return values need the namespace removed.
61
+ `Redis::Namespace` provides a namespaced interface to `Redis` by keeping an internal registry of the method signatures in `Redis` provided by the [redis-rb][] gem; we keep track of which arguments need the namespace added, and which return values need the namespace removed.
54
62
 
55
63
  Blind Passthrough
56
64
  -----------------
57
- If your version of this gem doesn't know about a particular command, it can't namespace it.
58
- Historically, this has meant that Redis::Namespace blindly passes unknown commands on to the underlying redis connection without modification which can lead to surprising effects.
65
+ If your version of this gem doesn't know about a particular command, it can't namespace it. Historically, this has meant that Redis::Namespace blindly passes unknown commands on to the underlying redis connection without modification which can lead to surprising effects.
59
66
 
60
67
  As of v1.5.0, blind passthrough has been deprecated, and the functionality will be removed entirely in 2.0.
61
68
 
@@ -63,26 +70,23 @@ If you come across a command that is not yet supported, please open an issue on
63
70
 
64
71
  Administrative Commands
65
72
  -----------------------
66
- The effects of some redis commands cannot be limited to a particular namespace (e.g., `FLUSHALL`, which literally truncates all databases in your redis server, regardless of keyspace).
67
- Historically, this has meant that Redis::Namespace intentionally passes administrative commands on to the underlying redis connection without modification, which can lead to surprising effects.
73
+ The effects of some redis commands cannot be limited to a particular namespace (e.g., `FLUSHALL`, which literally truncates all databases in your redis server, regardless of keyspace). Historically, this has meant that Redis::Namespace intentionally passes administrative commands on to the underlying redis connection without modification, which can lead to surprising effects.
68
74
 
69
- As of v1.6.0, the direct use of administrative commands has been deprecated, and the functionality will be removed entirely in 2.0;
70
- while such commands are often useful for testing or administration, their meaning is inherently hidden when placed behind an interface that implies it will namespace everything.
75
+ As of v1.6.0, the direct use of administrative commands has been deprecated, and the functionality will be removed entirely in 2.0; while such commands are often useful for testing or administration, their meaning is inherently hidden when placed behind an interface that implies it will namespace everything.
71
76
 
72
- The prefered way to send an administrative command is on the redis connection
73
- itself, which is publicly exposed as `Redis::Namespace#redis`:
77
+ The prefered way to send an administrative command is on the redis connection itself, which is publicly exposed as `Redis::Namespace#redis`:
74
78
 
75
- ~~~ ruby
79
+ ```ruby
76
80
  namespaced.redis.flushall()
77
81
  # => "OK"
78
- ~~~
82
+ ```
79
83
 
80
84
  2.x Planned Breaking Changes
81
85
  ============================
82
86
 
83
87
  As mentioned above, 2.0 will remove blind passthrough and the administrative command passthrough.
84
88
  By default in 1.5+, deprecation warnings are present and enabled;
85
- they can be silenced by initializing `Redis::Namespace` with `warnings: false` or by setting the `REDIS_NAMESPACE_QUIET` environment variable.
89
+ they can be silenced by initializing `Redis::Namespace` with `warning: false` or by setting the `REDIS_NAMESPACE_QUIET` environment variable.
86
90
 
87
91
  Early opt-in
88
92
  ------------
@@ -2,6 +2,6 @@
2
2
 
3
3
  class Redis
4
4
  class Namespace
5
- VERSION = '1.6.0'
5
+ VERSION = '1.11.0'
6
6
  end
7
7
  end
@@ -57,20 +57,26 @@ class Redis
57
57
  "append" => [ :first ],
58
58
  "bitcount" => [ :first ],
59
59
  "bitop" => [ :exclude_first ],
60
+ "bitpos" => [ :first ],
60
61
  "blpop" => [ :exclude_last, :first ],
61
62
  "brpop" => [ :exclude_last, :first ],
62
63
  "brpoplpush" => [ :exclude_last ],
64
+ "bzpopmin" => [ :first ],
65
+ "bzpopmax" => [ :first ],
63
66
  "debug" => [ :exclude_first ],
64
67
  "decr" => [ :first ],
65
68
  "decrby" => [ :first ],
66
69
  "del" => [ :all ],
67
70
  "dump" => [ :first ],
68
- "exists" => [ :first ],
71
+ "exists" => [ :all ],
72
+ "exists?" => [ :all ],
69
73
  "expire" => [ :first ],
70
74
  "expireat" => [ :first ],
75
+ "expiretime" => [ :first ],
71
76
  "eval" => [ :eval_style ],
72
77
  "evalsha" => [ :eval_style ],
73
78
  "get" => [ :first ],
79
+ "getex" => [ :first ],
74
80
  "getbit" => [ :first ],
75
81
  "getrange" => [ :first ],
76
82
  "getset" => [ :first ],
@@ -117,6 +123,7 @@ class Redis
117
123
  "persist" => [ :first ],
118
124
  "pexpire" => [ :first ],
119
125
  "pexpireat" => [ :first ],
126
+ "pexpiretime" => [ :first ],
120
127
  "pfadd" => [ :first ],
121
128
  "pfcount" => [ :all ],
122
129
  "pfmerge" => [ :all ],
@@ -133,6 +140,7 @@ class Redis
133
140
  "rpush" => [ :first ],
134
141
  "rpushx" => [ :first ],
135
142
  "sadd" => [ :first ],
143
+ "sadd?" => [ :first ],
136
144
  "scard" => [ :first ],
137
145
  "scan" => [ :scan_style, :second ],
138
146
  "scan_each" => [ :scan_style, :all ],
@@ -147,11 +155,13 @@ class Redis
147
155
  "sinterstore" => [ :all ],
148
156
  "sismember" => [ :first ],
149
157
  "smembers" => [ :first ],
158
+ "smismember" => [ :first ],
150
159
  "smove" => [ :exclude_last ],
151
160
  "sort" => [ :sort ],
152
161
  "spop" => [ :first ],
153
162
  "srandmember" => [ :first ],
154
163
  "srem" => [ :first ],
164
+ "srem?" => [ :first ],
155
165
  "sscan" => [ :first ],
156
166
  "sscan_each" => [ :first ],
157
167
  "strlen" => [ :first ],
@@ -160,27 +170,31 @@ class Redis
160
170
  "sunionstore" => [ :all ],
161
171
  "ttl" => [ :first ],
162
172
  "type" => [ :first ],
173
+ "unlink" => [ :all ],
163
174
  "unsubscribe" => [ :all ],
164
175
  "zadd" => [ :first ],
165
176
  "zcard" => [ :first ],
166
177
  "zcount" => [ :first ],
167
178
  "zincrby" => [ :first ],
168
179
  "zinterstore" => [ :exclude_options ],
180
+ "zpopmin" => [ :first ],
181
+ "zpopmax" => [ :first ],
169
182
  "zrange" => [ :first ],
170
183
  "zrangebyscore" => [ :first ],
184
+ "zrangebylex" => [ :first ],
171
185
  "zrank" => [ :first ],
172
186
  "zrem" => [ :first ],
173
187
  "zremrangebyrank" => [ :first ],
174
188
  "zremrangebyscore" => [ :first ],
189
+ "zremrangebylex" => [ :first ],
175
190
  "zrevrange" => [ :first ],
176
191
  "zrevrangebyscore" => [ :first ],
192
+ "zrevrangebylex" => [ :first ],
177
193
  "zrevrank" => [ :first ],
178
194
  "zscan" => [ :first ],
179
195
  "zscan_each" => [ :first ],
180
196
  "zscore" => [ :first ],
181
- "zunionstore" => [ :exclude_options ],
182
- "[]" => [ :first ],
183
- "[]=" => [ :first ]
197
+ "zunionstore" => [ :exclude_options ]
184
198
  }
185
199
  TRANSACTION_COMMANDS = {
186
200
  "discard" => [],
@@ -192,6 +206,7 @@ class Redis
192
206
  HELPER_COMMANDS = {
193
207
  "auth" => [],
194
208
  "disconnect!" => [],
209
+ "close" => [],
195
210
  "echo" => [],
196
211
  "ping" => [],
197
212
  "time" => [],
@@ -228,13 +243,23 @@ class Redis
228
243
  # Support 1.8.7 by providing a namespaced reference to Enumerable::Enumerator
229
244
  Enumerator = Enumerable::Enumerator unless defined?(::Enumerator)
230
245
 
246
+ # This is used by the Redis gem to determine whether or not to display that deprecation message.
247
+ @sadd_returns_boolean = true
248
+
249
+ # This is used by the Redis gem to determine whether or not to display that deprecation message.
250
+ @srem_returns_boolean = true
251
+
252
+ class << self
253
+ attr_accessor :sadd_returns_boolean, :srem_returns_boolean
254
+ end
255
+
231
256
  attr_writer :namespace
232
257
  attr_reader :redis
233
258
  attr_accessor :warning
234
259
 
235
260
  def initialize(namespace, options = {})
236
261
  @namespace = namespace
237
- @redis = options[:redis] || Redis.current
262
+ @redis = options[:redis] || Redis.new
238
263
  @warning = !!options.fetch(:warning) do
239
264
  !ENV['REDIS_NAMESPACE_QUIET']
240
265
  end
@@ -253,8 +278,8 @@ class Redis
253
278
  end
254
279
 
255
280
  def client
256
- warn("The client method is deprecated as of redis-rb 4.0.0, please use the new _client" +
257
- "method instead. Support for the old method will be removed in redis-namespace 2.0.") if @has_new_client_method
281
+ warn("The client method is deprecated as of redis-rb 4.0.0, please use the new _client " +
282
+ "method instead. Support for the old method will be removed in redis-namespace 2.0.") if @has_new_client_method && deprecations?
258
283
  _client
259
284
  end
260
285
 
@@ -272,7 +297,9 @@ class Redis
272
297
 
273
298
  # emulate Ruby 1.9+ and keep respond_to_missing? logic together.
274
299
  def respond_to?(command, include_private=false)
275
- super or respond_to_missing?(command, include_private)
300
+ return !deprecations? if DEPRECATED_COMMANDS.include?(command.to_s.downcase)
301
+
302
+ respond_to_missing?(command, include_private) or super
276
303
  end
277
304
 
278
305
  def keys(query = nil)
@@ -297,7 +324,15 @@ class Redis
297
324
  :redis => @redis)
298
325
  end
299
326
 
300
- @namespace
327
+ @namespace.respond_to?(:call) ? @namespace.call : @namespace
328
+ end
329
+
330
+ def full_namespace
331
+ redis.is_a?(Namespace) ? "#{redis.full_namespace}:#{namespace}" : namespace.to_s
332
+ end
333
+
334
+ def connection
335
+ @redis.connection.tap { |info| info[:namespace] = namespace }
301
336
  end
302
337
 
303
338
  def exec
@@ -307,16 +342,40 @@ class Redis
307
342
  def eval(*args)
308
343
  call_with_namespace(:eval, *args)
309
344
  end
345
+ ruby2_keywords(:eval) if respond_to?(:ruby2_keywords, true)
346
+
347
+ # This operation can run for a very long time if the namespace contains lots of keys!
348
+ # It should be used in tests, or when the namespace is small enough
349
+ # and you are sure you know what you are doing.
350
+ def clear
351
+ if warning?
352
+ warn("This operation can run for a very long time if the namespace contains lots of keys! " +
353
+ "It should be used in tests, or when the namespace is small enough " +
354
+ "and you are sure you know what you are doing.")
355
+ end
310
356
 
311
- def method_missing(command, *args, &block)
312
- normalized_command = command.to_s.downcase
357
+ batch_size = 1000
358
+
359
+ if supports_scan?
360
+ cursor = "0"
361
+ begin
362
+ cursor, keys = scan(cursor, count: batch_size)
363
+ del(*keys) unless keys.empty?
364
+ end until cursor == "0"
365
+ else
366
+ all_keys = keys("*")
367
+ all_keys.each_slice(batch_size) do |keys|
368
+ del(*keys)
369
+ end
370
+ end
371
+ end
372
+
373
+ ADMINISTRATIVE_COMMANDS.keys.each do |command|
374
+ define_method(command) do |*args, &block|
375
+ raise NoMethodError if deprecations?
313
376
 
314
- if ADMINISTRATIVE_COMMANDS.include?(normalized_command)
315
- # administrative commands usage is deprecated and will be removed in 2.0
316
- # redis-namespace cannot safely apply a namespace to their effects.
317
- return super if deprecations?
318
377
  if warning?
319
- warn("Passing '#{normalized_command}' command to redis as is; " +
378
+ warn("Passing '#{command}' command to redis as is; " +
320
379
  "administrative commands cannot be effectively namespaced " +
321
380
  "and should be called on the redis connection directly; " +
322
381
  "passthrough has been deprecated and will be removed in " +
@@ -324,8 +383,25 @@ class Redis
324
383
  )
325
384
  end
326
385
  call_with_namespace(command, *args, &block)
327
- elsif COMMANDS.include?(normalized_command)
386
+ end
387
+ ruby2_keywords(command) if respond_to?(:ruby2_keywords, true)
388
+ end
389
+
390
+ COMMANDS.keys.each do |command|
391
+ next if ADMINISTRATIVE_COMMANDS.include?(command)
392
+ next if method_defined?(command)
393
+
394
+ define_method(command) do |*args, &block|
328
395
  call_with_namespace(command, *args, &block)
396
+ end
397
+ ruby2_keywords(command) if respond_to?(:ruby2_keywords, true)
398
+ end
399
+
400
+ def method_missing(command, *args, &block)
401
+ normalized_command = command.to_s.downcase
402
+
403
+ if COMMANDS.include?(normalized_command)
404
+ send(normalized_command, *args, &block)
329
405
  elsif @redis.respond_to?(normalized_command) && !deprecations?
330
406
  # blind passthrough is deprecated and will be removed in 2.0
331
407
  # redis-namespace does not know how to handle this command.
@@ -336,23 +412,23 @@ class Redis
336
412
  "passthrough has been deprecated and will be removed in " +
337
413
  "redis-namespace 2.0 (at #{call_site})")
338
414
  end
339
- @redis.send(command, *args, &block)
415
+
416
+ wrapped_send(@redis, command, args, &block)
340
417
  else
341
418
  super
342
419
  end
343
420
  end
421
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
344
422
 
345
423
  def inspect
346
424
  "<#{self.class.name} v#{VERSION} with client v#{Redis::VERSION} "\
347
- "for #{@redis.id}/#{@namespace}>"
425
+ "for #{@redis.id}/#{full_namespace}>"
348
426
  end
349
427
 
350
428
  def respond_to_missing?(command, include_all=false)
351
429
  normalized_command = command.to_s.downcase
352
430
 
353
431
  case
354
- when DEPRECATED_COMMANDS.include?(normalized_command)
355
- !deprecations?
356
432
  when COMMANDS.include?(normalized_command)
357
433
  true
358
434
  when !deprecations? && redis.respond_to?(command, include_all)
@@ -378,6 +454,7 @@ class Redis
378
454
  case before
379
455
  when :first
380
456
  args[0] = add_namespace(args[0]) if args[0]
457
+ args[-1] = ruby2_keywords_hash(args[-1]) if args[-1].is_a?(Hash)
381
458
  when :all
382
459
  args = add_namespace(args)
383
460
  when :exclude_first
@@ -390,13 +467,14 @@ class Redis
390
467
  args.push(last) if last
391
468
  when :exclude_options
392
469
  if args.last.is_a?(Hash)
393
- last = args.pop
470
+ last = ruby2_keywords_hash(args.pop)
394
471
  args = add_namespace(args)
395
472
  args.push(last)
396
473
  else
397
474
  args = add_namespace(args)
398
475
  end
399
476
  when :alternate
477
+ args = args.flatten
400
478
  args.each_with_index { |a, i| args[i] = add_namespace(a) if i.even? }
401
479
  when :sort
402
480
  args[0] = add_namespace(args[0]) if args[0]
@@ -410,6 +488,7 @@ class Redis
410
488
  args[1][:get].each_index do |i|
411
489
  args[1][:get][i] = add_namespace(args[1][:get][i]) unless args[1][:get][i] == "#"
412
490
  end
491
+ args[1] = ruby2_keywords_hash(args[1])
413
492
  end
414
493
  when :eval_style
415
494
  # redis.eval() and evalsha() can either take the form:
@@ -430,7 +509,7 @@ class Redis
430
509
  when :scan_style
431
510
  options = (args.last.kind_of?(Hash) ? args.pop : {})
432
511
  options[:match] = add_namespace(options.fetch(:match, '*'))
433
- args << options
512
+ args << ruby2_keywords_hash(options)
434
513
 
435
514
  if block
436
515
  original_block = block
@@ -439,7 +518,7 @@ class Redis
439
518
  end
440
519
 
441
520
  # Dispatch the command to Redis and store the result.
442
- result = @redis.send(command, *args, &block)
521
+ result = wrapped_send(@redis, command, args, &block)
443
522
 
444
523
  # Don't try to remove namespace from a Redis::Future, you can't.
445
524
  return result if result.is_a?(Redis::Future)
@@ -456,9 +535,36 @@ class Redis
456
535
 
457
536
  result
458
537
  end
538
+ ruby2_keywords(:call_with_namespace) if respond_to?(:ruby2_keywords, true)
539
+
540
+ protected
541
+
542
+ def redis=(redis)
543
+ @redis = redis
544
+ end
459
545
 
460
546
  private
461
547
 
548
+ if Hash.respond_to?(:ruby2_keywords_hash)
549
+ def ruby2_keywords_hash(kwargs)
550
+ Hash.ruby2_keywords_hash(kwargs)
551
+ end
552
+ else
553
+ def ruby2_keywords_hash(kwargs)
554
+ kwargs
555
+ end
556
+ end
557
+
558
+ def wrapped_send(redis_client, command, args = [], &block)
559
+ if redis_client.class.name == "ConnectionPool"
560
+ redis_client.with do |pool_connection|
561
+ pool_connection.send(command, *args, &block)
562
+ end
563
+ else
564
+ redis_client.send(command, *args, &block)
565
+ end
566
+ end
567
+
462
568
  # Avoid modifying the caller's (pass-by-reference) arguments.
463
569
  def clone_args(arg)
464
570
  if arg.is_a?(Array)
@@ -475,18 +581,16 @@ class Redis
475
581
  end
476
582
 
477
583
  def namespaced_block(command, &block)
478
- redis.send(command) do |r|
479
- begin
480
- original, @redis = @redis, r
481
- yield self
482
- ensure
483
- @redis = original
484
- end
584
+ if block.arity == 0
585
+ wrapped_send(redis, command, &block)
586
+ else
587
+ outer_block = proc { |r| copy = dup; copy.redis = r; yield copy }
588
+ wrapped_send(redis, command, &outer_block)
485
589
  end
486
590
  end
487
591
 
488
592
  def add_namespace(key)
489
- return key unless key && @namespace
593
+ return key unless key && namespace
490
594
 
491
595
  case key
492
596
  when Array
@@ -495,12 +599,12 @@ class Redis
495
599
  key.keys.each {|k| key[add_namespace(k)] = key.delete(k)}
496
600
  key
497
601
  else
498
- "#{@namespace}:#{key}"
602
+ "#{namespace}:#{key}"
499
603
  end
500
604
  end
501
605
 
502
606
  def rem_namespace(key)
503
- return key unless key && @namespace
607
+ return key unless key && namespace
504
608
 
505
609
  case key
506
610
  when Array
@@ -512,7 +616,7 @@ class Redis
512
616
  key.each { |k| yielder.yield rem_namespace(k) }
513
617
  end
514
618
  else
515
- key.to_s.sub(/\A#{@namespace}:/, '')
619
+ key.to_s.sub(/\A#{namespace}:/, '')
516
620
  end
517
621
  end
518
622
 
@@ -527,5 +631,10 @@ class Redis
527
631
  Enumerator.new(&block)
528
632
  end
529
633
  end
634
+
635
+ def supports_scan?
636
+ redis_version = @redis.info["redis_version"]
637
+ Gem::Version.new(redis_version) >= Gem::Version.new("2.8.0")
638
+ end
530
639
  end
531
640
  end
@@ -31,7 +31,7 @@ describe Redis::Namespace do
31
31
  end
32
32
 
33
33
  before(:each) do
34
- allow(redis).to receive(:unhandled) do |*args|
34
+ allow(redis).to receive(:unhandled) do |*args|
35
35
  "unhandled(#{args.inspect})"
36
36
  end
37
37
  allow(redis).to receive(:flushdb).and_return("OK")
@@ -43,7 +43,7 @@ describe Redis::Namespace do
43
43
  its(:deprecations?) { should be true }
44
44
 
45
45
  context('with an unhandled command') do
46
- it { should_not respond_to :unhandled }
46
+ it { is_expected.not_to respond_to :unhandled }
47
47
 
48
48
  it('raises a NoMethodError') do
49
49
  expect do
@@ -53,7 +53,7 @@ describe Redis::Namespace do
53
53
  end
54
54
 
55
55
  context('with an administrative command') do
56
- it { should_not respond_to :flushdb }
56
+ it { is_expected.not_to respond_to :flushdb }
57
57
 
58
58
  it('raises a NoMethodError') do
59
59
  expect do
@@ -69,7 +69,7 @@ describe Redis::Namespace do
69
69
  its(:deprecations?) { should be false }
70
70
 
71
71
  context('with an an unhandled command') do
72
- it { should respond_to :unhandled }
72
+ it { is_expected.to respond_to :unhandled }
73
73
 
74
74
  it 'blindly passes through' do
75
75
  expect(redis).to receive(:unhandled)
@@ -106,7 +106,7 @@ describe Redis::Namespace do
106
106
  end
107
107
 
108
108
  context('with an administrative command') do
109
- it { should respond_to :flushdb }
109
+ it { is_expected.to respond_to :flushdb }
110
110
  it 'processes the command' do
111
111
  expect(redis).to receive(:flushdb)
112
112
  capture_stderr { namespaced.flushdb }