redis-objects 1.5.0 → 1.7.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: 1645ef709707def1356b48d10f02ed09c7cffc5e83568dc3b2d14c030fdff3d7
4
- data.tar.gz: f1ef896dfaa12b573631417eaae3039bf16fd0d07ea4019956aa002c24a057f5
3
+ metadata.gz: a34d5bd684e2dfb04c3216b84fc8c0d0ac4b8037919ba6f887bd1930e3eca727
4
+ data.tar.gz: e3b4c9df6c48b594850948dd437cec9acecb9a7622102f71d57a4f17068e7fd8
5
5
  SHA512:
6
- metadata.gz: a9d492b56d5c712d5c33d19ac682fd45eb937ead9dba84d7123346a40aaf88020dd6be82c8697342a130470f8a23bf0d3cc701e6691cf15e685efc1aec64737a
7
- data.tar.gz: c5033f81a4d8c9aba6798aca9bf49cf5613217b6ef388252df92fe099111b4a85c0709e96fc64fcfe2bb0cc54720a536f2b08ea6c49976d44604347d7049b978
6
+ metadata.gz: 99d33225c4326dec14e62b14e2c0735ac688b7c1d7a86f767d0141fe3a49782e2fc41e02c80cf8c16a79e0c570249e4e3f758e86d1c652688566b4392890abd8
7
+ data.tar.gz: d1c6831c0d327fa3f6b2e5e8b3ed45deacdc4373836565084ed16e7492219c7de73e5d79ccc61f0bf28863ad10ef185284c81799369a27fa1cd3f375e426b61c
data/.gitignore CHANGED
@@ -9,3 +9,5 @@ spec/redis.pid
9
9
  dump.rdb
10
10
  Gemfile.lock
11
11
  redis-objects-*.gem
12
+ coverage/
13
+ .DS_Store
data/.travis.yml CHANGED
@@ -5,7 +5,12 @@ before_install:
5
5
  - gem install bundler
6
6
 
7
7
  rvm:
8
- - 2.3.3
9
- - 2.4.0
10
- - 2.5.1
11
- - 2.6.4
8
+ - 2.3.8
9
+ - 2.4.10
10
+ - 2.5.9
11
+ - 2.6.7
12
+ - 2.7.3
13
+ - 3.0.1
14
+
15
+ # For code coverage reports
16
+ script: bundle exec rake
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,30 @@
1
1
  = Changelog for Redis::Objects
2
2
 
3
+ == 1.7.0 (29 Apr 2022)
4
+
5
+ * Bumped version to 1.7.0 to revert redis-rb version lock [Nate Wiger]
6
+
7
+ == 1.6.0 (29 Apr 2022)
8
+
9
+ * Upgrade version to 1.6.0 due to redis-rb changes to Redis.current [Nate Wiger]
10
+
11
+ == 1.5.1 (10 Jul 2021)
12
+
13
+ * Added double-splat for **options to account for Ruby 3.0 [Nate Wiger]
14
+
15
+ * Fix ConnectionPoolProxy Ruby 3.0 compatibility Fix: https://github.com/nateware/redis-objects/pull/258 [Jean byroot Boussier]
16
+
17
+ * Change Redis#exists to Redis#exists? * bump redis version to 4.2 [Alina Hryshchuk]
18
+
19
+ * Local variable `dir` is not in use since 98226b95f35ef455f231692fdb679dfd61200a78 [Akira Matsuda]
20
+
21
+ * Issue 249: when atomic decrbyfloat fails, increment back instead of decrementing again [Slava Samoliuk]
22
+
23
+ * Update documentation to reflect ability to assign values directly [Artin Boghosian]
24
+
25
+ * Allow directly assigning values of lists, hashes and sets [Artin Boghosian]
26
+
27
+
3
28
  == 1.5.0 (18 Sep 2019)
4
29
 
5
30
  * updated README on expireat [Nate Wiger]
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  Redis::Objects - Map Redis types directly to Ruby objects
2
2
  =========================================================
3
3
 
