redis-namespace 1.7.0 → 1.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9ebbb9000d7b34a0a8386ac9d72e2f63f4573720b22a8eb7e3676c0c1759200b
4
- data.tar.gz: ec5c495b22e58cc98d4c759f7873f2d2f9f69c8205b750062207b6f4b1f45897
3
+ metadata.gz: 8c4fe7070f484a694ce41f97a0297e03d92cfa67746903d9fb74fa8439410494
4
+ data.tar.gz: 0cbfe04c2ca7b2d42ee603f0d67ba21c13cb938832cc99f89a2323842bb828a3
5
5
  SHA512:
6
- metadata.gz: e05aa113c14df634a64fdfad7891781ace6a5060a533e9417f94476235f3e05fd33859c4653fcbe8f417582e9257cdf20b1599ac1b2e4cbe38ccaf7c88af2dba
7
- data.tar.gz: 220e3ec476e5613dd3a8529f2f7b30f7aba9433a1f4ab4402c55be555ef00ff5651738824ddc583e83e7cbad9297d1f1a107bd61e4b44df5e8c9efa50589cfea
6
+ metadata.gz: ec085b7e9fba5241cb2e2ad4ccf22d4282001a0ee37731e408a2135ea5e5439747441046cc6c48a2a372afe99f2fb6a67401867a20c8605e41206954eca72078
7
+ data.tar.gz: 0eff5c268bba88cb8bd5e0ab8115a404fed3180e82ebc4297ab8675da6c73e13b3bb943e77ec0392de4460683488589bbc227d95515aa5dc70cdb4236b58801d
data/README.md CHANGED
@@ -33,6 +33,13 @@ redis_connection.get('ns:foo')
33
33
  # => nil
34
34
  ```
35
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
+ ```
42
+
36
43
  Installation
37
44
  ============
38
45
 
@@ -79,7 +86,7 @@ namespaced.redis.flushall()
79
86
 
80
87
  As mentioned above, 2.0 will remove blind passthrough and the administrative command passthrough.
81
88
  By default in 1.5+, deprecation warnings are present and enabled;
82
- 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.
83
90
 
84
91
  Early opt-in
85
92
  ------------
@@ -2,6 +2,6 @@
2
2
 
3
3
  class Redis
4
4
  class Namespace
5
- VERSION = '1.7.0'
5
+ VERSION = '1.10.0'
6
6
  end
7
7
  end
@@ -68,12 +68,14 @@ class Redis
68
68
  "decrby" => [ :first ],
69
69
  "del" => [ :all ],
70
70
  "dump" => [ :first ],
71
- "exists" => [ :first ],
71
+ "exists" => [ :all ],
72
+ "exists?" => [ :all ],
72
73
  "expire" => [ :first ],
73
74
  "expireat" => [ :first ],
74
75
  "eval" => [ :eval_style ],
75
76
  "evalsha" => [ :eval_style ],
76
77
  "get" => [ :first ],
78
+ "getex" => [ :first ],
77
79
  "getbit" => [ :first ],
78
80
  "getrange" => [ :first ],
79
81
  "getset" => [ :first ],
@@ -136,6 +138,7 @@ class Redis
136
138
  "rpush" => [ :first ],
137
139
  "rpushx" => [ :first ],
138
140
  "sadd" => [ :first ],
141
+ "sadd?" => [ :first ],
139
142
  "scard" => [ :first ],
140
143
  "scan" => [ :scan_style, :second ],
141
144
  "scan_each" => [ :scan_style, :all ],
@@ -150,6 +153,7 @@ class Redis
150
153
  "sinterstore" => [ :all ],
151
154
  "sismember" => [ :first ],
152
155
  "smembers" => [ :first ],
156
+ "smismember" => [ :first ],
153
157
  "smove" => [ :exclude_last ],
154
158
  "sort" => [ :sort ],
155
159
  "spop" => [ :first ],
