redis-objects 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|