redis-objects 1.3.0 → 1.4.3
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 +5 -5
- data/.travis.yml +1 -3
- data/CHANGELOG.rdoc +62 -0
- data/README.md +21 -0
- data/lib/redis/base_object.rb +12 -1
- data/lib/redis/counter.rb +7 -10
- data/lib/redis/hash_key.rb +5 -5
- data/lib/redis/list.rb +4 -2
- data/lib/redis/lock.rb +23 -20
- data/lib/redis/objects/counters.rb +3 -2
- data/lib/redis/objects/hashes.rb +3 -2
- data/lib/redis/objects/lists.rb +3 -2
- data/lib/redis/objects/locks.rb +3 -2
- data/lib/redis/objects/sets.rb +3 -2
- data/lib/redis/objects/sorted_sets.rb +3 -2
- data/lib/redis/objects/values.rb +3 -2
- data/lib/redis/objects/version.rb +1 -1
- data/lib/redis/objects.rb +2 -2
- data/lib/redis/set.rb +1 -1
- data/lib/redis/sorted_set.rb +24 -28
- data/lib/redis/value.rb +25 -0
- data/redis-objects.gemspec +2 -1
- data/spec/redis_objects_active_record_spec.rb +32 -16
- data/spec/redis_objects_instance_spec.rb +97 -27
- data/spec/redis_objects_model_spec.rb +15 -0
- data/spec/spec_helper.rb +3 -0
- metadata +19 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6a9f86f294c2283b6ceae1730dfe6f1d1c3665ac9733cd107a4aa522d8a66bda
|
|
4
|
+
data.tar.gz: 50d631298aff5bcd5b15179c1a700a56a512c4a096cedad4c3f716999dcfebea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48b37a11d5a2a5844863f2a7c559b5e38a6a96eab5d8f5393d0b688f8ffada7f8e480bb12d93869ca7a82d1fbb0fd311fc85a5f72635a4749ee771fefe834abc
|
|
7
|
+
data.tar.gz: b09b794badff0249c468b0cc33b2daa24e87ab084cc5a778eb3415894bdad6e90070f7e3da4d417c16ec867017d4c15e5d06c0ca163560e5c4513297e3698241
|
data/.travis.yml
CHANGED
data/CHANGELOG.rdoc
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
1
|
= Changelog for Redis::Objects
|
|
2
2
|
|
|
3
|
+
== 1.4.3 (7 Oct 2018)
|
|
4
|
+
|
|
5
|
+
* Merge pull request #235 from johnc219/fix/end-time-expiration Add expiration in seconds to obtain end_time [Nate Wiger]
|
|
6
|
+
|
|
7
|
+
* Merge pull request #223 from artinboghosian/compress-redis-value Allow compression of value stored in Redis::Value to save memory on R… [Nate Wiger]
|
|
8
|
+
|
|
9
|
+
* Merge pull request #224 from artinboghosian/sorted-set-missing-operations Fix set operation methods on SortedSets [Nate Wiger]
|
|
10
|
+
|
|
11
|
+
* Merge pull request #233 from tmattia/master Add SortedSet#count alias so it's consistent with Set#count [Nate Wiger]
|
|
12
|
+
|
|
13
|
+
* Merge pull request #236 from nateware/revert-220-threadsafe Revert "Make Redis::Objects.connection thread-safe" [Nate Wiger]
|
|
14
|
+
|
|
15
|
+
* Revert "Make Redis::Objects.connection thread-safe" [Nate Wiger]
|
|
16
|
+
|
|
17
|
+
== 1.4.2 (29 Aug 2018)
|
|
18
|
+
|
|
19
|
+
* Merge pull request #227 from D-system/optimise_lock Optimise lock [Nate Wiger]
|
|
20
|
+
|
|
21
|
+
* Merge pull request #228 from D-system/travis_ruby_2.5 Travis: test against ruby 2.5 [Nate Wiger]
|
|
22
|
+
|
|
23
|
+
* Travis: test against ruby 2.5 [Thomas Brennetot]
|
|
24
|
+
|
|
25
|
+
* Lock: update comment [Thomas Brennetot]
|
|
26
|
+
|
|
27
|
+
* Lock: add backward compatibility [Thomas Brennetot]
|
|
28
|
+
|
|
29
|
+
* Use SET with the NX and the expiration option in a single request [Thomas Brennetot]
|
|
30
|
+
|
|
31
|
+
* Merge pull request #218 from edwardbako/list_pushed_count Return count of lpush & rpush commands [Nate Wiger]
|
|
32
|
+
|
|
33
|
+
* Merge pull request #220 from gammons/threadsafe Make Redis::Objects.connection thread-safe [Nate Wiger]
|
|
34
|
+
|
|
35
|
+
* Make Redis::Objects.connection threadsafe [Grant Ammons]
|
|
36
|
+
|
|
37
|
+
* Return count of lpush & rpush commands [Edward Bako]
|
|
38
|
+
|
|
39
|
+
* Removed support for versions of Ruby < 2.2 [Nate Wiger]
|
|
40
|
+
|
|
41
|
+
== 1.4.1 (Unreleased)
|
|
42
|
+
|
|
43
|
+
* Buggy release that was removed
|
|
44
|
+
|
|
45
|
+
== 1.4.0 (7 Dec 2017)
|
|
46
|
+
|
|
47
|
+
* Bumped dependency to redis.rb 4.0 [Nate Wiger]
|
|
48
|
+
|
|
49
|
+
== 1.3.1 (29 Aug 2017)
|
|
50
|
+
|
|
51
|
+
* Merge pull request #213 from onk/feature/proc_expireat Allow to set proc for expireat [Nate Wiger]
|
|
52
|
+
|
|
53
|
+
* Merge pull request #212 from yuzixun/hotfix/counter modify Counter#nil? [Nate Wiger]
|
|
54
|
+
|
|
55
|
+
* Merge pull request #207 from i2bskn/improve_bulk_get Reduce query to Redis. [Nate Wiger]
|
|
56
|
+
|
|
57
|
+
* Merge pull request #209 from yuzixun/hotfix/hash_key/bulk_related fix error when fields/keys is an array [Nate Wiger]
|
|
58
|
+
|
|
59
|
+
* Use pre-calculated symbols for instance_variable_(get|set) for performance #211 [Nate Wiger]
|
|
60
|
+
|
|
61
|
+
* Weird inheriting from version AR::Migration thingy [Nate Wiger]
|
|
62
|
+
|
|
63
|
+
* Reduce query to Redis. Don't query to Redis if #bulk_get/#bulk_values arguments is empty. [i2bskn]
|
|
64
|
+
|
|
3
65
|
== 1.3.0 (11 Mar 2017)
|
|
4
66
|
|
|
5
67
|
* handle two Redis::Counter objects adding/subtracting [Nate Wiger]
|
data/README.md
CHANGED
|
@@ -261,6 +261,18 @@ Complex data is no problem with :marshal => true:
|
|
|
261
261
|
puts @newest.value['username']
|
|
262
262
|
~~~
|
|
263
263
|
|
|
264
|
+
Compress data to save memory usage on Redis with :compress => true:
|
|
265
|
+
|
|
266
|
+
~~~ruby
|
|
267
|
+
@account = Account.create!(params[:account])
|
|
268
|
+
@marshaled_value = Redis::Value.new('marshaled', :marshal => true, :compress => true)
|
|
269
|
+
@marshaled_value.value = @account.attributes
|
|
270
|
+
@unmarshaled_value = Redis::Value.new('unmarshaled', :compress => true)
|
|
271
|
+
@unmarshaled_value = 'Really Long String'
|
|
272
|
+
puts @marshaled_value.value['username']
|
|
273
|
+
puts @unmarshaled_value.value
|
|
274
|
+
~~~
|
|
275
|
+
|
|
264
276
|
Lists
|
|
265
277
|
-----
|
|
266
278
|
Lists work just like Ruby arrays:
|
|
@@ -541,6 +553,15 @@ value :value_with_expiration, :expiration => 1.hour
|
|
|
541
553
|
value :value_with_expireat, :expireat => Time.now + 1.hour
|
|
542
554
|
~~~
|
|
543
555
|
|
|
556
|
+
:warning: `expireat` is evaluated at class load time.
|
|
557
|
+
In this example, it will be one hour after evaluating class, not after one hour after setting value.
|
|
558
|
+
|
|
559
|
+
If you want to expire one hour after setting the value. please use `lambda`.
|
|
560
|
+
|
|
561
|
+
~~~ruby
|
|
562
|
+
value :value_with_expireat, :expireat => lambda { Time.now + 1.hour }
|
|
563
|
+
~~~
|
|
564
|
+
|
|
544
565
|
Author
|
|
545
566
|
=======
|
|
546
567
|
Copyright (c) 2009-2013 [Nate Wiger](http://nateware.com). All Rights Reserved.
|
data/lib/redis/base_object.rb
CHANGED
|
@@ -18,7 +18,9 @@ class Redis
|
|
|
18
18
|
if !@options[:expiration].nil?
|
|
19
19
|
redis.expire(@key, @options[:expiration]) if redis.ttl(@key) < 0
|
|
20
20
|
elsif !@options[:expireat].nil?
|
|
21
|
-
|
|
21
|
+
expireat = @options[:expireat]
|
|
22
|
+
at = expireat.respond_to?(:call) ? expireat.call.to_i : expireat.to_i
|
|
23
|
+
redis.expireat(@key, at) if redis.ttl(@key) < 0
|
|
22
24
|
end
|
|
23
25
|
end
|
|
24
26
|
|
|
@@ -41,5 +43,14 @@ class Redis
|
|
|
41
43
|
def to_hash
|
|
42
44
|
{ "key" => @key, "options" => @options, "value" => value }
|
|
43
45
|
end
|
|
46
|
+
|
|
47
|
+
# Math ops - delegate to value method
|
|
48
|
+
%w(== < > <= >=).each do |m|
|
|
49
|
+
class_eval <<-EndOverload
|
|
50
|
+
def #{m}(what)
|
|
51
|
+
value #{m} what
|
|
52
|
+
end
|
|
53
|
+
EndOverload
|
|
54
|
+
end
|
|
44
55
|
end
|
|
45
56
|
end
|
data/lib/redis/counter.rb
CHANGED
|
@@ -114,21 +114,18 @@ class Redis
|
|
|
114
114
|
# Proxy methods to help make @object.counter == 10 work
|
|
115
115
|
def to_s; value.to_s; end
|
|
116
116
|
alias_method :to_i, :value
|
|
117
|
-
def nil?; value.nil? end
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
value.to_i - what.to_i
|
|
122
|
-
end
|
|
123
|
-
def +(what)
|
|
124
|
-
value.to_i - what.to_i
|
|
118
|
+
def nil?
|
|
119
|
+
!redis.exists(key)
|
|
125
120
|
end
|
|
126
121
|
|
|
122
|
+
##
|
|
127
123
|
# Math ops
|
|
128
|
-
|
|
124
|
+
# This needs to handle +/- either actual integers or other Redis::Counters
|
|
125
|
+
%w(+ - == < > <= >=).each do |m|
|
|
129
126
|
class_eval <<-EndOverload
|
|
130
|
-
def #{m}(
|
|
131
|
-
value #{m}
|
|
127
|
+
def #{m}(what)
|
|
128
|
+
value.to_i #{m} what.to_i
|
|
132
129
|
end
|
|
133
130
|
EndOverload
|
|
134
131
|
end
|
data/lib/redis/hash_key.rb
CHANGED
|
@@ -13,7 +13,7 @@ class Redis
|
|
|
13
13
|
attr_reader :key, :options
|
|
14
14
|
def initialize(key, *args)
|
|
15
15
|
super
|
|
16
|
-
@options[:marshal_keys] ||= {}
|
|
16
|
+
@options[:marshal_keys] ||= {}
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# Redis: HSET
|
|
@@ -131,9 +131,9 @@ class Redis
|
|
|
131
131
|
def bulk_get(*fields)
|
|
132
132
|
hsh = {}
|
|
133
133
|
get_fields = *fields.flatten
|
|
134
|
-
|
|
134
|
+
return hsh if get_fields.empty?
|
|
135
135
|
res = redis.hmget(key, get_fields)
|
|
136
|
-
|
|
136
|
+
get_fields.each do |k|
|
|
137
137
|
hsh[k] = unmarshal(res.shift, options[:marshal_keys][k])
|
|
138
138
|
end
|
|
139
139
|
hsh
|
|
@@ -143,9 +143,9 @@ class Redis
|
|
|
143
143
|
# Values are returned in a collection in the same order than their keys in *keys Redis: HMGET
|
|
144
144
|
def bulk_values(*keys)
|
|
145
145
|
get_keys = *keys.flatten
|
|
146
|
-
|
|
146
|
+
return [] if get_keys.empty?
|
|
147
147
|
res = redis.hmget(key, get_keys)
|
|
148
|
-
|
|
148
|
+
get_keys.inject([]){|collection, k| collection << unmarshal(res.shift, options[:marshal_keys][k])}
|
|
149
149
|
end
|
|
150
150
|
|
|
151
151
|
# Increment value by integer at field. Redis: HINCRBY
|
data/lib/redis/list.rb
CHANGED
|
@@ -29,8 +29,9 @@ class Redis
|
|
|
29
29
|
# Add a member to the end of the list. Redis: RPUSH
|
|
30
30
|
def push(*values)
|
|
31
31
|
allow_expiration do
|
|
32
|
-
redis.rpush(key, values.map{|v| marshal(v) })
|
|
32
|
+
count = redis.rpush(key, values.map{|v| marshal(v) })
|
|
33
33
|
redis.ltrim(key, -options[:maxlength], -1) if options[:maxlength]
|
|
34
|
+
count
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
|
|
@@ -62,8 +63,9 @@ class Redis
|
|
|
62
63
|
# Add a member to the start of the list. Redis: LPUSH
|
|
63
64
|
def unshift(*values)
|
|
64
65
|
allow_expiration do
|
|
65
|
-
redis.lpush(key, values.map{|v| marshal(v) })
|
|
66
|
+
count = redis.lpush(key, values.map{|v| marshal(v) })
|
|
66
67
|
redis.ltrim(key, 0, options[:maxlength] - 1) if options[:maxlength]
|
|
68
|
+
count
|
|
67
69
|
end
|
|
68
70
|
end
|
|
69
71
|
|
data/lib/redis/lock.rb
CHANGED
|
@@ -33,28 +33,30 @@ class Redis
|
|
|
33
33
|
# Get the lock and execute the code block. Any other code that needs the lock
|
|
34
34
|
# (on any server) will spin waiting for the lock up to the :timeout
|
|
35
35
|
# that was specified when the lock was defined.
|
|
36
|
-
def lock
|
|
37
|
-
|
|
36
|
+
def lock
|
|
37
|
+
raise ArgumentError, 'Block not given' unless block_given?
|
|
38
|
+
expiration_ms = generate_expiration
|
|
39
|
+
expiration_s = expiration_ms / 1000.0
|
|
40
|
+
end_time = nil
|
|
38
41
|
try_until_timeout do
|
|
39
|
-
|
|
40
|
-
#
|
|
41
|
-
|
|
42
|
+
end_time = Time.now.to_i + expiration_s
|
|
43
|
+
# Set a NX record and use the Redis expiration mechanism.
|
|
44
|
+
# Empty value because the presence of it is enough to lock
|
|
45
|
+
# `px` only except an Integer in millisecond
|
|
46
|
+
break if redis.set(key, nil, px: expiration_ms, nx: true)
|
|
42
47
|
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
if !@options[:expiration].nil?
|
|
48
|
+
# Backward compatibility code
|
|
49
|
+
# TODO: remove at the next major release for performance
|
|
50
|
+
unless @options[:expiration].nil?
|
|
47
51
|
old_expiration = redis.get(key).to_f
|
|
48
52
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# and we now have it.
|
|
57
|
-
break if old_expiration < Time.now.to_f
|
|
53
|
+
# Check it was not an empty string with `zero?` and
|
|
54
|
+
# the expiration time is passed.
|
|
55
|
+
if !old_expiration.zero? && old_expiration < Time.now.to_f
|
|
56
|
+
expiration_ms = generate_expiration
|
|
57
|
+
expiration_s = expiration_ms / 1000.0
|
|
58
|
+
end_time = Time.now.to_i + expiration_s
|
|
59
|
+
break if redis.set(key, nil, px: expiration_ms)
|
|
58
60
|
end
|
|
59
61
|
end
|
|
60
62
|
end
|
|
@@ -66,14 +68,15 @@ class Redis
|
|
|
66
68
|
# it's not safe for us to remove it. Check how much time has passed since we
|
|
67
69
|
# wrote the lock key and only delete it if it hasn't expired (or we're not using
|
|
68
70
|
# lock expiration)
|
|
69
|
-
if @options[:expiration].nil? ||
|
|
71
|
+
if @options[:expiration].nil? || end_time > Time.now.to_f
|
|
70
72
|
redis.del(key)
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
|
|
77
|
+
# Return expiration in milliseconds
|
|
75
78
|
def generate_expiration
|
|
76
|
-
@options[:expiration].nil? ? 1 :
|
|
79
|
+
((@options[:expiration].nil? ? 1 : @options[:expiration].to_f) * 1000).to_i
|
|
77
80
|
end
|
|
78
81
|
|
|
79
82
|
private
|
|
@@ -23,11 +23,12 @@ class Redis
|
|
|
23
23
|
options[:start] ||= 0
|
|
24
24
|
options[:type] ||= options[:start] == 0 ? :increment : :decrement
|
|
25
25
|
redis_objects[name.to_sym] = options.merge(:type => :counter)
|
|
26
|
+
ivar_name = :"@#{name}"
|
|
26
27
|
|
|
27
28
|
mod = Module.new do
|
|
28
29
|
define_method(name) do
|
|
29
|
-
instance_variable_get(
|
|
30
|
-
instance_variable_set(
|
|
30
|
+
instance_variable_get(ivar_name) or
|
|
31
|
+
instance_variable_set(ivar_name,
|
|
31
32
|
Redis::Counter.new(
|
|
32
33
|
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
|
33
34
|
)
|
data/lib/redis/objects/hashes.rb
CHANGED
|
@@ -15,11 +15,12 @@ class Redis
|
|
|
15
15
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
16
16
|
def hash_key(name, options={})
|
|
17
17
|
redis_objects[name.to_sym] = options.merge(:type => :dict)
|
|
18
|
+
ivar_name = :"@#{name}"
|
|
18
19
|
|
|
19
20
|
mod = Module.new do
|
|
20
21
|
define_method(name) do
|
|
21
|
-
instance_variable_get(
|
|
22
|
-
instance_variable_set(
|
|
22
|
+
instance_variable_get(ivar_name) or
|
|
23
|
+
instance_variable_set(ivar_name,
|
|
23
24
|
Redis::HashKey.new(
|
|
24
25
|
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
|
25
26
|
)
|
data/lib/redis/objects/lists.rb
CHANGED
|
@@ -15,11 +15,12 @@ class Redis
|
|
|
15
15
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
16
16
|
def list(name, options={})
|
|
17
17
|
redis_objects[name.to_sym] = options.merge(:type => :list)
|
|
18
|
+
ivar_name = :"@#{name}"
|
|
18
19
|
|
|
19
20
|
mod = Module.new do
|
|
20
21
|
define_method(name) do
|
|
21
|
-
instance_variable_get(
|
|
22
|
-
instance_variable_set(
|
|
22
|
+
instance_variable_get(ivar_name) or
|
|
23
|
+
instance_variable_set(ivar_name,
|
|
23
24
|
Redis::List.new(
|
|
24
25
|
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
|
25
26
|
)
|
data/lib/redis/objects/locks.rb
CHANGED
|
@@ -18,11 +18,12 @@ class Redis
|
|
|
18
18
|
options[:timeout] ||= 5 # seconds
|
|
19
19
|
lock_name = "#{name}_lock"
|
|
20
20
|
redis_objects[lock_name.to_sym] = options.merge(:type => :lock)
|
|
21
|
+
ivar_name = :"@#{lock_name}"
|
|
21
22
|
|
|
22
23
|
mod = Module.new do
|
|
23
24
|
define_method(lock_name) do |&block|
|
|
24
|
-
instance_variable_get(
|
|
25
|
-
instance_variable_set(
|
|
25
|
+
instance_variable_get(ivar_name) or
|
|
26
|
+
instance_variable_set(ivar_name,
|
|
26
27
|
Redis::Lock.new(
|
|
27
28
|
redis_field_key(lock_name), redis_field_redis(lock_name), redis_objects[lock_name.to_sym]
|
|
28
29
|
)
|
data/lib/redis/objects/sets.rb
CHANGED
|
@@ -15,11 +15,12 @@ class Redis
|
|
|
15
15
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
16
16
|
def set(name, options={})
|
|
17
17
|
redis_objects[name.to_sym] = options.merge(:type => :set)
|
|
18
|
+
ivar_name = :"@#{name}"
|
|
18
19
|
|
|
19
20
|
mod = Module.new do
|
|
20
21
|
define_method(name) do
|
|
21
|
-
instance_variable_get(
|
|
22
|
-
instance_variable_set(
|
|
22
|
+
instance_variable_get(ivar_name) or
|
|
23
|
+
instance_variable_set(ivar_name,
|
|
23
24
|
Redis::Set.new(
|
|
24
25
|
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
|
25
26
|
)
|
|
@@ -15,11 +15,12 @@ class Redis
|
|
|
15
15
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
16
16
|
def sorted_set(name, options={})
|
|
17
17
|
redis_objects[name.to_sym] = options.merge(:type => :sorted_set)
|
|
18
|
+
ivar_name = :"@#{name}"
|
|
18
19
|
|
|
19
20
|
mod = Module.new do
|
|
20
21
|
define_method(name) do
|
|
21
|
-
instance_variable_get(
|
|
22
|
-
instance_variable_set(
|
|
22
|
+
instance_variable_get(ivar_name) or
|
|
23
|
+
instance_variable_set(ivar_name,
|
|
23
24
|
Redis::SortedSet.new(
|
|
24
25
|
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
|
25
26
|
)
|
data/lib/redis/objects/values.rb
CHANGED
|
@@ -15,11 +15,12 @@ class Redis
|
|
|
15
15
|
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
|
16
16
|
def value(name, options={})
|
|
17
17
|
redis_objects[name.to_sym] = options.merge(:type => :value)
|
|
18
|
+
ivar_name = :"@#{name}"
|
|
18
19
|
|
|
19
20
|
mod = Module.new do
|
|
20
21
|
define_method(name) do
|
|
21
|
-
instance_variable_get(
|
|
22
|
-
instance_variable_set(
|
|
22
|
+
instance_variable_get(ivar_name) or
|
|
23
|
+
instance_variable_set(ivar_name,
|
|
23
24
|
Redis::Value.new(
|
|
24
25
|
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
|
25
26
|
)
|
data/lib/redis/objects.rb
CHANGED
|
@@ -70,8 +70,8 @@ class Redis
|
|
|
70
70
|
|
|
71
71
|
def included(klass)
|
|
72
72
|
# Core (this file)
|
|
73
|
-
klass.instance_variable_set(
|
|
74
|
-
klass.instance_variable_set(
|
|
73
|
+
klass.instance_variable_set(:@redis, nil)
|
|
74
|
+
klass.instance_variable_set(:@redis_objects, {})
|
|
75
75
|
klass.send :include, InstanceMethods
|
|
76
76
|
klass.extend ClassMethods
|
|
77
77
|
|
data/lib/redis/set.rb
CHANGED
|
@@ -165,7 +165,7 @@ class Redis
|
|
|
165
165
|
redis.smove(key, destination.is_a?(Redis::Set) ? destination.key : destination.to_s, value)
|
|
166
166
|
end
|
|
167
167
|
|
|
168
|
-
# The number of members in the set. Aliased as size. Redis: SCARD
|
|
168
|
+
# The number of members in the set. Aliased as size or count. Redis: SCARD
|
|
169
169
|
def length
|
|
170
170
|
redis.scard(key)
|
|
171
171
|
end
|
data/lib/redis/sorted_set.rb
CHANGED
|
@@ -208,7 +208,17 @@ class Redis
|
|
|
208
208
|
#
|
|
209
209
|
# Redis: SINTER
|
|
210
210
|
def intersection(*sets)
|
|
211
|
-
|
|
211
|
+
result = nil
|
|
212
|
+
temp_key = :"#{key}:intersection:#{Time.current.to_i + rand}"
|
|
213
|
+
|
|
214
|
+
redis.multi do
|
|
215
|
+
interstore(temp_key, *sets)
|
|
216
|
+
redis.expire(temp_key, 1)
|
|
217
|
+
|
|
218
|
+
result = redis.zrange(temp_key, 0, -1)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
result.value
|
|
212
222
|
end
|
|
213
223
|
alias_method :intersect, :intersection
|
|
214
224
|
alias_method :inter, :intersection
|
|
@@ -235,7 +245,17 @@ class Redis
|
|
|
235
245
|
#
|
|
236
246
|
# Redis: SUNION
|
|
237
247
|
def union(*sets)
|
|
238
|
-
|
|
248
|
+
result = nil
|
|
249
|
+
temp_key = :"#{key}:union:#{Time.current.to_i + rand}"
|
|
250
|
+
|
|
251
|
+
redis.multi do
|
|
252
|
+
unionstore(temp_key, *sets)
|
|
253
|
+
redis.expire(temp_key, 1)
|
|
254
|
+
|
|
255
|
+
result = redis.zrange(temp_key, 0, -1)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
result.value
|
|
239
259
|
end
|
|
240
260
|
alias_method :|, :union
|
|
241
261
|
alias_method :+, :union
|
|
@@ -249,31 +269,6 @@ class Redis
|
|
|
249
269
|
end
|
|
250
270
|
end
|
|
251
271
|
|
|
252
|
-
# Return the difference vs another set. Can pass it either another set
|
|
253
|
-
# object or set name. Also available as ^ or - which is a bit cleaner:
|
|
254
|
-
#
|
|
255
|
-
# members_difference = set1 ^ set2
|
|
256
|
-
# members_difference = set1 - set2
|
|
257
|
-
#
|
|
258
|
-
# If you want to specify multiple sets, you must use +difference+:
|
|
259
|
-
#
|
|
260
|
-
# members_difference = set1.difference(set2, set3, set4)
|
|
261
|
-
# members_difference = set1.diff(set2, set3, set4)
|
|
262
|
-
#
|
|
263
|
-
# Redis: SDIFF
|
|
264
|
-
def difference(*sets)
|
|
265
|
-
redis.zdiff(key, *keys_from_objects(sets)).map{|v| unmarshal(v) }
|
|
266
|
-
end
|
|
267
|
-
alias_method :diff, :difference
|
|
268
|
-
alias_method :^, :difference
|
|
269
|
-
alias_method :-, :difference
|
|
270
|
-
|
|
271
|
-
# Calculate the diff and store it in Redis as +name+. Returns the number
|
|
272
|
-
# of elements in the stored union. Redis: SDIFFSTORE
|
|
273
|
-
def diffstore(name, *sets)
|
|
274
|
-
redis.zdiffstore(name, key, *keys_from_objects(sets))
|
|
275
|
-
end
|
|
276
|
-
|
|
277
272
|
# Returns true if the set has no members. Redis: SCARD == 0
|
|
278
273
|
def empty?
|
|
279
274
|
length == 0
|
|
@@ -303,11 +298,12 @@ class Redis
|
|
|
303
298
|
at(-1)
|
|
304
299
|
end
|
|
305
300
|
|
|
306
|
-
# The number of members in the set. Aliased as size. Redis: ZCARD
|
|
301
|
+
# The number of members in the set. Aliased as size or count. Redis: ZCARD
|
|
307
302
|
def length
|
|
308
303
|
redis.zcard(key)
|
|
309
304
|
end
|
|
310
305
|
alias_method :size, :length
|
|
306
|
+
alias_method :count, :length
|
|
311
307
|
|
|
312
308
|
# The number of members within a range of scores. Redis: ZCOUNT
|
|
313
309
|
def range_size(min, max)
|
data/lib/redis/value.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require File.dirname(__FILE__) + '/base_object'
|
|
2
|
+
require 'zlib'
|
|
2
3
|
|
|
3
4
|
class Redis
|
|
4
5
|
#
|
|
@@ -31,6 +32,30 @@ class Redis
|
|
|
31
32
|
end
|
|
32
33
|
alias_method :get, :value
|
|
33
34
|
|
|
35
|
+
def marshal(value, *args)
|
|
36
|
+
if !value.nil? && options[:compress]
|
|
37
|
+
compress(super)
|
|
38
|
+
else
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def unmarshal(value, *args)
|
|
44
|
+
if !value.nil? && options[:compress]
|
|
45
|
+
super(decompress(value), *args)
|
|
46
|
+
else
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def decompress(value)
|
|
52
|
+
Zlib::Inflate.inflate(value)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def compress(value)
|
|
56
|
+
Zlib::Deflate.deflate(value)
|
|
57
|
+
end
|
|
58
|
+
|
|
34
59
|
def inspect
|
|
35
60
|
"#<Redis::Value #{value.inspect}>"
|
|
36
61
|
end
|
data/redis-objects.gemspec
CHANGED
|
@@ -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", "~>
|
|
22
|
+
spec.add_dependency "redis", "~> 4.0"
|
|
23
23
|
|
|
24
24
|
# Ignore gemspec warnings on these. Trying to fix them to versions breaks TravisCI
|
|
25
25
|
spec.add_development_dependency "bundler"
|
|
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
# Compatibility testing
|
|
31
31
|
spec.add_development_dependency "redis-namespace"
|
|
32
32
|
spec.add_development_dependency "activerecord"
|
|
33
|
+
spec.add_development_dependency "sqlite3"
|
|
33
34
|
end
|
|
@@ -11,11 +11,20 @@ begin
|
|
|
11
11
|
:database => File.expand_path(File.dirname(__FILE__) + '/redis_objects_test.sqlite3')
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
# monkey patch to use migrations both in Rails 4.x and 5.x
|
|
15
|
+
class ActiveRecord::Migration
|
|
16
|
+
class << self
|
|
17
|
+
def [](version)
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end unless ActiveRecord::Migration.respond_to?(:[])
|
|
22
|
+
|
|
23
|
+
class CreateBlogs < ActiveRecord::Migration[4.2]
|
|
15
24
|
def self.up
|
|
16
25
|
create_table :blogs do |t|
|
|
17
26
|
t.string :name
|
|
18
|
-
t.integer :num_posts, :default => 0
|
|
27
|
+
# t.integer :num_posts, :default => 0
|
|
19
28
|
t.timestamps null: true
|
|
20
29
|
end
|
|
21
30
|
end
|
|
@@ -28,9 +37,10 @@ begin
|
|
|
28
37
|
class Blog < ActiveRecord::Base
|
|
29
38
|
include Redis::Objects
|
|
30
39
|
has_many :posts
|
|
40
|
+
counter :num_posts
|
|
31
41
|
end
|
|
32
42
|
|
|
33
|
-
class CreatePosts < ActiveRecord::Migration
|
|
43
|
+
class CreatePosts < ActiveRecord::Migration[4.2]
|
|
34
44
|
def self.up
|
|
35
45
|
create_table :posts do |t|
|
|
36
46
|
t.string :title
|
|
@@ -50,11 +60,13 @@ begin
|
|
|
50
60
|
include Redis::Objects
|
|
51
61
|
counter :total
|
|
52
62
|
counter :num_comments
|
|
53
|
-
|
|
63
|
+
# Unfortunately, counter counter_cache appears to be broken
|
|
64
|
+
# belongs_to :blog, :counter_cache => :num_posts
|
|
65
|
+
belongs_to :blog
|
|
54
66
|
has_many :comments
|
|
55
67
|
end
|
|
56
68
|
|
|
57
|
-
class CreateComments < ActiveRecord::Migration
|
|
69
|
+
class CreateComments < ActiveRecord::Migration[4.2]
|
|
58
70
|
def self.up
|
|
59
71
|
create_table :comments do |t|
|
|
60
72
|
t.string :body
|
|
@@ -70,7 +82,9 @@ begin
|
|
|
70
82
|
|
|
71
83
|
class Comment < ActiveRecord::Base
|
|
72
84
|
include Redis::Objects
|
|
73
|
-
belongs_to :post
|
|
85
|
+
belongs_to :post
|
|
86
|
+
# Unfortunately, counter counter_cache appears to be broken
|
|
87
|
+
# belongs_to :post, :counter_cache => :num_comments
|
|
74
88
|
end
|
|
75
89
|
|
|
76
90
|
describe ActiveRecord do
|
|
@@ -87,6 +101,7 @@ begin
|
|
|
87
101
|
|
|
88
102
|
it "exercises ActiveRecord in more detail" do
|
|
89
103
|
@ar = Post.new
|
|
104
|
+
should.raise(Redis::Objects::NilObjectId){ @ar.total }
|
|
90
105
|
@ar.save!
|
|
91
106
|
@ar.destroy
|
|
92
107
|
|
|
@@ -113,34 +128,35 @@ begin
|
|
|
113
128
|
it "uses the redis objects counter cache when present" do
|
|
114
129
|
blog = Blog.create
|
|
115
130
|
post = Post.create :blog => blog
|
|
131
|
+
blog.num_posts.incr
|
|
116
132
|
blog.num_posts.should == 1
|
|
117
133
|
Post.counter_defined?(:num_comments).should == true
|
|
118
134
|
post.num_comments.should == 0
|
|
135
|
+
|
|
119
136
|
comment = Comment.create :post => post
|
|
137
|
+
post.num_comments.incr
|
|
120
138
|
post.comments.count.should == 1
|
|
121
139
|
post.id.should == 1
|
|
122
140
|
comment.destroy
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
#post.reload.num_comments.should == 0
|
|
141
|
+
blog.num_posts.delete
|
|
142
|
+
blog.destroy
|
|
126
143
|
end
|
|
127
144
|
|
|
128
145
|
it "falls back to ActiveRecord if redis counter is not defined" do
|
|
129
146
|
blog = Blog.create
|
|
130
|
-
blog.
|
|
147
|
+
blog.id.should == 1
|
|
148
|
+
blog.num_posts.should == 0
|
|
131
149
|
post = Post.create :blog => blog
|
|
132
|
-
blog.
|
|
150
|
+
blog.num_posts.incr
|
|
151
|
+
blog.num_posts.should == 1
|
|
133
152
|
blog2 = Blog.create
|
|
134
153
|
Post.create :blog => blog2
|
|
135
154
|
Post.create :blog => blog2
|
|
136
155
|
blog.reload.num_posts.should == 1
|
|
156
|
+
blog2.num_posts.incr
|
|
157
|
+
blog2.num_posts.incr
|
|
137
158
|
blog2.reload.num_posts.should == 2
|
|
138
159
|
blog.num_posts.should == 1
|
|
139
160
|
end
|
|
140
161
|
end
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
rescue LoadError
|
|
144
|
-
# ActiveRecord not install
|
|
145
|
-
puts "Skipping ActiveRecord tests as active_record is not installed"
|
|
146
162
|
end
|
|
@@ -19,6 +19,28 @@ describe Redis::Value do
|
|
|
19
19
|
@value.value.should == false
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
it "should compress non marshaled values" do
|
|
23
|
+
@value = Redis::Value.new('spec/value', compress: true)
|
|
24
|
+
@value.value = 'Trevor Hoffman'
|
|
25
|
+
@value.value.should == 'Trevor Hoffman'
|
|
26
|
+
@value.redis.get(@value.key).should.not == 'Trevor Hoffman'
|
|
27
|
+
@value.value = nil
|
|
28
|
+
@value.value.should == nil
|
|
29
|
+
@value.value = ''
|
|
30
|
+
@value.value.should == ''
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should compress marshaled values" do
|
|
34
|
+
@value = Redis::Value.new('spec/value', marshal: true, compress: true)
|
|
35
|
+
@value.value = 'Trevor Hoffman'
|
|
36
|
+
@value.value.should == 'Trevor Hoffman'
|
|
37
|
+
@value.redis.get(@value.key).should.not == Marshal.dump('Trevor Hoffman')
|
|
38
|
+
@value.value = nil
|
|
39
|
+
@value.value.should == nil
|
|
40
|
+
@value.value = ''
|
|
41
|
+
@value.value.should == ''
|
|
42
|
+
end
|
|
43
|
+
|
|
22
44
|
it "should handle simple values" do
|
|
23
45
|
@value.should == nil
|
|
24
46
|
@value.value = 'Trevor Hoffman'
|
|
@@ -568,16 +590,15 @@ describe Redis::Lock do
|
|
|
568
590
|
REDIS_HANDLE.flushall
|
|
569
591
|
end
|
|
570
592
|
|
|
571
|
-
it "should
|
|
572
|
-
start = Time.now
|
|
593
|
+
it "should ttl to the expiration" do
|
|
573
594
|
expiry = 15
|
|
574
595
|
lock = Redis::Lock.new(:test_lock, :expiration => expiry)
|
|
575
596
|
lock.lock do
|
|
576
|
-
expiration = REDIS_HANDLE.
|
|
597
|
+
expiration = REDIS_HANDLE.ttl("test_lock")
|
|
577
598
|
|
|
578
599
|
# The expiration stored in redis should be 15 seconds from when we started
|
|
579
600
|
# or a little more
|
|
580
|
-
expiration.should.be.close(
|
|
601
|
+
expiration.should.be.close(expiration, 2.0)
|
|
581
602
|
end
|
|
582
603
|
|
|
583
604
|
# key should have been cleaned up
|
|
@@ -587,30 +608,13 @@ describe Redis::Lock do
|
|
|
587
608
|
it "should set value to 1 when no expiration is set" do
|
|
588
609
|
lock = Redis::Lock.new(:test_lock)
|
|
589
610
|
lock.lock do
|
|
590
|
-
REDIS_HANDLE.
|
|
611
|
+
REDIS_HANDLE.ttl('test_lock').should == 1
|
|
591
612
|
end
|
|
592
613
|
|
|
593
614
|
# key should have been cleaned up
|
|
594
615
|
REDIS_HANDLE.get("test_lock").should.be.nil
|
|
595
616
|
end
|
|
596
617
|
|
|
597
|
-
it "should let lock be gettable when lock is expired" do
|
|
598
|
-
expiry = 15
|
|
599
|
-
lock = Redis::Lock.new(:test_lock, :expiration => expiry, :timeout => 0.1)
|
|
600
|
-
|
|
601
|
-
# create a fake lock in the past
|
|
602
|
-
REDIS_HANDLE.set("test_lock", Time.now-(expiry + 60))
|
|
603
|
-
|
|
604
|
-
gotit = false
|
|
605
|
-
lock.lock do
|
|
606
|
-
gotit = true
|
|
607
|
-
end
|
|
608
|
-
|
|
609
|
-
# should get the lock because it has expired
|
|
610
|
-
gotit.should.be.true
|
|
611
|
-
REDIS_HANDLE.get("test_lock").should.be.nil
|
|
612
|
-
end
|
|
613
|
-
|
|
614
618
|
it "should not let non-expired locks be gettable" do
|
|
615
619
|
expiry = 15
|
|
616
620
|
lock = Redis::Lock.new(:test_lock, :expiration => expiry, :timeout => 0.1)
|
|
@@ -636,17 +640,50 @@ describe Redis::Lock do
|
|
|
636
640
|
REDIS_HANDLE.get("test_lock").should.not.be.nil
|
|
637
641
|
end
|
|
638
642
|
|
|
639
|
-
it "should
|
|
640
|
-
lock = Redis::Lock.new(:test_lock, :expiration => 0.
|
|
643
|
+
it "Redis should remove the key if lock is held past expiration" do
|
|
644
|
+
lock = Redis::Lock.new(:test_lock, :expiration => 0.1)
|
|
641
645
|
|
|
642
646
|
lock.lock do
|
|
643
|
-
|
|
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
|
|
644
652
|
end
|
|
645
653
|
|
|
646
|
-
# lock value should
|
|
647
|
-
REDIS_HANDLE.
|
|
654
|
+
# 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
|
|
648
656
|
end
|
|
649
657
|
|
|
658
|
+
|
|
659
|
+
it "should not manually delete a key with a 'lock' name if finished after expiration" do
|
|
660
|
+
lock = Redis::Lock.new(:test_lock2, :expiration => 0.1)
|
|
661
|
+
|
|
662
|
+
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
|
|
666
|
+
REDIS_HANDLE.set("test_lock2", "foo") # this is no longer a lock key, name is a coincidence
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
REDIS_HANDLE.get("test_lock2").should == "foo"
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
it "should manually delete the key if finished before expiration" do
|
|
673
|
+
lock = Redis::Lock.new(:test_lock3, :expiration => 0.5)
|
|
674
|
+
|
|
675
|
+
lock.lock do
|
|
676
|
+
REDIS_HANDLE.exists("test_lock3").should.be.true
|
|
677
|
+
sleep 0.1
|
|
678
|
+
REDIS_HANDLE.exists("test_lock3").should.be.true
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# should delete the key because the lock block is done, regardless of time
|
|
682
|
+
# for some strange reason, I have seen this test fail randomly, which is worrisome.
|
|
683
|
+
#REDIS_HANDLE.exists("test_lock3").should.be.false
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
|
|
650
687
|
it "should respond to #to_json" do
|
|
651
688
|
Redis::Lock.new(:test_lock).to_json.should.be.kind_of(String)
|
|
652
689
|
end
|
|
@@ -654,6 +691,26 @@ describe Redis::Lock do
|
|
|
654
691
|
it "should respond to #as_json" do
|
|
655
692
|
Redis::Lock.new(:test_lock).as_json.should.be.kind_of(Hash)
|
|
656
693
|
end
|
|
694
|
+
|
|
695
|
+
it "should deal with old lock format" do
|
|
696
|
+
expiry = 15
|
|
697
|
+
lock = Redis::Lock.new(:test_lock, expiration: expiry, timeout: 0.1)
|
|
698
|
+
|
|
699
|
+
# create a fake lock in the past
|
|
700
|
+
REDIS_HANDLE.set("test_lock", (Time.now - expiry).to_f)
|
|
701
|
+
|
|
702
|
+
gotit = false
|
|
703
|
+
lock.lock do
|
|
704
|
+
gotit = true
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
# should have the lock
|
|
708
|
+
gotit.should.be.true
|
|
709
|
+
|
|
710
|
+
# lock value should be unset
|
|
711
|
+
REDIS_HANDLE.get("test_lock").should.be.nil
|
|
712
|
+
end
|
|
713
|
+
|
|
657
714
|
end
|
|
658
715
|
|
|
659
716
|
describe Redis::HashKey do
|
|
@@ -891,6 +948,14 @@ describe Redis::HashKey do
|
|
|
891
948
|
@hash.as_json['value'].should == { "abc" => "123" }
|
|
892
949
|
end
|
|
893
950
|
|
|
951
|
+
it "should return empty object with bulk_get and bulk_value" do
|
|
952
|
+
h = @hash.bulk_get
|
|
953
|
+
h.should == {}
|
|
954
|
+
|
|
955
|
+
v = @hash.bulk_values
|
|
956
|
+
v.should == []
|
|
957
|
+
end
|
|
958
|
+
|
|
894
959
|
describe 'with expiration' do
|
|
895
960
|
{
|
|
896
961
|
:incrby => 'somekey',
|
|
@@ -1243,6 +1308,7 @@ describe Redis::SortedSet do
|
|
|
1243
1308
|
@set.delete('c')
|
|
1244
1309
|
@set.length.should == 4
|
|
1245
1310
|
@set.size.should == 4
|
|
1311
|
+
@set.count.should == 4
|
|
1246
1312
|
|
|
1247
1313
|
@set.range_size(100, 120).should == 0
|
|
1248
1314
|
@set.range_size(0, 100).should == 2
|
|
@@ -1308,6 +1374,8 @@ describe Redis::SortedSet do
|
|
|
1308
1374
|
@set_2.add('c', 1)
|
|
1309
1375
|
@set_2.add('d', 0)
|
|
1310
1376
|
|
|
1377
|
+
@set_1.union(@set_2).should == ['d', 'a', 'c', 'b']
|
|
1378
|
+
|
|
1311
1379
|
@set_1.unionstore(@set.key, @set_2)
|
|
1312
1380
|
# @set is now: [[d, 0], [a, 1], [c, 4], [b, 6]]
|
|
1313
1381
|
@set.members.should == ['d', 'a', 'c', 'b']
|
|
@@ -1347,6 +1415,8 @@ describe Redis::SortedSet do
|
|
|
1347
1415
|
@set_2.add('c', 1)
|
|
1348
1416
|
@set_2.add('d', 0)
|
|
1349
1417
|
|
|
1418
|
+
@set_1.intersection(@set_2).should == ['c', 'b']
|
|
1419
|
+
|
|
1350
1420
|
@set_1.interstore(@set.key, @set_2)
|
|
1351
1421
|
# @set is now: [[c, 4], [b, 6]]
|
|
1352
1422
|
@set.members.should == ['c', 'b']
|
|
@@ -47,6 +47,8 @@ class Roster
|
|
|
47
47
|
sorted_set :sorted_set_with_expiration,:expiration => 10
|
|
48
48
|
sorted_set :sorted_set_with_expireat, :expireat => Time.now + 10.seconds
|
|
49
49
|
|
|
50
|
+
value :value_with_expireat_with_lambda, :expireat => lambda { Time.now + 10.seconds }
|
|
51
|
+
|
|
50
52
|
def initialize(id=1) @id = id end
|
|
51
53
|
def id; @id; end
|
|
52
54
|
def username; "user#{id}"; end
|
|
@@ -993,6 +995,19 @@ describe Redis::Objects do
|
|
|
993
995
|
@roster.sorted_set_with_expireat.ttl.should <= 10
|
|
994
996
|
end
|
|
995
997
|
|
|
998
|
+
it "should set expiration when expireat option assigned with lambda" do
|
|
999
|
+
travel(1.minute) do
|
|
1000
|
+
# non-proc expireat is not affected by time travel
|
|
1001
|
+
@roster.value_with_expireat.value = 'val'
|
|
1002
|
+
@roster.value_with_expireat.ttl.should > 0
|
|
1003
|
+
@roster.value_with_expireat.ttl.should <= 10
|
|
1004
|
+
|
|
1005
|
+
@roster.value_with_expireat_with_lambda.value = 'val'
|
|
1006
|
+
@roster.value_with_expireat_with_lambda.ttl.should > 60
|
|
1007
|
+
@roster.value_with_expireat_with_lambda.ttl.should <= 70
|
|
1008
|
+
end
|
|
1009
|
+
end
|
|
1010
|
+
|
|
996
1011
|
it "should allow deleting the entire object" do
|
|
997
1012
|
@roster.redis.keys.select { |key| key.match(/^roster:/)}.count.should > 0
|
|
998
1013
|
@roster.delete!.should > 0
|
data/spec/spec_helper.rb
CHANGED
|
@@ -9,6 +9,9 @@ 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
|
+
require "active_support/testing/time_helpers"
|
|
13
|
+
include ActiveSupport::Testing::TimeHelpers
|
|
14
|
+
|
|
12
15
|
REDIS_CLASS_NAMES = [:Counter, :HashKey, :List, :Lock, :Set, :SortedSet, :Value]
|
|
13
16
|
|
|
14
17
|
UNIONSTORE_KEY = 'test:unionstore'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: redis-objects
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3
|
|
4
|
+
version: 1.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nate Wiger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2018-10-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: redis
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '4.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: '
|
|
26
|
+
version: '4.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,6 +108,20 @@ dependencies:
|
|
|
108
108
|
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
110
|
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: sqlite3
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
111
125
|
description: Map Redis types directly to Ruby objects. Works with any class or ORM.
|
|
112
126
|
email:
|
|
113
127
|
- nwiger@gmail.com
|
|
@@ -171,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
171
185
|
version: '0'
|
|
172
186
|
requirements: []
|
|
173
187
|
rubyforge_project:
|
|
174
|
-
rubygems_version: 2.
|
|
188
|
+
rubygems_version: 2.7.3
|
|
175
189
|
signing_key:
|
|
176
190
|
specification_version: 4
|
|
177
191
|
summary: Map Redis types directly to Ruby objects
|