redis-objects 1.3.0 → 1.3.1
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 +4 -4
- data/README.md +9 -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/objects.rb +2 -2
- 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/spec/redis_objects_active_record_spec.rb +32 -16
- data/spec/redis_objects_instance_spec.rb +8 -0
- data/spec/redis_objects_model_spec.rb +15 -0
- data/spec/spec_helper.rb +3 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae3bd1cf4a81eca5bbb34a027f066b2dcbd233d5
|
4
|
+
data.tar.gz: 03ec3b26287e799061232c37a9604b0b65e52219
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af1e410ca8fdb1251e4b67c6ff6e051a76ea78804c729b82cc7ba90ced750cc9139c9fbf86d78b2e79476a55c66a7c59733c150f6ff9adb9a9e16bf94ee7daf7
|
7
|
+
data.tar.gz: 9e8deeb4e39b6ccf713f461eae512870c3fac18d211984583d3ad10814b231515d724aee6c22b9cecd7306b29d2e20f68de77f4f794c3deae3b6fa4d74153a1b
|
data/README.md
CHANGED
@@ -541,6 +541,15 @@ value :value_with_expiration, :expiration => 1.hour
|
|
541
541
|
value :value_with_expireat, :expireat => Time.now + 1.hour
|
542
542
|
~~~
|
543
543
|
|
544
|
+
:warning: `expireat` is evaluated at class load time.
|
545
|
+
In this example, it will be one hour after evaluating class, not after one hour after setting value.
|
546
|
+
|
547
|
+
If you want to expire one hour after setting the value. please use `lambda`.
|
548
|
+
|
549
|
+
~~~ruby
|
550
|
+
value :value_with_expireat, :expireat => lambda { Time.now + 1.hour }
|
551
|
+
~~~
|
552
|
+
|
544
553
|
Author
|
545
554
|
=======
|
546
555
|
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/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
|
|
@@ -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
|
)
|
@@ -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
|
@@ -891,6 +891,14 @@ describe Redis::HashKey do
|
|
891
891
|
@hash.as_json['value'].should == { "abc" => "123" }
|
892
892
|
end
|
893
893
|
|
894
|
+
it "should return empty object with bulk_get and bulk_value" do
|
895
|
+
h = @hash.bulk_get
|
896
|
+
h.should == {}
|
897
|
+
|
898
|
+
v = @hash.bulk_values
|
899
|
+
v.should == []
|
900
|
+
end
|
901
|
+
|
894
902
|
describe 'with expiration' do
|
895
903
|
{
|
896
904
|
:incrby => 'somekey',
|
@@ -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.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nate Wiger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -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.4.5
|
175
189
|
signing_key:
|
176
190
|
specification_version: 4
|
177
191
|
summary: Map Redis types directly to Ruby objects
|