4
- [![Build Status](https://travis-ci.org/nateware/redis-objects.png)](https://travis-ci.org/nateware/redis-objects)
4
+ [![Build Status](https://app.travis-ci.com/nateware/redis-objects.svg?branch=master)](https://travis-ci.com/github/nateware/redis-objects)
5
+ [![Code Coverage](https://codecov.io/gh/nateware/redis-objects/branch/master/graph/badge.svg)](https://codecov.io/gh/nateware/redis-objects)
5
6
  [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MJF7JU5M7F8VL)
6
7
 
7
8
  This is **not** an ORM. People that are wrapping ORM’s around Redis are missing the point.
@@ -144,6 +145,7 @@ Familiar Ruby array operations Just Work (TM):
144
145
  @team.on_base.shift
145
146
  @team.on_base.length # 1
146
147
  @team.on_base.delete('player2')
148
+ @team.on_base = ['player1', 'player2'] # ['player1', 'player2']
147
149
  ~~~
148
150
 
149
151
  Sets work too:
@@ -157,6 +159,15 @@ Sets work too:
157
159
  puts player
158
160
  end
159
161
  player = @team.outfielders.detect{|of| of == 'outfielder2'}
162
+ @team.outfielders = ['outfielder1', 'outfielder3'] # ['outfielder1', 'outfielder3']
163
+ ~~~
164
+
165
+ Hashes work too:
166
+
167
+ ~~~ruby
168
+ @team.pitchers_faced['player1'] = 'pitcher2'
169
+ @team.pitchers_faced['player2'] = 'pitcher1'
170
+ @team.pitchers_faced = { 'player1' => 'pitcher2', 'player2' => 'pitcher1' }
160
171
  ~~~
161
172
 
162
173
  And you can do unions and intersections between objects (kinda cool):
data/lib/redis/counter.rb CHANGED
@@ -102,7 +102,7 @@ class Redis
102
102
  def decrbyfloat(by=1.0, &block)
103
103
  allow_expiration do
104
104
  val = redis.incrbyfloat(key, -by).to_f
105
- block_given? ? rewindable_block(:incrbyfloat, -by, val, &block) : val
105
+ block_given? ? rewindable_block(:incrbyfloat, by, val, &block) : val
106
106
  end
107
107
  end
108
108
 
@@ -112,7 +112,7 @@ class Redis
112
112
  alias_method :to_i, :value
113
113
 
114
114
  def nil?
115
- !redis.exists(key)
115
+ !redis.exists?(key)
116
116
  end
117
117
 
118
118
  ##
@@ -16,7 +16,7 @@ class Redis
16
16
  def sort(options={})
17
17
  return super() if block_given?
18
18
  options[:order] = "asc alpha" if options.keys.count == 0 # compat with Ruby
19
- val = redis.sort(key, options)
19
+ val = redis.sort(key, **options)
20
20
  val.is_a?(Array) ? val.map{|v| unmarshal(v)} : val
21
21
  end
22
22
 
@@ -123,24 +123,20 @@ class Redis
123
123
  hsh
124
124
  end
125
125
 
126
- # Get values in bulk, takes an array of keys as arguments.
126
+ # Get values in bulk, takes an array of fields as arguments.
127
127
  # Values are returned in a collection in the same order than their keys in *keys Redis: HMGET
128
- def bulk_values(*keys)
129
- get_keys = *keys.flatten
130
- return [] if get_keys.empty?
131
- res = redis.hmget(key, get_keys)
132
- get_keys.inject([]){|collection, k| collection << unmarshal(res.shift, options[:marshal_keys][k])}
128
+ def bulk_values(*fields)
129
+ get_fields = *fields.flatten
130
+ return [] if get_fields.empty?
131
+ res = redis.hmget(key, get_fields)
132
+ get_fields.collect{|k| unmarshal(res.shift, options[:marshal_keys][k])}
133
133
  end
134
134
 
135
135
  # Increment value by integer at field. Redis: HINCRBY
136
136
  def incrby(field, by=1)
137
137
  allow_expiration do
138
138
  ret = redis.hincrby(key, field, by)
139
- unless ret.is_a? Array
140
- ret.to_i
141
- else
142
- nil
143
- end
139
+ ret.to_i
144
140
  end
145
141
  end
146
142
  alias_method :incr, :incrby
@@ -155,11 +151,7 @@ class Redis
155
151
  def incrbyfloat(field, by=1.0)
156
152
  allow_expiration do
157
153
  ret = redis.hincrbyfloat(key, field, by)
158
- unless ret.is_a? Array
159
- ret.to_f
160
- else
161
- nil
162
- end
154
+ ret.to_f
163
155
  end
164
156
  end
165
157
 
@@ -2,10 +2,14 @@ class Redis
2
2
  module Helpers
3
3
  # These are core commands that all types share (rename, etc)
4
4
  module CoreCommands
5
- def exists?
5
+ def exists
6
6
  redis.exists key
7
7
  end
8
8
 
9
+ def exists?
10
+ redis.exists? key
11
+ end
12
+
9
13
  # Delete key. Redis: DEL
10
14
  def delete
11
15
  redis.del key
@@ -9,6 +9,7 @@ class Redis
9
9
  def method_missing(name, *args, &block)
10
10
  @pool.with { |x| x.send(name, *args, &block) }
11
11
  end
12
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
12
13
 
13
14
  def respond_to_missing?(name, include_all = false)
14
15
  @pool.with { |x| x.respond_to?(name, include_all) }
@@ -26,6 +26,15 @@ class Redis
26
26
  )
27
27
  )
28
28
  end
29
+
30
+ define_method(:"#{name}=") do |values|
31
+ hash_key = public_send(name)
32
+
33
+ redis.pipelined do
34
+ hash_key.clear
35
+ hash_key.bulk_set(values)
36
+ end
37
+ end
29
38
  end
30
39
 
31
40
  if options[:global]
@@ -26,6 +26,15 @@ class Redis
26
26
  )
27
27
  )
28
28
  end
29
+
30
+ define_method(:"#{name}=") do |values|
31
+ list = public_send(name)
32
+
33
+ redis.pipelined do
34
+ list.clear
35
+ list.push(*values)
36
+ end
37
+ end
29
38
  end
30
39
 
31
40
  if options[:global]
@@ -26,6 +26,15 @@ class Redis
26
26
  )
27
27
  )
28
28
  end