@@ -199,6 +203,7 @@ class Redis
199
203
  HELPER_COMMANDS = {
200
204
  "auth" => [],
201
205
  "disconnect!" => [],
206
+ "close" => [],
202
207
  "echo" => [],
203
208
  "ping" => [],
204
209
  "time" => [],
@@ -235,13 +240,20 @@ class Redis
235
240
  # Support 1.8.7 by providing a namespaced reference to Enumerable::Enumerator
236
241
  Enumerator = Enumerable::Enumerator unless defined?(::Enumerator)
237
242
 
243
+ # This is used by the Redis gem to determine whether or not to display that deprecation message.
244
+ @sadd_returns_boolean = true
245
+
246
+ class << self
247
+ attr_accessor :sadd_returns_boolean
248
+ end
249
+
238
250
  attr_writer :namespace
239
251
  attr_reader :redis
240
252
  attr_accessor :warning
241
253
 
242
254
  def initialize(namespace, options = {})
243
255
  @namespace = namespace
244
- @redis = options[:redis] || Redis.current
256
+ @redis = options[:redis] || Redis.new
245
257
  @warning = !!options.fetch(:warning) do
246
258
  !ENV['REDIS_NAMESPACE_QUIET']
247
259
  end
@@ -260,7 +272,7 @@ class Redis
260
272
  end
261
273
 
262
274
  def client
263
- warn("The client method is deprecated as of redis-rb 4.0.0, please use the new _client" +
275
+ warn("The client method is deprecated as of redis-rb 4.0.0, please use the new _client " +
264
276
  "method instead. Support for the old method will be removed in redis-namespace 2.0.") if @has_new_client_method && deprecations?
265
277
  _client
266
278
  end
@@ -306,7 +318,7 @@ class Redis
306
318
  :redis => @redis)
307
319
  end
308
320
 
309
- @namespace
321
+ @namespace.respond_to?(:call) ? @namespace.call : @namespace
310
322
  end
311
323
 
312
324
  def full_namespace
@@ -314,7 +326,7 @@ class Redis
314
326
  end
315
327
 
316
328
  def connection
317
- @redis.connection.tap { |info| info[:namespace] = @namespace }
329
+ @redis.connection.tap { |info| info[:namespace] = namespace }
318
330
  end
319
331
 
320
332
  def exec
@@ -324,6 +336,33 @@ class Redis
324
336
  def eval(*args)
325
337
  call_with_namespace(:eval, *args)
326
338
  end
339
+ ruby2_keywords(:eval) if respond_to?(:ruby2_keywords, true)
340
+
341
+ # This operation can run for a very long time if the namespace contains lots of keys!
342
+ # It should be used in tests, or when the namespace is small enough
343
+ # and you are sure you know what you are doing.
344
+ def clear
345
+ if warning?
346
+ warn("This operation can run for a very long time if the namespace contains lots of keys! " +
347
+ "It should be used in tests, or when the namespace is small enough " +
348
+ "and you are sure you know what you are doing.")
349
+ end
350
+
351
+ batch_size = 1000
352
+
353
+ if supports_scan?
354
+ cursor = "0"
355
+ begin
356
+ cursor, keys = scan(cursor, count: batch_size)
357
+ del(*keys) unless keys.empty?
358
+ end until cursor == "0"
359
+ else
360
+ all_keys = keys("*")
361
+ all_keys.each_slice(batch_size) do |keys|
362
+ del(*keys)
363
+ end
364
+ end
365
+ end
327
366
 
328
367
  ADMINISTRATIVE_COMMANDS.keys.each do |command|
329
368
  define_method(command) do |*args, &block|
@@ -339,6 +378,7 @@ class Redis
339
378
  end
340
379
  call_with_namespace(command, *args, &block)
341
380
  end
381
+ ruby2_keywords(command) if respond_to?(:ruby2_keywords, true)
342
382
  end
343
383
 
344
384
  COMMANDS.keys.each do |command|
@@ -348,6 +388,7 @@ class Redis
348
388
  define_method(command) do |*args, &block|
349
389
  call_with_namespace(command, *args, &block)
350
390
  end
391
+ ruby2_keywords(command) if respond_to?(:ruby2_keywords, true)
351
392
  end
352
393
 
353
394
  def method_missing(command, *args, &block)
@@ -365,15 +406,17 @@ class Redis
365
406
  "passthrough has been deprecated and will be removed in " +
366
407
  "redis-namespace 2.0 (at #{call_site})")
367
408
  end
368
- @redis.send(command, *args, &block)
409
+
410
+ wrapped_send(@redis, command, args, &block)
369
411
  else
370
412
  super
371
413
  end
372
414
  end
415
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
373
416
 
374
417
  def inspect
375
418
  "<#{self.class.name} v#{VERSION} with client v#{Redis::VERSION} "\
376
- "for #{@redis.id}/#{@namespace}>"
419
+ "for #{@redis.id}/#{full_namespace}>"
377
420
  end
378
421
 
379
422
  def respond_to_missing?(command, include_all=false)
@@ -405,6 +448,7 @@ class Redis
405
448
  case before
406
449
  when :first
407
450
  args[0] = add_namespace(args[0]) if args[0]
451
+ args[-1] = ruby2_keywords_hash(args[-1]) if args[-1].is_a?(Hash)
408
452
  when :all
409
453
  args = add_namespace(args)
410
454
  when :exclude_first
@@ -417,13 +461,14 @@ class Redis
417
461
  args.push(last) if last
418
462
  when :exclude_options
419
463
  if args.last.is_a?(Hash)
