redis-namespace 1.7.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
2
  SHA256:
3
- metadata.gz: 9ebbb9000d7b34a0a8386ac9d72e2f63f4573720b22a8eb7e3676c0c1759200b
4
- data.tar.gz: ec5c495b22e58cc98d4c759f7873f2d2f9f69c8205b750062207b6f4b1f45897
3
+ metadata.gz: e003f4015162bfd465e79dd90e54b44ae46f0ec269118e10c08215d639166296
4
+ data.tar.gz: 16078b51e1bdab24977aa2b6fb7d0cd4761a7c41c340fcdacfb96dd32216c061
5
5
  SHA512:
6
- metadata.gz: e05aa113c14df634a64fdfad7891781ace6a5060a533e9417f94476235f3e05fd33859c4653fcbe8f417582e9257cdf20b1599ac1b2e4cbe38ccaf7c88af2dba
7
- data.tar.gz: 220e3ec476e5613dd3a8529f2f7b30f7aba9433a1f4ab4402c55be555ef00ff5651738824ddc583e83e7cbad9297d1f1a107bd61e4b44df5e8c9efa50589cfea
6
+ metadata.gz: fb965b575fe3ec2f2f45b99a71839f569f647681939b16576c053ab3e25bf85db1db030941bbe6eaeecc624eeeaf8c0497c58c53c4231440896e9e116bc189e3
7
+ data.tar.gz: 07b2b2e20c4a152b3e3a4cfbf6abad773f335e098067a9b213e29dd80cfc4519f3d27c43febe6cf8261ffe95e73184e23a1b9ff557007eb7a8308e43a398bbb1
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.11.0'
6
6
  end
7
7
  end
@@ -68,12 +68,15 @@ 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 ],
75
+ "expiretime" => [ :first ],
74
76
  "eval" => [ :eval_style ],
75
77
  "evalsha" => [ :eval_style ],
76
78
  "get" => [ :first ],
79
+ "getex" => [ :first ],
77
80
  "getbit" => [ :first ],
78
81
  "getrange" => [ :first ],
79
82
  "getset" => [ :first ],
@@ -120,6 +123,7 @@ class Redis
120
123
  "persist" => [ :first ],
121
124
  "pexpire" => [ :first ],
122
125
  "pexpireat" => [ :first ],
126
+ "pexpiretime" => [ :first ],
123
127
  "pfadd" => [ :first ],
124
128
  "pfcount" => [ :all ],
125
129
  "pfmerge" => [ :all ],
@@ -136,6 +140,7 @@ class Redis
136
140
  "rpush" => [ :first ],
137
141
  "rpushx" => [ :first ],
138
142
  "sadd" => [ :first ],
143
+ "sadd?" => [ :first ],
139
144
  "scard" => [ :first ],
140
145
  "scan" => [ :scan_style, :second ],
141
146
  "scan_each" => [ :scan_style, :all ],
@@ -150,11 +155,13 @@ class Redis
150
155
  "sinterstore" => [ :all ],
151
156
  "sismember" => [ :first ],
152
157
  "smembers" => [ :first ],
158
+ "smismember" => [ :first ],
153
159
  "smove" => [ :exclude_last ],
154
160
  "sort" => [ :sort ],
155
161
  "spop" => [ :first ],
156
162
  "srandmember" => [ :first ],
157
163
  "srem" => [ :first ],
164
+ "srem?" => [ :first ],
158
165
  "sscan" => [ :first ],
159
166
  "sscan_each" => [ :first ],
160
167
  "strlen" => [ :first ],