29
+
30
+ define_method(:"#{name}=") do |values|
31
+ set = public_send(name)
32
+
33
+ redis.pipelined do
34
+ set.clear
35
+ set.merge(*values)
36
+ end
37
+ end
29
38
  end
30
39
 
31
40
  if options[:global]
@@ -1,5 +1,5 @@
1
1
  class Redis
2
2
  module Objects
3
- VERSION = "1.5.0"
3
+ VERSION = "1.7.0"
4
4
  end
5
5
  end
data/lib/redis/objects.rb CHANGED
@@ -46,8 +46,6 @@ class Redis
46
46
  #
47
47
  #
48
48
  module Objects
49
- dir = File.expand_path(__FILE__.sub(/\.rb$/,''))
50
-
51
49
  autoload :Counters, 'redis/objects/counters'
52
50
  autoload :Lists, 'redis/objects/lists'
53
51
  autoload :Locks, 'redis/objects/locks'
@@ -117,7 +117,7 @@ class Redis
117
117
  options[:offset] || options[:limit] || options[:count]
118
118
  args[:with_scores] = true if options[:withscores] || options[:with_scores]
119
119
 
120
- redis.zrangebyscore(key, min, max, args).map{|v| unmarshal(v) }
120
+ redis.zrangebyscore(key, min, max, **args).map{|v| unmarshal(v) }
121
121
  end
122
122
 
123
123
  # Returns all the elements in the sorted set at key with a score between max and min
@@ -133,7 +133,7 @@ class Redis
133
133
  options[:offset] || options[:limit] || options[:count]
134
134
  args[:with_scores] = true if options[:withscores] || options[:with_scores]
135
135
 
136
- redis.zrevrangebyscore(key, max, min, args).map{|v| unmarshal(v) }
136
+ redis.zrevrangebyscore(key, max, min, **args).map{|v| unmarshal(v) }
137
137
  end
138
138
 
139
139
  # Remove all elements in the sorted set at key with rank between start and end. Start and end are
@@ -222,7 +222,7 @@ class Redis
222
222
  def interstore(name, *sets)
223
223
  allow_expiration do
224
224
  opts = sets.last.is_a?(Hash) ? sets.pop : {}
225
- redis.zinterstore(key_from_object(name), keys_from_objects([self] + sets), opts)
225
+ redis.zinterstore(key_from_object(name), keys_from_objects([self] + sets), **opts)
226
226
  end
227
227
  end
228
228
 
@@ -258,7 +258,7 @@ class Redis
258
258
  def unionstore(name, *sets)
259
259
  allow_expiration do
260
260
  opts = sets.last.is_a?(Hash) ? sets.pop : {}
261
- redis.zunionstore(key_from_object(name), keys_from_objects([self] + sets), opts)
261
+ redis.zunionstore(key_from_object(name), keys_from_objects([self] + sets), **opts)
262
262
  end
263
263
  end
264
264
 
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  # Only fix this one version or else tests break
22
- spec.add_dependency "redis", "~> 4.0"
22
+ spec.add_dependency "redis"
23
23
 
24
24
  # Ignore gemspec warnings on these. Trying to fix them to versions breaks TravisCI
25
25
  spec.add_development_dependency "bundler"
@@ -29,6 +29,10 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  # Compatibility testing
31
31
  spec.add_development_dependency "redis-namespace"
32
+ spec.add_development_dependency "activesupport"
32
33
  spec.add_development_dependency "activerecord"
33
34
  spec.add_development_dependency "sqlite3"
35
+
36
+ # Code coverage
37
+ spec.add_development_dependency "simplecov-cobertura"
34
38
  end
@@ -4,7 +4,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
4
  # tests whether autoload functionality works correctly; had issues previously
5
5
 
6
6
  require 'redis/objects'
7
- # $redis used automatically
7
+ Redis::Objects.redis = REDIS_HANDLE
8
8
 
9
9
  describe 'Redis::Objects' do
10
10
  it "should autoload everything" do
@@ -5,6 +5,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
5
5
 
6
6
  require 'redis/objects'
7
7
  require 'connection_pool'
8
+ Redis::Objects.redis = REDIS_HANDLE
8
9
 
9
10
  BAD_REDIS = "totally bad bogus redis handle"
10
11
 
@@ -95,13 +96,13 @@ describe 'Connection tests' do
95
96
  end
96
97
 
97
98
  it "should support local handles with a vanilla redis connection" do
98
- Redis.current = nil # reset from other tests
99
+ # Redis.current = nil # reset from other tests
99
100
  Redis::Objects.redis = nil
100
101
  @redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
101
102
 
102
103
  # Redis.current is lazily auto-populated to touch 6379
103
104
  # This why we choose the weird 9212 port to avoid
104
- Redis.current.inspect.should == Redis.new.inspect
105
+ # Redis.current.inspect.should == Redis.new.inspect
105
106
  Redis::Objects.redis.inspect.should == Redis.new.inspect
106
107
 
107
108
  v = Redis::Value.new('conn/value', @redis_handle)
@@ -140,13 +141,13 @@ describe 'Connection tests' do
140
141
  end
141
142
 
142
143
  it "should support local handles with a connection_pool" do