420
- last = args.pop
464
+ last = ruby2_keywords_hash(args.pop)
421
465
  args = add_namespace(args)
422
466
  args.push(last)
423
467
  else
424
468
  args = add_namespace(args)
425
469
  end
426
470
  when :alternate
471
+ args = args.flatten
427
472
  args.each_with_index { |a, i| args[i] = add_namespace(a) if i.even? }
428
473
  when :sort
429
474
  args[0] = add_namespace(args[0]) if args[0]
@@ -437,6 +482,7 @@ class Redis
437
482
  args[1][:get].each_index do |i|
438
483
  args[1][:get][i] = add_namespace(args[1][:get][i]) unless args[1][:get][i] == "#"
439
484
  end
485
+ args[1] = ruby2_keywords_hash(args[1])
440
486
  end
441
487
  when :eval_style
442
488
  # redis.eval() and evalsha() can either take the form:
@@ -457,7 +503,7 @@ class Redis
457
503
  when :scan_style
458
504
  options = (args.last.kind_of?(Hash) ? args.pop : {})
459
505
  options[:match] = add_namespace(options.fetch(:match, '*'))
460
- args << options
506
+ args << ruby2_keywords_hash(options)
461
507
 
462
508
  if block
463
509
  original_block = block
@@ -466,7 +512,7 @@ class Redis
466
512
  end
467
513
 
468
514
  # Dispatch the command to Redis and store the result.
469
- result = @redis.send(command, *args, &block)
515
+ result = wrapped_send(@redis, command, args, &block)
470
516
 
471
517
  # Don't try to remove namespace from a Redis::Future, you can't.
472
518
  return result if result.is_a?(Redis::Future)
@@ -483,9 +529,36 @@ class Redis
483
529
 
484
530
  result
485
531
  end
532
+ ruby2_keywords(:call_with_namespace) if respond_to?(:ruby2_keywords, true)
533
+
534
+ protected
535
+
536
+ def redis=(redis)
537
+ @redis = redis
538
+ end
486
539
 
487
540
  private
488
541
 
542
+ if Hash.respond_to?(:ruby2_keywords_hash)
543
+ def ruby2_keywords_hash(kwargs)
544
+ Hash.ruby2_keywords_hash(kwargs)
545
+ end
546
+ else
547
+ def ruby2_keywords_hash(kwargs)
548
+ kwargs
549
+ end
550
+ end
551
+
552
+ def wrapped_send(redis_client, command, args = [], &block)
553
+ if redis_client.class.name == "ConnectionPool"
554
+ redis_client.with do |pool_connection|
555
+ pool_connection.send(command, *args, &block)
556
+ end
557
+ else
558
+ redis_client.send(command, *args, &block)
559
+ end
560
+ end
561
+
489
562
  # Avoid modifying the caller's (pass-by-reference) arguments.
490
563
  def clone_args(arg)
491
564
  if arg.is_a?(Array)
@@ -502,18 +575,16 @@ class Redis
502
575
  end
503
576
 
504
577
  def namespaced_block(command, &block)
505
- redis.send(command) do |r|
506
- begin
507
- original, @redis = @redis, r
508
- yield self
509
- ensure
510
- @redis = original
511
- end
578
+ if block.arity == 0
579
+ wrapped_send(redis, command, &block)
580
+ else
581
+ outer_block = proc { |r| copy = dup; copy.redis = r; yield copy }
582
+ wrapped_send(redis, command, &outer_block)
512
583
  end
513
584
  end
514
585
 
515
586
  def add_namespace(key)
516
- return key unless key && @namespace
587
+ return key unless key && namespace
517
588
 
518
589
  case key
519
590
  when Array
@@ -522,12 +593,12 @@ class Redis
522
593
  key.keys.each {|k| key[add_namespace(k)] = key.delete(k)}
523
594
  key
524
595
  else
525
- "#{@namespace}:#{key}"
596
+ "#{namespace}:#{key}"
526
597
  end
527
598
  end
528
599
 
529
600
  def rem_namespace(key)
530
- return key unless key && @namespace
601
+ return key unless key && namespace
531
602
 
532
603
  case key
533
604
  when Array
@@ -539,7 +610,7 @@ class Redis
539
610
  key.each { |k| yielder.yield rem_namespace(k) }
540
611
  end
541
612
  else