@@ -199,6 +206,7 @@ class Redis
199
206
  HELPER_COMMANDS = {
200
207
  "auth" => [],
201
208
  "disconnect!" => [],
209
+ "close" => [],
202
210
  "echo" => [],
203
211
  "ping" => [],
204
212
  "time" => [],
@@ -235,13 +243,23 @@ class Redis
235
243
  # Support 1.8.7 by providing a namespaced reference to Enumerable::Enumerator
236
244
  Enumerator = Enumerable::Enumerator unless defined?(::Enumerator)
237
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
+
238
256
  attr_writer :namespace
239
257
  attr_reader :redis
240
258
  attr_accessor :warning
241
259
 
242
260
  def initialize(namespace, options = {})
243
261
  @namespace = namespace
244
- @redis = options[:redis] || Redis.current
262
+ @redis = options[:redis] || Redis.new
245
263
  @warning = !!options.fetch(:warning) do
246
264
  !ENV['REDIS_NAMESPACE_QUIET']
247
265
  end
@@ -260,7 +278,7 @@ class Redis
260
278
  end
261
279
 
262
280
  def client
263
- warn("The client method is deprecated as of redis-rb 4.0.0, please use the new _client" +
281
+ warn("The client method is deprecated as of redis-rb 4.0.0, please use the new _client " +
264
282
  "method instead. Support for the old method will be removed in redis-namespace 2.0.") if @has_new_client_method && deprecations?
265
283
  _client
266
284
  end
@@ -306,7 +324,7 @@ class Redis
306
324
  :redis => @redis)
307
325
  end
308
326
 
309
- @namespace
327
+ @namespace.respond_to?(:call) ? @namespace.call : @namespace
310
328
  end
311
329
 
312
330
  def full_namespace
@@ -314,7 +332,7 @@ class Redis
314
332
  end
315
333
 
316
334
  def connection
317
- @redis.connection.tap { |info| info[:namespace] = @namespace }
335
+ @redis.connection.tap { |info| info[:namespace] = namespace }
318
336
  end
319
337
 
320
338
  def exec
@@ -324,6 +342,33 @@ class Redis
324
342
  def eval(*args)
325
343
  call_with_namespace(:eval, *args)
326
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
356
+
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
327
372
 
328
373
  ADMINISTRATIVE_COMMANDS.keys.each do |command|
329
374
  define_method(command) do |*args, &block|
@@ -339,6 +384,7 @@ class Redis
339
384
  end
340
385
  call_with_namespace(command, *args, &block)
341
386
  end
387
+ ruby2_keywords(command) if respond_to?(:ruby2_keywords, true)
342
388
  end
343
389
 
344
390
  COMMANDS.keys.each do |command|
@@ -348,6 +394,7 @@ class Redis
348
394
  define_method(command) do |*args, &block|
349
395
  call_with_namespace(command, *args, &block)
350
396
  end
397
+ ruby2_keywords(command) if respond_to?(:ruby2_keywords, true)
351
398
  end
352
399
 
353
400
  def method_missing(command, *args, &block)
@@ -365,15 +412,17 @@ class Redis
365
412
  "passthrough has been deprecated and will be removed in " +
366
413
  "redis-namespace 2.0 (at #{call_site})")
367
414
  end
368
- @redis.send(command, *args, &block)
415
+
416
+ wrapped_send(@redis, command, args, &block)
369
417
  else
370
418
  super
371
419
  end
372
420
  end
421
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
373
422
 
374
423
  def inspect
375
424
  "<#{self.class.name} v#{VERSION} with client v#{Redis::VERSION} "\
376
- "for #{@redis.id}/#{@namespace}>"
425
+ "for #{@redis.id}/#{full_namespace}>"
377
426
  end
378
427
 
379
428
  def respond_to_missing?(command, include_all=false)
@@ -405,6 +454,7 @@ class Redis
405
454
  case before
406
455
  when :first
407
456
  args[0] = add_namespace(args[0]) if args[0]
457
+ args[-1] = ruby2_keywords_hash(args[-1]) if args[-1].is_a?(Hash)
408
458
  when :all
409
459
  args = add_namespace(args)
410
460
  when :exclude_first
@@ -417,13 +467,14 @@ class Redis
417
467
  args.push(last) if last
418
468
  when :exclude_options
419
469
  if args.last.is_a?(Hash)