143
- Redis.current = nil # reset from other tests
144
+ # Redis.current = nil # reset from other tests
144
145
  Redis::Objects.redis = nil
145
146
  @redis_handle = ConnectionPool.new { Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) }
146
147
 
147
148
  # Redis.current is lazily auto-populated to touch 6379
148
149
  # This why we choose the weird 9212 port to avoid
149
- Redis.current.inspect.should == Redis.new.inspect
150
+ # Redis.current.inspect.should == Redis.new.inspect
150
151
  Redis::Objects.redis.inspect.should == Redis.new.inspect
151
152
 
152
153
  v = Redis::Value.new('conn/value', @redis_handle)
@@ -184,24 +185,19 @@ describe 'Connection tests' do
184
185
  c.decr(1)
185
186
  end
186
187
 
187
- it "should support Redis.current" do
188
- Redis.current = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
189
-
190
- Redis::Value.new('conn/value').should == 'yay'
191
- Redis::HashKey.new('conn/hash').keys.should == ['k']
192
- Redis::List.new('conn/list').sort.should == ['3', '4', '5']
193
- Redis::Set.new('conn/set').sort.should == ['5', '6', '7']
194
- Redis::SortedSet.new('conn/zset').should == ['d', 'b', 'a', 'c']
195
- Redis::Counter.new('conn/counter').should == 2
188
+ it "should properly support fallback handle variables" do
189
+ # Redis.current is lazily auto-populated to touch 6379
190
+ # This why we choose the weird 9212 port to avoid
191
+ old_redis = $redis
192
+ $redis = BAD_REDIS
193
+ Redis::Objects.redis.should == BAD_REDIS
194
+ $redis = old_redis
196
195
  end
197
196
 
198
197
  it "should support Redis::Objects.redis= with a connection_pool" do
198
+ # reset redis
199
199
  @redis_handle = ConnectionPool.new { Redis.new(:host => REDIS_HOST, :port => REDIS_PORT) }
200
-
201
- # Redis.current is lazily auto-populated to touch 6379
202
- # This why we choose the weird 9212 port to avoid
203
- Redis.current = BAD_REDIS
204
- Redis::Objects.redis.should == BAD_REDIS
200
+ Redis::Objects.redis = @redis_handle
205
201
 
206
202
  # This set of tests sucks, it fucks up the per-data-type handles
207
203
  # because Redis.current is then set to a BS value, and the lazy
@@ -229,13 +225,8 @@ describe 'Connection tests' do
229
225
 
230
226
  it "should support Redis::Objects.redis= with a vanilla redis connection" do
231
227
  # reset redis
232
- Redis::Objects.redis = nil
233
228
  @redis_handle = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
234
-
235
- # Redis.current is lazily auto-populated to touch 6379
236
- # This why we choose the weird 9212 port to avoid
237
- Redis.current = BAD_REDIS
238
- Redis::Objects.redis.should == BAD_REDIS
229
+ Redis::Objects.redis = @redis_handle
239
230
 
240
231
  # This set of tests sucks, it fucks up the per-data-type handles
241
232
  # because Redis.current is then set to a BS value, and the lazy
@@ -260,7 +251,7 @@ describe 'Connection tests' do
260
251
  Redis::Counter.new('conn/counter').should == 2
261
252
 
262
253
  # Fix for future tests
263
- Redis.current = @redis_handle
254
+ # Redis.current = @redis_handle
264
255
  end
265
256
 
266
257
  it "should support pipelined changes" do
@@ -2,6 +2,7 @@
2
2
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
 
4
4
  require 'redis/objects'
5
+ Redis::Objects.redis = REDIS_HANDLE
5
6
 
6
7
  class CustomSerializer
7
8
  class << self
@@ -2,6 +2,7 @@
2
2
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
 
4
4
  require 'redis/objects'
5
+ Redis::Objects.redis = REDIS_HANDLE
5
6
 
6
7
  describe Redis::Value do
7
8
  before do
@@ -19,6 +20,21 @@ describe Redis::Value do
19
20
  @value.value.should == false
20
21
  end
21
22
 
23
+ it "should handle simple values" do
24
+ @value.should == nil
25
+ @value.value = 'Trevor Hoffman'
26
+ @value.should == 'Trevor Hoffman'
27
+ @value.get.should == 'Trevor Hoffman'
28
+ @value.exists?.should == true
29
+ @value.exists.should == 1
30
+ @value.del.should == 1
31
+ @value.should.be.nil
32
+ @value.exists?.should == false
33
+ @value.exists.should == 0
34
+ @value.value = 42
35
+ @value.value.should == '42'
36
+ end
37
+
22
38
  it "should compress non marshaled values" do
23
39
  @value = Redis::Value.new('spec/value', compress: true)
24
40
  @value.value = 'Trevor Hoffman'
@@ -39,17 +55,8 @@ describe Redis::Value do
39
55
  @value.value.should == nil
40
56
  @value.value = ''
41
57
  @value.value.should == ''
42
- end
43
-
44
- it "should handle simple values" do
45
- @value.should == nil
46
- @value.value = 'Trevor Hoffman'
47
- @value.should == 'Trevor Hoffman'
48
- @value.get.should == 'Trevor Hoffman'
49
- @value.del.should == 1
50
- @value.should.be.nil
51
- @value.value = 42
52
- @value.value.should == '42'
58
+ @value.delete
59
+ @value.value.should.be.nil
53
60
  end