542
- key.to_s.sub(/\A#{@namespace}:/, '')
613
+ key.to_s.sub(/\A#{namespace}:/, '')
543
614
  end
544
615
  end
545
616
 
@@ -554,5 +625,10 @@ class Redis
554
625
  Enumerator.new(&block)
555
626
  end
556
627
  end
628
+
629
+ def supports_scan?
630
+ redis_version = @redis.info["redis_version"]
631
+ Gem::Version.new(redis_version) >= Gem::Version.new("2.8.0")
632
+ end
557
633
  end
558
634
  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")
data/spec/redis_spec.rb CHANGED
@@ -1,30 +1,20 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require File.dirname(__FILE__) + '/spec_helper'
4
+ require 'connection_pool'
4
5
 
5
6
  describe "redis" do
6
- @redis_version = Gem::Version.new(Redis.current.info["redis_version"])
7
+ @redis_version = Gem::Version.new(Redis.new.info["redis_version"])
7
8
  let(:redis_client) { @redis.respond_to?(:_client) ? @redis._client : @redis.client}
8
9
 
9
- before(:all) do
10
+ before(:each) do
10
11
  # use database 15 for testing so we dont accidentally step on your real data
11
12
  @redis = Redis.new :db => 15
12
- end
13
-
14
- before(:each) do
15
- @namespaced = Redis::Namespace.new(:ns, :redis => @redis)
16
13
  @redis.flushdb
14
+ @namespaced = Redis::Namespace.new(:ns, :redis => @redis)
17
15
  @redis.set('foo', 'bar')
18
16
  end
19
17
 
20
- after(:each) do
21
- @redis.flushdb
22
- end
23
-
24
- after(:all) do
25
- @redis.quit
26
- end
27
-
28
18
  # redis-rb 3.3.4+
29
19
  it "should inject :namespace into connection info" do
30
20
  info = @redis.connection.merge(:namespace => :ns)
@@ -52,6 +42,17 @@ describe "redis" do
52
42
  expect(@namespaced.type('counter')).to eq('string')
53
43
  end
54
44
 
45
+ it "should work with Proc namespaces" do
46
+ namespace = Proc.new { :dynamic_ns }
47
+ namespaced = Redis::Namespace.new(namespace, redis: @redis)
48
+
49
+ expect(namespaced.get('foo')).to eq(nil)
50
+ namespaced.set('foo', 'chris')
51
+ expect(namespaced.get('foo')).to eq('chris')
52
+ @redis.set('foo', 'bob')
53
+ expect(@redis.get('foo')).to eq('bob')
54
+ end
55
+
55
56
  context 'when sending capital commands (issue 68)' do
56
57
  it 'should be able to use a namespace' do
57
58
  @namespaced.send('SET', 'fubar', 'quux')
@@ -106,6 +107,13 @@ describe "redis" do
106
107
  expect(@namespaced.lrange('bar',0,-1)).to eq(['bar'])
107
108
  end
108
109
 
110
+ it "should be able to use a namespace with getex" do
111
+ expect(@namespaced.set('mykey', 'Hello')).to eq('OK')
112
+ expect(@namespaced.getex('mykey', ex: 50)).to eq('Hello')
113
+ expect(@namespaced.get('mykey')).to eq('Hello')
114
+ expect(@namespaced.ttl('mykey')).to eq(50)
115
+ end
116
+
109
117
  it 'should be able to use a namespace with getbit' do
110
118
  @namespaced.set('foo','bar')
111
119
  expect(@namespaced.getbit('foo',1)).to eq(1)
@@ -140,10 +148,22 @@ describe "redis" do
140
148
 
141
149
  it 'should be able to use a namespace with setbit' do
142
150
  @namespaced.setbit('virgin_key', 1, 1)
143
- expect(@namespaced.exists('virgin_key')).to be true
151
+ expect(@namespaced.exists?('virgin_key')).to be true
144
152
  expect(@namespaced.get('virgin_key')).to eq(@namespaced.getrange('virgin_key',0,-1))
145
153
  end
146
154
 
155
+ it 'should be able to use a namespace with exists' do
156
+ @namespaced.set('foo', 1000)
157
+ @namespaced.set('bar', 2000)
158
+ expect(@namespaced.exists('foo', 'bar')).to eq(2)
159
+ end
160
+
161
+ it 'should be able to use a namespace with exists?' do
162
+ @namespaced.set('foo', 1000)
163
+ @namespaced.set('bar', 2000)
164
+ expect(@namespaced.exists?('does_not_exist', 'bar')).to eq(true)
165
+ end
166
+
147
167
  it 'should be able to use a namespace with bitpos' do
148
168
  @namespaced.setbit('bit_map', 42, 1)
149
169
  expect(@namespaced.bitpos('bit_map', 0)).to eq(0)
@@ -165,19 +185,44 @@ describe "redis" do
165
185
  expect(@namespaced.mapped_mget('foo', 'baz', 'bar')).to eq({'foo'=>'1000', 'bar'=>'2000', 'baz' => nil})
166
186
  end
167
187
 
188
+ it "should utilize connection_pool while using a namespace with mget" do
189
+ memo = @namespaced
190
+ connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
191
+ @namespaced = Redis::Namespace.new(:ns, redis: connection_pool)
192
+
193
+ expect(connection_pool).to receive(:with).and_call_original do |arg|
194
+ expect(arg).to be(an_instance_of(Redis))
195
+ end.at_least(:once)
196
+
197
+ @namespaced.set('foo', 1000)
198
+ @namespaced.set('bar', 2000)
199
+ expect(@namespaced.mapped_mget('foo', 'bar')).to eq({ 'foo' => '1000', 'bar' => '2000' })
200
+ expect(@namespaced.mapped_mget('foo', 'baz', 'bar')).to eq({'foo'=>'1000', 'bar'=>'2000', 'baz' => nil})
201
+ @redis.get('foo').should eq('bar')
202
+
203
+ @namespaced = memo
204
+ end
205
+
168
206
  it "should be able to use a namespace with mset" do
169
207
  @namespaced.mset('foo', '1000', 'bar', '2000')
170
208
  expect(@namespaced.mapped_mget('foo', 'bar')).to eq({ 'foo' => '1000', 'bar' => '2000' })
171
209
  expect(@namespaced.mapped_mget('foo', 'baz', 'bar')).to eq({ 'foo' => '1000', 'bar' => '2000', 'baz' => nil})
210
+
172
211
  @namespaced.mapped_mset('foo' => '3000', 'bar' => '5000')
173
212
  expect(@namespaced.mapped_mget('foo', 'bar')).to eq({ 'foo' => '3000', 'bar' => '5000' })
174
213
  expect(@namespaced.mapped_mget('foo', 'baz', 'bar')).to eq({ 'foo' => '3000', 'bar' => '5000', 'baz' => nil})
214
+
215
+ @namespaced.mset(['foo', '4000'], ['baz', '6000'])
216
+ expect(@namespaced.mapped_mget('foo', 'bar', 'baz')).to eq({ 'foo' => '4000', 'bar' => '5000', 'baz' => '6000' })
175
217
  end
176
218
 
177
219
  it "should be able to use a namespace with msetnx" do
178
220
  @namespaced.msetnx('foo', '1000', 'bar', '2000')
179
221
  expect(@namespaced.mapped_mget('foo', 'bar')).to eq({ 'foo' => '1000', 'bar' => '2000' })
180
222
  expect(@namespaced.mapped_mget('foo', 'baz', 'bar')).to eq({ 'foo' => '1000', 'bar' => '2000', 'baz' => nil})
223
+
224
+ @namespaced.msetnx(['baz', '4000'])
225
+ expect(@namespaced.mapped_mget('foo', 'baz', 'bar')).to eq({ 'foo' => '1000', 'bar' => '2000', 'baz' => '4000'})
181
226
  end
182
227
 
183
228
  it "should be able to use a namespace with mapped_msetnx" do
@@ -239,7 +284,7 @@ describe "redis" do
239
284
  @namespaced.zadd('sort2', 2, 2)
240
285
  @namespaced.zadd('sort2', 3, 3)
241
286
  @namespaced.zadd('sort2', 4, 4)
242
- @namespaced.zunionstore('union', ['sort1', 'sort2'], :weights => [2, 1])
287
+ @namespaced.zunionstore('union', ['sort1', 'sort2'], weights: [2, 1])
243
288
  expect(@namespaced.zrevrange('union', 0, -1)).to eq(%w( 2 4 3 1 ))
244
289
  end
245
290
 
@@ -313,6 +358,11 @@ describe "redis" do
313
358
  expect(values).to match_array(['banana', 'eggplant'])
314
359
  end
315
360
 
361
+ it "should add a new member" do
362
+ expect(@namespaced.sadd?('foo', 1)).to eq(true)
363
+ expect(@namespaced.sadd?('foo', 1)).to eq(false)
364
+ end
365
+
316
366
  it "should add namespace to sort" do
317
367
  @namespaced.sadd('foo', 1)
318
368
  @namespaced.sadd('foo', 2)
@@ -350,6 +400,26 @@ describe "redis" do
350
400
  expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
351
401
  end
352
402
 
403
+ it "should utilize connection_pool while adding namepsace to multi blocks" do
404
+ memo = @namespaced
405
+ connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
406
+ @namespaced = Redis::Namespace.new(:ns, redis: connection_pool)
407
+
408
+ expect(connection_pool).to receive(:with).and_call_original do |arg|
409
+ expect(arg).to be(an_instance_of(Redis))
410
+ end.at_least(:once)
411
+
412
+ @namespaced.mapped_hmset "foo", {"key" => "value"}
413
+ @namespaced.multi do |r|
414
+ r.del "foo"
415
+ r.mapped_hmset "foo", {"key1" => "value1"}
416
+ end
417
+ expect(@redis.get("foo")).to eq("bar")
418
+ expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
419
+
420
+ @namespaced = memo
421
+ end
422
+
353
423
  it "should pass through multi commands without block" do
354
424
  @namespaced.mapped_hmset "foo", {"key" => "value"}
355
425
 
@@ -361,6 +431,28 @@ describe "redis" do
361
431
  expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
362
432
  end
363
433
 
434
+ it "should utilize connection_pool while passing through multi commands without block" do
435
+ memo = @namespaced
436
+ connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
437
+ @namespaced = Redis::Namespace.new(:ns, redis: connection_pool)
438
+
439
+ expect(connection_pool).to receive(:with).and_call_original do |arg|
440
+ expect(arg).to be(an_instance_of(Redis))
441
+ end.at_least(:once)
442
+
443
+ @namespaced.mapped_hmset "foo", {"key" => "value"}
444
+
445
+ @namespaced.multi
446
+ @namespaced.del "foo"
447
+ @namespaced.mapped_hmset "foo", {"key1" => "value1"}
448
+ @namespaced.exec
449
+
450
+ expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
451
+ expect(@redis.get("foo")).to eq("bar")
452
+
453
+ @namespaced = memo
454
+ end
455
+
364
456
  it 'should return futures without attempting to remove namespaces' do
365
457
  @namespaced.multi do
366
458
  @future = @namespaced.keys('*')
@@ -377,6 +469,26 @@ describe "redis" do
377
469
  expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
378
470
  end
379
471
 
472
+ it "should utilize connection_pool while adding namespace to pipelined blocks" do
473
+ memo = @namespaced
474
+ connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
475
+ @namespaced = Redis::Namespace.new(:ns, redis: connection_pool)
476
+
477
+ expect(connection_pool).to receive(:with).and_call_original do |arg|
478
+ expect(arg).to be(an_instance_of(Redis))
479
+ end.at_least(:once)
480
+
481
+ @namespaced.mapped_hmset "foo", {"key" => "value"}
482
+ @namespaced.pipelined do |r|
483
+ r.del "foo"
484
+ r.mapped_hmset "foo", {"key1" => "value1"}
485
+ end
486
+ expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
487
+ expect(@redis.get("foo")).to eq("bar")
488
+
489
+ @namespaced = memo
490
+ end
491
+
380
492
  it "should returned response array from pipelined block" do
381
493
  @namespaced.mset "foo", "bar", "key", "value"
382
494
  result = @namespaced.pipelined do |r|
@@ -386,6 +498,27 @@ describe "redis" do
386
498
  expect(result).to eq(["bar", "value"])
387
499
  end
388
500
 
501
+ it "is thread safe for multi blocks" do
502
+ mon = Monitor.new
503
+ entered = false
504
+ entered_cond = mon.new_cond
505
+
506
+ thread = Thread.new do
507
+ mon.synchronize do
508
+ entered_cond.wait_until { entered }
509
+ @namespaced.multi
510
+ end
511
+ end
512
+
513
+ @namespaced.multi do |transaction|
514
+ entered = true
515
+ mon.synchronize { entered_cond.signal }
516
+ thread.join(0.1)
517
+ transaction.get("foo")
518
+ end
519
+ thread.join
520
+ end
521
+
389
522
  it "should add namespace to strlen" do
390
523
  @namespaced.set("mykey", "123456")
391
524
  expect(@namespaced.strlen("mykey")).to eq(6)
@@ -442,6 +575,45 @@ describe "redis" do
442
575
  expect { @namespaced.unknown('foo') }.to raise_exception NoMethodError
443
576
  end
444
577
 
578
+ describe '#inspect' do
579
+ let(:single_level_names) { %i[first] }
580
+ let(:double_level_names) { %i[first second] }
581
+ let(:triple_level_names) { %i[first second third] }
582
+ let(:namespace_builder) do
583
+ ->(redis, *namespaces) { namespaces.reduce(redis) { |r, n| Redis::Namespace.new(n, redis: r) } }
584
+ end
585
+ let(:regexp_builder) do
586
+ ->(*namespaces) { %r{/#{namespaces.join(':')}>\z} }
587
+ end
588
+
589
+ context 'when one namespace' do
590
+ let(:single_namespaced) { namespace_builder.call(@redis, *single_level_names) }
591
+ let(:regexp) { regexp_builder.call(*single_level_names) }
592
+
593
+ it 'should have correct ending of inspect string' do
594
+ expect(regexp =~ single_namespaced.inspect).not_to be(nil)
595
+ end
596
+ end
597
+
598
+ context 'when two namespaces' do
599
+ let(:double_namespaced) { namespace_builder.call(@redis, *double_level_names) }
600
+ let(:regexp) { regexp_builder.call(*double_level_names) }
601
+
602
+ it 'should have correct ending of inspect string' do
603
+ expect(regexp =~ double_namespaced.inspect).not_to be(nil)
604
+ end
605
+ end
606
+
607
+ context 'when three namespaces' do
608
+ let(:triple_namespaced) { namespace_builder.call(@redis, *triple_level_names) }
609
+ let(:regexp) { regexp_builder.call(*triple_level_names) }
610
+
611
+ it 'should have correct ending of inspect string' do
612
+ expect(regexp =~ triple_namespaced.inspect).not_to be(nil)
613
+ end
614
+ end
615
+ end
616
+
445
617
  # Redis 2.6 RC reports its version as 2.5.
446
618
  if @redis_version >= Gem::Version.new("2.5.0")
447
619
  describe "redis 2.6 commands" do
@@ -626,7 +798,7 @@ describe "redis" do
626
798
  expect(result).to match_array(namespaced_keys)
627
799
  end
628
800
  end
629
- end if Redis.current.respond_to?(:scan)
801
+ end if Redis.new.respond_to?(:scan)
630
802
 
631
803
  context '#scan_each' do
632
804
  context 'when :match supplied' do
@@ -659,7 +831,7 @@ describe "redis" do
659
831
  end
660
832
  end
661
833
  end
662
- end if Redis.current.respond_to?(:scan_each)
834
+ end if Redis.new.respond_to?(:scan_each)
663
835
  end
664
836
 
665
837
  context 'hash scan methods' do
@@ -687,7 +859,7 @@ describe "redis" do
687
859
  expect(results).to match_array(@redis.hgetall('ns:hsh').to_a)
688
860
  end
689
861
  end
690
- end if Redis.current.respond_to?(:hscan)
862
+ end if Redis.new.respond_to?(:hscan)
691
863
 
692
864
  context '#hscan_each' do
693
865
  context 'when :match supplied' do
@@ -720,7 +892,7 @@ describe "redis" do
720
892
  end
721
893
  end
722
894
  end
723
- end if Redis.current.respond_to?(:hscan_each)
895
+ end if Redis.new.respond_to?(:hscan_each)
724
896
  end
725
897
 
726
898
  context 'set scan methods' do
@@ -748,7 +920,7 @@ describe "redis" do
748
920
  expect(results).to match_array(set)
749
921
  end
750
922
  end
751
- end if Redis.current.respond_to?(:sscan)
923
+ end if Redis.new.respond_to?(:sscan)
752
924
 
753
925
  context '#sscan_each' do
754
926
  context 'when :match supplied' do
@@ -781,7 +953,7 @@ describe "redis" do
781
953
  end
782
954
  end
783
955
  end
784
- end if Redis.current.respond_to?(:sscan_each)
956
+ end if Redis.new.respond_to?(:sscan_each)
785
957
  end
786
958
 
787
959
  context 'zset scan methods' do
@@ -811,7 +983,7 @@ describe "redis" do
811
983
  expect(results).to match_array(hash.to_a)
812
984
  end
813
985
  end
814
- end if Redis.current.respond_to?(:zscan)
986
+ end if Redis.new.respond_to?(:zscan)
815
987
 
816
988
  context '#zscan_each' do
817
989
  context 'when :match supplied' do
@@ -844,7 +1016,7 @@ describe "redis" do
844
1016
  end
845
1017
  end
846
1018
  end
847
- end if Redis.current.respond_to?(:zscan_each)
1019
+ end if Redis.new.respond_to?(:zscan_each)
848
1020
  end
849
1021
  end
850
1022
  end
@@ -881,4 +1053,30 @@ describe "redis" do
881
1053
  expect(sub_sub_namespaced.full_namespace).to eql("ns:sub1:sub2")
882
1054
  end
883
1055
  end
1056
+
1057
+ describe :clear do
1058
+ it "warns with helpful output" do
1059
+ expect { @namespaced.clear }.to output(/can run for a very long time/).to_stderr
1060
+ end
1061
+
1062
+ it "should delete all the keys" do
1063
+ @redis.set("foo", "bar")
1064
+ @namespaced.mset("foo1", "bar", "foo2", "bar")
1065
+ capture_stderr { @namespaced.clear }
1066
+
1067
+ expect(@redis.keys).to eq ["foo"]
1068
+ expect(@namespaced.keys).to be_empty
1069
+ end
1070
+
1071
+ it "should delete all the keys in older redis" do
1072
+ allow(@redis).to receive(:info).and_return({ "redis_version" => "2.7.0" })
1073
+
1074
+ @redis.set("foo", "bar")
1075
+ @namespaced.mset("foo1", "bar", "foo2", "bar")
1076
+ capture_stderr { @namespaced.clear }
1077
+
1078
+ expect(@redis.keys).to eq ["foo"]
1079
+ expect(@namespaced.keys).to be_empty
1080
+ end
1081
+ end
884
1082
  end
data/spec/spec_helper.rb CHANGED
@@ -12,6 +12,10 @@ $TESTING=true
12
12
  $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
13
13
  require 'redis/namespace'
14
14
 
15
+ if Redis.respond_to?(:exists_returns_integer=)
16
+ Redis.exists_returns_integer = true
17
+ end
18
+
15
19
  module Helper
16
20
  def capture_stderr(io = nil)
17
21
  require 'stringio'
metadata CHANGED
@@ -1,17 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-namespace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Wanstrath
8
8
  - Terence Lee
9
9
  - Steve Klabnik
10
10
  - Ryan Biesemeyer
11
- autorequire:
11
+ - Mike Bianco
12
+ autorequire:
12
13
  bindir: bin
13
14
  cert_chain: []
14
- date: 2019-12-11 00:00:00.000000000 Z
15
+ date: 2022-12-20 00:00:00.000000000 Z
15
16
  dependencies:
16
17
  - !ruby/object:Gem::Dependency
17
18
  name: redis
@@ -19,28 +20,28 @@ dependencies:
19
20
  requirements:
20
21
  - - ">="
21
22
  - !ruby/object:Gem::Version
22
- version: 3.0.4
23
+ version: '4'
23
24
  type: :runtime
24
25
  prerelease: false
25
26
  version_requirements: !ruby/object:Gem::Requirement
26
27
  requirements:
27
28
  - - ">="
28
29
  - !ruby/object:Gem::Version
29
- version: 3.0.4
30
+ version: '4'
30
31
  - !ruby/object:Gem::Dependency
31
32
  name: rake
32
33
  requirement: !ruby/object:Gem::Requirement
33
34
  requirements:
34
- - - "~>"
35
+ - - ">="
35
36
  - !ruby/object:Gem::Version
36
- version: '10.1'
37
+ version: '0'
37
38
  type: :development
38
39
  prerelease: false
39
40
  version_requirements: !ruby/object:Gem::Requirement
40
41
  requirements:
41
- - - "~>"
42
+ - - ">="
42
43
  - !ruby/object:Gem::Version
43
- version: '10.1'
44
+ version: '0'
44
45
  - !ruby/object:Gem::Dependency
45
46
  name: rspec
46
47
  requirement: !ruby/object:Gem::Requirement
@@ -69,6 +70,20 @@ dependencies:
69
70
  - - ">="
70
71
  - !ruby/object:Gem::Version
71
72
  version: '0'
73
+ - !ruby/object:Gem::Dependency
74
+ name: connection_pool
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ type: :development
81
+ prerelease: false
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
72
87
  description: |
73
88
  Adds a Redis::Namespace class which can be used to namespace calls
74
89
  to Redis. This is useful when using a single instance of Redis with
@@ -78,6 +93,7 @@ email:
78
93
  - hone02@gmail.com
79
94
  - steve@steveklabnik.com
80
95
  - me@yaauie.com
96
+ - mike@mikebian.co
81
97
  executables: []
82
98
  extensions: []
83
99
  extra_rdoc_files: []
@@ -91,17 +107,21 @@ files:
91
107
  - spec/deprecation_spec.rb
92
108
  - spec/redis_spec.rb
93
109
  - spec/spec_helper.rb
94
- homepage: http://github.com/resque/redis-namespace
110
+ homepage: https://github.com/resque/redis-namespace
95
111
  licenses:
96
112
  - MIT
97
- metadata: {}
98
- post_install_message:
113
+ metadata:
114
+ bug_tracker_uri: https://github.com/resque/redis-namespace/issues
115
+ changelog_uri: https://github.com/resque/redis-namespace/blob/master/CHANGELOG.md
116
+ documentation_uri: https://www.rubydoc.info/gems/redis-namespace/1.10.0
117
+ rubygems_mfa_required: 'true'
118
+ post_install_message:
99
119
  rdoc_options: []
100
120
  require_paths:
101
121
  - lib
102
122
  required_ruby_version: !ruby/object:Gem::Requirement
103
123
  requirements:
104
- - - "~>"
124
+ - - ">="
105
125
  - !ruby/object:Gem::Version
106
126
  version: '2.4'
107
127
  required_rubygems_version: !ruby/object:Gem::Requirement
@@ -110,8 +130,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
130
  - !ruby/object:Gem::Version
111
131
  version: '0'
112
132
  requirements: []
113
- rubygems_version: 3.0.3
114
- signing_key:
133
+ rubygems_version: 3.3.15
134
+ signing_key:
115
135
  specification_version: 4
116
136
  summary: Namespaces Redis commands.
117
137
  test_files: []