420
- last = args.pop
470
+ last = ruby2_keywords_hash(args.pop)
421
471
  args = add_namespace(args)
422
472
  args.push(last)
423
473
  else
424
474
  args = add_namespace(args)
425
475
  end
426
476
  when :alternate
477
+ args = args.flatten
427
478
  args.each_with_index { |a, i| args[i] = add_namespace(a) if i.even? }
428
479
  when :sort
429
480
  args[0] = add_namespace(args[0]) if args[0]
@@ -437,6 +488,7 @@ class Redis
437
488
  args[1][:get].each_index do |i|
438
489
  args[1][:get][i] = add_namespace(args[1][:get][i]) unless args[1][:get][i] == "#"
439
490
  end
491
+ args[1] = ruby2_keywords_hash(args[1])
440
492
  end
441
493
  when :eval_style
442
494
  # redis.eval() and evalsha() can either take the form:
@@ -457,7 +509,7 @@ class Redis
457
509
  when :scan_style
458
510
  options = (args.last.kind_of?(Hash) ? args.pop : {})
459
511
  options[:match] = add_namespace(options.fetch(:match, '*'))
460
- args << options
512
+ args << ruby2_keywords_hash(options)
461
513
 
462
514
  if block
463
515
  original_block = block
@@ -466,7 +518,7 @@ class Redis
466
518
  end
467
519
 
468
520
  # Dispatch the command to Redis and store the result.
469
- result = @redis.send(command, *args, &block)
521
+ result = wrapped_send(@redis, command, args, &block)
470
522
 
471
523
  # Don't try to remove namespace from a Redis::Future, you can't.
472
524
  return result if result.is_a?(Redis::Future)
@@ -483,9 +535,36 @@ class Redis
483
535
 
484
536
  result
485
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
486
545
 
487
546
  private
488
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
+
489
568
  # Avoid modifying the caller's (pass-by-reference) arguments.
490
569
  def clone_args(arg)
491
570
  if arg.is_a?(Array)
@@ -502,18 +581,16 @@ class Redis
502
581
  end
503
582
 
504
583
  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
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)
512
589
  end
513
590
  end
514
591
 
515
592
  def add_namespace(key)
516
- return key unless key && @namespace
593
+ return key unless key && namespace
517
594
 
518
595
  case key
519
596
  when Array
@@ -522,12 +599,12 @@ class Redis
522
599
  key.keys.each {|k| key[add_namespace(k)] = key.delete(k)}
523
600
  key
524
601
  else
525
- "#{@namespace}:#{key}"
602
+ "#{namespace}:#{key}"
526
603
  end
527
604
  end
528
605
 
529
606
  def rem_namespace(key)
530
- return key unless key && @namespace
607
+ return key unless key && namespace
531
608
 
532
609
  case key
533
610
  when Array
@@ -539,7 +616,7 @@ class Redis
539
616
  key.each { |k| yielder.yield rem_namespace(k) }
540
617
  end
541
618
  else