54
61
 
55
62
  it "should handle complex marshaled values" do
@@ -88,7 +95,7 @@ describe Redis::Value do
88
95
  marshalled_string = Marshal.dump({json: 'marshal'})
89
96
  @value = Redis::Value.new('spec/marshal', :default => marshalled_string, :marshal => true)
90
97
  @value.value.should == marshalled_string
91
- @value.clear
98
+ @value.delete
92
99
  end
93
100
 
94
101
  it "should support renaming values" do
@@ -499,23 +506,56 @@ describe Redis::Counter do
499
506
  @counter.incrbyfloat 2.0e2
500
507
  @counter.to_f.should == 5214.31
501
508
  @counter.clear
509
+ @counter.should.be.nil
502
510
  end
503
511
 
504
512
  it "should support an atomic block" do
505
513
  @counter = Redis::Counter.new("spec/block_counter")
506
514
  @counter.should == 0
507
515
  @counter.increment(1)
508
- # The block is never executed.
509
- @updated =
510
- @counter.increment(1) do |updated|
511
- if updated == 2
512
- 'yep'
513
- else
514
- raise("test failed")
515
- end
516
- end
516
+
517
+ # successfully increments
518
+ @updated = @counter.increment(1) { |updated| updated == 2 ? 'yep' : nil }
519
+ @updated.should == 'yep'
520
+ @counter.should == 2
521
+
522
+ # fails to increment
523
+ @updated = @counter.increment(1) { |updated| updated == 2 ? 'yep' : nil }
524
+ @updated.should == nil
525
+ @counter.should == 2
526
+
527
+ # successfully increments by float
528
+ @updated = @counter.incrbyfloat(1.5) { |updated| updated == 3.5 ? 'yep' : nil }
529
+ @updated.should == 'yep'
530
+ @counter.should == 3.5
531
+
532
+ # fails to increment by float
533
+ @updated = @counter.incrbyfloat(2.5) { |updated| updated == 5 ? 'yep' : nil }
534
+ @updated.should == nil
535
+ @counter.should == 3.5
536
+
537
+ # fails to decrement by float
538
+ @updated = @counter.decrbyfloat(0.5) { |updated| updated == 5 ? 'yep' : nil }
539
+ @updated.should == nil
540
+ @counter.should == 3.5
541
+
542
+ # successfully decrements by float
543
+ @updated = @counter.decrbyfloat(0.5) { |updated| updated == 3 ? 'yep' : nil }
544
+ @updated.should == 'yep'
545
+ @counter.should == 3
546
+
547
+ # fails to decrement
548
+ @updated = @counter.decrement(1) { |updated| updated == 3 ? 'yep' : nil }
549
+ @updated.should == nil
550
+ @counter.should == 3
551
+
552
+ # successfully decrements
553
+ @updated = @counter.decrement(1) { |updated| updated == 2 ? 'yep' : nil }
517
554
  @updated.should == 'yep'
518
555
  @counter.should == 2
556
+
557
+ @counter.value = nil
558
+ @counter.should == 0
519
559
  end
520
560
 
521
561
  it "should support #to_json" do
@@ -615,7 +655,7 @@ describe Redis::Lock do
615
655
  REDIS_HANDLE.get("test_lock").should.be.nil
616
656
  end
617
657
 
618
- it "should not let non-expired locks be gettable" do
658
+ it "should not let blocks execute if they timeout" do
619
659
  expiry = 15
620
660
  lock = Redis::Lock.new(:test_lock, :expiration => expiry, :timeout => 0.1)
621
661
 
@@ -640,19 +680,68 @@ describe Redis::Lock do
640
680
  REDIS_HANDLE.get("test_lock").should.not.be.nil
641
681
  end
642
682
 
683
+ it "should handle a timeout of 0" do
684
+ expiry = 15
685
+ lock = Redis::Lock.new(:test_lock, :timeout => 0)
686
+
687
+ # create a fake lock
688
+ REDIS_HANDLE.set("test_lock", (Time.now + expiry).to_f)
689
+
690
+ gotit = false
691
+ error = nil
692
+ begin
693
+ lock.lock do
694
+ gotit = true
695
+ end
696
+ rescue => error
697
+ end
698
+
699
+ error.should.be.kind_of(Redis::Lock::LockTimeout)
700
+
701
+ # should not have the lock
702
+ gotit.should.not.be.true
703
+
704
+ # lock value should still be set
705
+ REDIS_HANDLE.get("test_lock").should.not.be.nil
706
+ end
707
+
708
+ it "should properly keep the lock across threads" do
709
+ lock = Redis::Lock.new(:test_lock0)
710
+
711
+ t1 = Thread.new do
712
+ lock.lock do
713
+ lock.exists?.should.be.true if RUNNING_LOCALLY
714
+ REDIS_HANDLE.exists?("test_lock0").should.be.true if RUNNING_LOCALLY
715
+ sleep 1.0 # hang onto the lock across other thread
716
+ end
717
+ end
718
+
719
+ t2 = Thread.new do
720
+ # check for the lock from another thread
721
+ lock.exists?.should.be.true if RUNNING_LOCALLY
722
+ REDIS_HANDLE.exists?("test_lock0").should.be.true if RUNNING_LOCALLY
723
+ end
724
+
725
+ t1.join
726
+ t2.join
727
+
728
+ # lock value should not be set since the lock was held for more than the expiry
729
+ lock.exists?.should.be.false
730
+ REDIS_HANDLE.exists?("test_lock0").should.be.false
731
+ end
732
+
643
733
  it "Redis should remove the key if lock is held past expiration" do
644
- lock = Redis::Lock.new(:test_lock, :expiration => 0.1)
734
+ lock = Redis::Lock.new(:test_lock1, :expiration => 0.1)
645
735
 
646
736
  lock.lock do
647
- REDIS_HANDLE.exists("test_lock").should.be.true
648
- sleep 0.3
649
- # technically undefined behavior because we don't have a BG thread
650
- # running and deleting lock keys - that is only triggered on block exit
651
- #REDIS_HANDLE.exists("test_lock").should.be.false
737
+ lock.exists?.should.be.true
738
+ REDIS_HANDLE.exists?("test_lock1").should.be.true
739
+ sleep 1.0 # hang onto the lock > expiry
652
740
  end
653
741
 
654
742
  # lock value should not be set since the lock was held for more than the expiry
655
- REDIS_HANDLE.exists("test_lock").should.be.false
743
+ lock.exists?.should.be.false
744
+ REDIS_HANDLE.exists?("test_lock1").should.be.false
656
745
  end
657
746
 
658
747
 
@@ -660,9 +749,9 @@ describe Redis::Lock do
660
749
  lock = Redis::Lock.new(:test_lock2, :expiration => 0.1)
661
750
 
662
751
  lock.lock do
663
- REDIS_HANDLE.exists("test_lock2").should.be.true
664
- sleep 0.3 # expired, key is deleted
665
- REDIS_HANDLE.exists("test_lock2").should.be.false
752
+ REDIS_HANDLE.exists?("test_lock2").should.be.true
753
+ sleep 1.0 # expired, key is deleted
754
+ REDIS_HANDLE.exists?("test_lock2").should.be.false
666
755
  REDIS_HANDLE.set("test_lock2", "foo") # this is no longer a lock key, name is a coincidence
667
756
  end
668
757
 
@@ -670,17 +759,17 @@ describe Redis::Lock do
670
759
  end
671
760
 
672
761
  it "should manually delete the key if finished before expiration" do
673
- lock = Redis::Lock.new(:test_lock3, :expiration => 0.5)
762
+ lock = Redis::Lock.new(:test_lock3, :expiration => 10.0)
674
763
 
675
764
  lock.lock do
676
- REDIS_HANDLE.exists("test_lock3").should.be.true
765
+ REDIS_HANDLE.exists?("test_lock3").should.be.true
677
766
  sleep 0.1
678
- REDIS_HANDLE.exists("test_lock3").should.be.true
767
+ REDIS_HANDLE.exists?("test_lock3").should.be.true
679
768
  end
680
769
 
681
- # should delete the key because the lock block is done, regardless of time
770
+ # should delete the key because the lock block is done, regardless of expiry
682
771
  # for some strange reason, I have seen this test fail randomly, which is worrisome.
683
- #REDIS_HANDLE.exists("test_lock3").should.be.false
772
+ REDIS_HANDLE.exists?("test_lock3").should.be.false
684
773
  end
685
774
 
686
775
 
@@ -900,6 +989,13 @@ describe Redis::HashKey do
900
989
  hsh['foo'].should == 'bar'
901
990
  end
902
991
 
992
+ it "should return multiple items via bulk_values" do
993
+ @hash['taco'] = 42
994
+ @hash['burrito'] = 99
995
+ res = @hash.bulk_values('taco', 'burrito')
996
+ res.should == ['42', '99'] # hashes don't convert
997
+ end
998
+
903
999
  it "should increment field" do
904
1000
  @hash.incr('counter')
905
1001
  @hash.incr('counter')
@@ -1007,7 +1103,9 @@ describe Redis::Set do
1007
1103
 
1008
1104
  it "should handle sets of simple values" do
1009
1105
  @set.should.be.empty
1010
- @set << 'a' << 'a' << 'a'
1106
+ @set << 'a'
1107
+ @set.randmember.should == 'a'
1108
+ @set << 'a' << 'a'
1011
1109
  @set.should == ['a']
1012
1110
  @set.to_s.should == 'a'
1013
1111
  @set.get.should == ['a']
@@ -1048,11 +1146,13 @@ describe Redis::Set do
1048
1146
  coll = @set.select{|st| st == 'c'}
1049
1147
  coll.should == ['c']
1050
1148
  @set.sort.should == ['a','b','c']
1149
+ @set.randmember.should.not == 'd'
1051
1150
  @set.delete_if{|m| m == 'c'}
1052
1151
  @set.sort.should == ['a','b']
1053
1152
 
1054
1153
  @set << nil
1055
1154
  @set.include?("").should.be.true
1155
+ @set.sort.should == ['','a','b']
1056
1156
  end
1057
1157
 