542
- key.to_s.sub(/\A#{@namespace}:/, '')
619
+ key.to_s.sub(/\A#{namespace}:/, '')
543
620
  end
544
621
  end
545
622
 
@@ -554,5 +631,10 @@ class Redis
554
631
  Enumerator.new(&block)
555
632
  end
556
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
557
639
  end
558
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")
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,17 @@ 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
+
366
+ it "should remove members" do
367
+ @namespaced.sadd('foo', 1)
368
+ expect(@namespaced.srem?('foo', 1)).to eq(true)
369
+ expect(@namespaced.srem?('foo', 1)).to eq(false)
370
+ end
371
+
316
372
  it "should add namespace to sort" do
317
373
  @namespaced.sadd('foo', 1)
318
374
  @namespaced.sadd('foo', 2)
@@ -350,6 +406,26 @@ describe "redis" do
350
406
  expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
351
407
  end
352
408
 
409
+ it "should utilize connection_pool while adding namepsace to multi blocks" do
410
+ memo = @namespaced
411
+ connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
412
+ @namespaced = Redis::Namespace.new(:ns, redis: connection_pool)
413
+
414
+ expect(connection_pool).to receive(:with).and_call_original do |arg|
415
+ expect(arg).to be(an_instance_of(Redis))
416
+ end.at_least(:once)
417
+
418
+ @namespaced.mapped_hmset "foo", {"key" => "value"}
419
+ @namespaced.multi do |r|
420
+ r.del "foo"
421
+ r.mapped_hmset "foo", {"key1" => "value1"}
422
+ end
423
+ expect(@redis.get("foo")).to eq("bar")
424
+ expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
425
+
426
+ @namespaced = memo
427
+ end
428
+
353
429
  it "should pass through multi commands without block" do
354
430
  @namespaced.mapped_hmset "foo", {"key" => "value"}
355
431
 
@@ -361,6 +437,28 @@ describe "redis" do
361
437
  expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
362
438
  end
363
439
 
440
+ it "should utilize connection_pool while passing through multi commands without block" do
441
+ memo = @namespaced
442
+ connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
443
+ @namespaced = Redis::Namespace.new(:ns, redis: connection_pool)
444
+
445
+ expect(connection_pool).to receive(:with).and_call_original do |arg|
446
+ expect(arg).to be(an_instance_of(Redis))
447
+ end.at_least(:once)
448
+
449
+ @namespaced.mapped_hmset "foo", {"key" => "value"}
450
+
451
+ @namespaced.multi
452
+ @namespaced.del "foo"
453
+ @namespaced.mapped_hmset "foo", {"key1" => "value1"}
454
+ @namespaced.exec
455
+
456
+ expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
457
+ expect(@redis.get("foo")).to eq("bar")
458
+
459
+ @namespaced = memo
460
+ end
461
+
364
462
  it 'should return futures without attempting to remove namespaces' do
365
463
  @namespaced.multi do
366
464
  @future = @namespaced.keys('*')
@@ -377,6 +475,26 @@ describe "redis" do
377
475
  expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
378
476
  end
379
477
 
478
+ it "should utilize connection_pool while adding namespace to pipelined blocks" do
479
+ memo = @namespaced
480
+ connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
481
+ @namespaced = Redis::Namespace.new(:ns, redis: connection_pool)
482
+
483
+ expect(connection_pool).to receive(:with).and_call_original do |arg|
484
+ expect(arg).to be(an_instance_of(Redis))
485
+ end.at_least(:once)
486
+
487
+ @namespaced.mapped_hmset "foo", {"key" => "value"}
488
+ @namespaced.pipelined do |r|
489
+ r.del "foo"
490
+ r.mapped_hmset "foo", {"key1" => "value1"}
491
+ end
492
+ expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
493
+ expect(@redis.get("foo")).to eq("bar")
494
+
495
+ @namespaced = memo
496
+ end
497
+
380
498
  it "should returned response array from pipelined block" do
381
499
  @namespaced.mset "foo", "bar", "key", "value"
382
500
  result = @namespaced.pipelined do |r|
@@ -386,6 +504,27 @@ describe "redis" do
386
504
  expect(result).to eq(["bar", "value"])
387
505
  end
388
506
 
507
+ it "is thread safe for multi blocks" do
508
+ mon = Monitor.new
509
+ entered = false
510
+ entered_cond = mon.new_cond
511
+
512
+ thread = Thread.new do
513
+ mon.synchronize do
514
+ entered_cond.wait_until { entered }
515
+ @namespaced.multi
516
+ end
517
+ end
518
+
519
+ @namespaced.multi do |transaction|
520
+ entered = true
521
+ mon.synchronize { entered_cond.signal }
522
+ thread.join(0.1)
523
+ transaction.get("foo")
524
+ end
525
+ thread.join
526
+ end
527
+
389
528
  it "should add namespace to strlen" do
390
529
  @namespaced.set("mykey", "123456")
391
530
  expect(@namespaced.strlen("mykey")).to eq(6)
@@ -442,6 +581,45 @@ describe "redis" do
442
581
  expect { @namespaced.unknown('foo') }.to raise_exception NoMethodError
443
582
  end
444
583
 
584
+ describe '#inspect' do
585
+ let(:single_level_names) { %i[first] }
586
+ let(:double_level_names) { %i[first second] }
587
+ let(:triple_level_names) { %i[first second third] }
588
+ let(:namespace_builder) do
589
+ ->(redis, *namespaces) { namespaces.reduce(redis) { |r, n| Redis::Namespace.new(n, redis: r) } }
590
+ end
591
+ let(:regexp_builder) do
592
+ ->(*namespaces) { %r{/#{namespaces.join(':')}>\z} }
593
+ end
594
+
595
+ context 'when one namespace' do
596
+ let(:single_namespaced) { namespace_builder.call(@redis, *single_level_names) }
597
+ let(:regexp) { regexp_builder.call(*single_level_names) }
598
+
599
+ it 'should have correct ending of inspect string' do
600
+ expect(regexp =~ single_namespaced.inspect).not_to be(nil)
601
+ end
602
+ end
603
+
604
+ context 'when two namespaces' do
605
+ let(:double_namespaced) { namespace_builder.call(@redis, *double_level_names) }
606
+ let(:regexp) { regexp_builder.call(*double_level_names) }
607
+
608
+ it 'should have correct ending of inspect string' do
609
+ expect(regexp =~ double_namespaced.inspect).not_to be(nil)
610
+ end
611
+ end
612
+
613
+ context 'when three namespaces' do
614
+ let(:triple_namespaced) { namespace_builder.call(@redis, *triple_level_names) }
615
+ let(:regexp) { regexp_builder.call(*triple_level_names) }
616
+
617
+ it 'should have correct ending of inspect string' do
618
+ expect(regexp =~ triple_namespaced.inspect).not_to be(nil)
619
+ end
620
+ end
621
+ end
622
+
445
623
  # Redis 2.6 RC reports its version as 2.5.
446
624
  if @redis_version >= Gem::Version.new("2.5.0")
447
625
  describe "redis 2.6 commands" do
@@ -488,6 +666,12 @@ describe "redis" do
488
666
  expect(@redis.ttl("ns:foo")).to satisfy {|v| (0..1).include?(v) }
489
667
  end
490
668
 
669
+ it "should namespace expiretime" do
670
+ @namespaced.set('mykey', 'Hello')
671
+ @namespaced.expireat('mykey', 2000000000)
672
+ expect(@namespaced.expiretime('mykey')).to eq(2000000000)
673
+ end
674
+
491
675
  it "should namespace hincrbyfloat" do
492
676
  @namespaced.hset('mykey', 'field', 10.50)
493
677
  expect(@namespaced.hincrbyfloat('mykey', 'field', 0.1)).to eq(10.6)
@@ -520,6 +704,12 @@ describe "redis" do
520
704
  expect(@namespaced.pexpire('mykey', 1555555555005)).to eq(true)
521
705
  end
522
706
 
707
+ it "should namespace pexpiretime" do
708
+ @namespaced.set('mykey', 'Hello')
709
+ @namespaced.pexpireat('mykey', 2000000000000)
710
+ expect(@namespaced.pexpiretime('mykey')).to eq(2000000000000)
711
+ end
712
+
523
713
  it "should namespace psetex" do
524
714
  expect(@namespaced.psetex('mykey', 10000, 'Hello')).to eq('OK')
525
715
  expect(@namespaced.get('mykey')).to eq('Hello')
@@ -626,7 +816,7 @@ describe "redis" do
626
816
  expect(result).to match_array(namespaced_keys)
627
817
  end
628
818
  end
629
- end if Redis.current.respond_to?(:scan)
819
+ end if Redis.new.respond_to?(:scan)
630
820
 
631
821
  context '#scan_each' do
632
822
  context 'when :match supplied' do
@@ -659,7 +849,7 @@ describe "redis" do
659
849
  end
660
850
  end
661
851
  end
662
- end if Redis.current.respond_to?(:scan_each)
852
+ end if Redis.new.respond_to?(:scan_each)
663
853
  end
664
854
 
665
855
  context 'hash scan methods' do
@@ -687,7 +877,7 @@ describe "redis" do
687
877
  expect(results).to match_array(@redis.hgetall('ns:hsh').to_a)
688
878
  end
689
879
  end
690
- end if Redis.current.respond_to?(:hscan)
880
+ end if Redis.new.respond_to?(:hscan)
691
881
 
692
882
  context '#hscan_each' do
693
883
  context 'when :match supplied' do
@@ -720,7 +910,7 @@ describe "redis" do
720
910
  end
721
911
  end
722
912
  end
723
- end if Redis.current.respond_to?(:hscan_each)
913
+ end if Redis.new.respond_to?(:hscan_each)
724
914
  end
725
915
 
726
916
  context 'set scan methods' do
@@ -748,7 +938,7 @@ describe "redis" do
748
938
  expect(results).to match_array(set)
749
939
  end
750
940
  end
751
- end if Redis.current.respond_to?(:sscan)
941
+ end if Redis.new.respond_to?(:sscan)
752
942
 
753
943
  context '#sscan_each' do
754
944
  context 'when :match supplied' do
@@ -781,7 +971,7 @@ describe "redis" do
781
971
  end
782
972
  end
783
973
  end
784
- end if Redis.current.respond_to?(:sscan_each)
974
+ end if Redis.new.respond_to?(:sscan_each)
785
975
  end
786
976
 
787
977
  context 'zset scan methods' do
@@ -811,7 +1001,7 @@ describe "redis" do
811
1001
  expect(results).to match_array(hash.to_a)
812
1002
  end
813
1003
  end
814
- end if Redis.current.respond_to?(:zscan)
1004
+ end if Redis.new.respond_to?(:zscan)
815
1005
 
816
1006
  context '#zscan_each' do
817
1007
  context 'when :match supplied' do
@@ -844,7 +1034,7 @@ describe "redis" do
844
1034
  end
845
1035
  end
846
1036
  end
847
- end if Redis.current.respond_to?(:zscan_each)
1037
+ end if Redis.new.respond_to?(:zscan_each)
848
1038
  end
849
1039
  end
850
1040
  end
@@ -881,4 +1071,30 @@ describe "redis" do
881
1071
  expect(sub_sub_namespaced.full_namespace).to eql("ns:sub1:sub2")
882
1072
  end
883
1073
  end
1074
+
1075
+ describe :clear do
1076
+ it "warns with helpful output" do
1077
+ expect { @namespaced.clear }.to output(/can run for a very long time/).to_stderr
1078
+ end
1079
+
1080
+ it "should delete all the keys" do
1081
+ @redis.set("foo", "bar")
1082
+ @namespaced.mset("foo1", "bar", "foo2", "bar")
1083
+ capture_stderr { @namespaced.clear }
1084
+
1085
+ expect(@redis.keys).to eq ["foo"]
1086
+ expect(@namespaced.keys).to be_empty
1087
+ end
1088
+
1089
+ it "should delete all the keys in older redis" do
1090
+ allow(@redis).to receive(:info).and_return({ "redis_version" => "2.7.0" })
1091
+
1092
+ @redis.set("foo", "bar")
1093
+ @namespaced.mset("foo1", "bar", "foo2", "bar")
1094
+ capture_stderr { @namespaced.clear }
1095
+
1096
+ expect(@redis.keys).to eq ["foo"]
1097
+ expect(@namespaced.keys).to be_empty
1098
+ end
1099
+ end
884
1100
  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.11.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: 2023-06-08 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.11.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.4.1
134
+ signing_key:
115
135
  specification_version: 4
116
136
  summary: Namespaces Redis commands.
117
137
  test_files: []