1058
1158
  it "should handle empty array adds" do
@@ -1168,6 +1268,12 @@ describe Redis::Set do
1168
1268
  @set_1.value.should == ['a']
1169
1269
  end
1170
1270
 
1271
+ it "should support moving between sets" do
1272
+ @set_1 << 'X' << 'Y' << 'Z'
1273
+ @set_1.move('X', @set_2)
1274
+ @set_2.should == ['X']
1275
+ end
1276
+
1171
1277
  it "should respond to #to_json" do
1172
1278
  @set_1 << 'a'
1173
1279
  JSON.parse(@set_1.to_json)['value'].should == ['a']
@@ -1267,6 +1373,9 @@ describe Redis::SortedSet do
1267
1373
  @set.to_s.should == 'a, b'
1268
1374
  @set.should == ['a','b']
1269
1375
  @set.members.should == ['a','b']
1376
+ @set.member?('a').should == true
1377
+ @set.member?('b').should == true
1378
+ @set.member?('c').should == false
1270
1379
  @set['d'] = 0
1271
1380
 
1272
1381
  @set.rangebyscore(0, 4).should == ['d','a']
@@ -1316,6 +1425,10 @@ describe Redis::SortedSet do
1316
1425
 
1317
1426
  @set.delete_if{|m| m == 'b'}
1318
1427
  @set.size.should == 3
1428
+
1429
+ # this is destructive so must come last
1430
+ res = @set.remrangebyrank(0, 2)
1431
+ res.should == 3
1319
1432
  end
1320
1433
 
1321
1434
  it "should handle inserting multiple values at once" do
@@ -2,6 +2,7 @@
2
2
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
 
4
4
  require 'redis/objects'
5
+ Redis::Objects.redis = REDIS_HANDLE
5
6
 
6
7
  class Roster
7
8
  include Redis::Objects
@@ -14,11 +15,15 @@ class Roster
14
15
  list :player_stats, :marshal => true
15
16
  set :outfielders, :marshal => true
16
17
  sorted_set :rank
18
+ lock :per_field
17
19
 
18
20
  # global class counters
19
21
  counter :total_players_online, :global => true
20
22
  set :all_players_online, :global => true
21
23
  value :last_player, :global => true
24
+ lock :nasty_global_mutex, :global => true # remember it appends "_lock"
25
+ sorted_set :global_player_leaderboard, :global => true
26
+ hash_key :global_player_online_status, :global => true
22
27
 
23
28
  # custom keys
24
29
  counter :player_totals, :key => 'players/#{username}/total'
@@ -162,6 +167,14 @@ describe Redis::Objects do
162
167
  @roster.redis.get(k).should == '1'
163
168
  end
164
169
 
170
+ it "should be able to directly assign value of hash" do
171
+ @roster.contact_information['John_Name'] = 'John Doe'
172
+ @roster.contact_information = { 'John_Phone' => '12345678', 'John_Address' => '321 LANE' }
173
+ @roster.contact_information['John_Phone'].should == '12345678'
174
+ @roster.contact_information['John_Address'].should == '321 LANE'
175
+ @roster.contact_information['John_Name'].should.be.nil
176
+ end
177
+
165
178
  it "should be able to get/set contact info" do
166
179
  @roster.contact_information['John_Phone'] = '123415352'
167
180
  @roster.contact_information['John_Address'] = '123 LANE'
@@ -202,6 +215,21 @@ describe Redis::Objects do
202
215
  end
203
216
  end
204
217
 
218
+ it "should handle obtaining / clearing locks" do
219
+ # class-level calls for Admin reasons
220
+ # this is a really weird API and I wonder if anyone actually uses it
221
+ id = 88
222
+ roster = Roster.new(id)
223
+ roster.per_field_lock.exists?.should == false
224
+ Roster.obtain_lock(:per_field, id) do
225
+ roster.per_field_lock.exists?.should == true
226
+ end
227
+ roster.per_field_lock.exists?.should == false
228
+
229
+ Roster.clear_lock(:per_field, id)
230
+ roster.per_field_lock.exists?.should == false
231
+ end
232
+
205
233
  it "should support increment/decrement of counters" do
206
234
  @roster.available_slots.key.should == 'roster:1:available_slots'
207
235
  @roster.available_slots.should == 10
@@ -265,6 +293,26 @@ describe Redis::Objects do
265
293
  Roster.get_counter(:total_players_online).should == 111
266
294
  end
267
295
 
296
+ it "should support class-level manipulation of global objects" do
297
+ Roster.nasty_global_mutex_lock.exists?.should == false
298
+ Roster.nasty_global_mutex_lock do
299
+ Roster.nasty_global_mutex_lock.exists?.should == true
300
+ end
301
+ Roster.nasty_global_mutex_lock.exists?.should == false
302
+
303
+ Roster.global_player_leaderboard.exists?.should == false
304
+ Roster.global_player_leaderboard.add('nate', 22)
305
+ Roster.global_player_leaderboard.add('jim', 11)
306
+ Roster.global_player_leaderboard.rank('nate').should == 1 # 0-based
307
+
308
+ Roster.global_player_online_status.exists?.should == false
309
+ Roster.global_player_online_status['nate'] = 'online'
310
+ Roster.global_player_online_status['jeff'] = 'offline'
311
+ Roster.global_player_online_status['nate'].should == 'online'
312
+ Roster.global_player_online_status['jeff'].should == 'offline'
313
+ Roster.global_player_online_status['bobby'].should.be.nil
314
+ end
315
+
268
316
  it "should take an atomic block for increment/decrement" do
269
317
  a = false
270
318
  @roster.available_slots.should == 10
@@ -423,6 +471,13 @@ describe Redis::Objects do
423
471
  end
424
472
  error.should.be.kind_of(NoMethodError)
425
473
 
474
+ error = nil
475
+ begin
476
+ Roster.increment_counter(:available_slots, nil)
477
+ rescue => error
478
+ end
479
+ error.should.be.kind_of(Redis::Objects::MissingID)
480
+
426
481
  error = nil
427
482
  begin
428
483
  Roster.obtain_lock(:badness, 2){}
@@ -485,6 +540,12 @@ describe Redis::Objects do
485
540
  @roster.starting_pitcher.should.be.nil
486
541
  end
487
542
 
543
+ it "should be able to directly assign value of list" do
544
+ @roster.player_stats << 'c'
545
+ @roster.player_stats = ['a', 'b']
546
+ @roster.player_stats.get.should == ['a', 'b']
547
+ end
548
+
488
549
  it "should handle lists of simple values" do
489
550
  @roster.player_stats.should.be.empty
490
551
  @roster.player_stats << 'a'
@@ -559,6 +620,14 @@ describe Redis::Objects do
559
620
  @roster.player_stats.get.should == ['a','c','f','j','h','i','a']
560
621
  end
561
622
 
623
+ it "should be able to directly assign values of set" do
624
+ @roster.outfielders << 'c'
625
+ @roster.outfielders = ['a', 'b']
626
+ @roster.outfielders.member?('a').should.be.true
627
+ @roster.outfielders.member?('b').should.be.true
628
+ @roster.outfielders.member?('c').should.be.false
629
+ end
630
+
562
631
  it "should handle sets of simple values" do
563
632
  @roster.outfielders.should.be.empty
564
633
  @roster.outfielders << 'a' << 'a' << 'a'
@@ -1020,4 +1089,10 @@ describe Redis::Objects do
1020
1089
  @roster.redis_instance_keys.include?('roster:1:player_stats').should == true
1021
1090
  @roster.redis_instance_keys.include?('players:all_stats').should == false
1022
1091
  end
1092
+
1093
+ it "should handle per-class connection handles" do
1094
+ redis = Roster.redis
1095
+ Roster.redis = redis
1096
+ Roster.redis.should == redis
1097
+ end
1023
1098
  end
data/spec/spec_helper.rb CHANGED
@@ -9,6 +9,21 @@ if $0 =~ /\brspec$/
9
9
  raise "\n===\nThese tests are in bacon, not rspec. Try: bacon #{ARGV * ' '}\n===\n"
10
10
  end
11
11
 
12
+ # For the incompatible change from redis.rb
13
+ Redis.exists_returns_integer = true
14
+
15
+ # Avoid phantom remote test failures
16
+ RUNNING_LOCALLY = !ENV['TRAVIS']
17
+
18
+ # Code coverage reports
19
+ require 'simplecov'
20
+ SimpleCov.start
21
+
22
+ require 'simplecov-cobertura'
23
+ SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter
24
+
25
+ #require "active_support/xml_mini"
26
+ require "active_support"
12
27
  require "active_support/testing/time_helpers"
13
28
  include ActiveSupport::Testing::TimeHelpers
14
29
 
@@ -69,8 +84,6 @@ end
69
84
 
70
85
  # Grab a global handle
71
86
  REDIS_HANDLE = Redis.new(:host => REDIS_HOST, :port => REDIS_PORT)
72
- #$redis = REDIS_HANDLE
73
- Redis.current = REDIS_HANDLE
74
87
 
75
88
  SORT_ORDER = {:order => 'desc alpha'}
76
89
  SORT_LIMIT = {:limit => [2, 2]}
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-objects
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Wiger
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-18 00:00:00.000000000 Z
11
+ date: 2022-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.0'
19
+ version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '4.0'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activesupport
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: activerecord
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +136,20 @@ dependencies:
122
136
  - - ">="
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov-cobertura
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
125
153
  description: Map Redis types directly to Ruby objects. Works with any class or ORM.
126
154
  email:
127
155
  - nwiger@gmail.com
@@ -171,7 +199,7 @@ homepage: http://github.com/nateware/redis-objects
171
199
  licenses:
172
200
  - Artistic-2.0
173
201
  metadata: {}
174
- post_install_message:
202
+ post_install_message:
175
203
  rdoc_options: []
176
204
  require_paths:
177
205
  - lib
@@ -186,8 +214,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
214
  - !ruby/object:Gem::Version
187
215
  version: '0'
188
216
  requirements: []
189
- rubygems_version: 3.0.3
190
- signing_key:
217
+ rubygems_version: 3.3.7
218
+ signing_key:
191
219
  specification_version: 4
192
220
  summary: Map Redis types directly to Ruby objects
193
221
  test_files: