redis-objects 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +12 -10
- data/VERSION +1 -1
- data/lib/redis/hash_key.rb +10 -1
- data/lib/redis/helpers/core_commands.rb +5 -1
- data/lib/redis/list.rb +12 -0
- data/lib/redis/set.rb +18 -0
- data/lib/redis/sorted_set.rb +10 -2
- data/lib/redis/value.rb +18 -5
- data/redis-objects.gemspec +2 -2
- data/spec/redis_objects_instance_spec.rb +65 -0
- data/spec/redis_objects_model_spec.rb +20 -0
- data/spec/spec_helper.rb +3 -0
- metadata +43 -49
data/README.rdoc
CHANGED
@@ -26,7 +26,9 @@ you want to create.
|
|
26
26
|
|
27
27
|
== Installation
|
28
28
|
|
29
|
-
|
29
|
+
Add it to your Gemfile as:
|
30
|
+
|
31
|
+
gem 'redis-objects', :require => 'redis/objects'
|
30
32
|
|
31
33
|
== Example 1: Model Class Usage
|
32
34
|
|
@@ -45,7 +47,7 @@ config/initializers/redis.rb is a good place for this.)
|
|
45
47
|
|
46
48
|
require 'redis'
|
47
49
|
require 'redis/objects'
|
48
|
-
Redis
|
50
|
+
Redis.current = Redis.new(:host => '127.0.0.1', :port => 6379)
|
49
51
|
|
50
52
|
Remember you can use Redis::Objects in any Ruby code. There are *no* dependencies
|
51
53
|
on Rails. Standalone, Sinatra, Resque - no problem.
|
@@ -124,15 +126,15 @@ which may already exist on the redis-server.
|
|
124
126
|
=== Initialization
|
125
127
|
|
126
128
|
Redis::Objects needs a handle to the +redis+ server. For standalone use, you
|
127
|
-
can either set
|
129
|
+
can either set Redis.current:
|
128
130
|
|
129
|
-
|
130
|
-
@list
|
131
|
+
Redis.current = Redis.new(:host => 'localhost', :port => 6379)
|
132
|
+
@list = Redis::List.new('mylist')
|
131
133
|
|
132
134
|
Or you can pass the Redis handle into the new method for each type:
|
133
135
|
|
134
|
-
redis
|
135
|
-
@list
|
136
|
+
@redis = Redis.new(:host => 'localhost', :port => 6379)
|
137
|
+
@list = Redis::List.new('mylist', @redis)
|
136
138
|
|
137
139
|
=== Counters
|
138
140
|
|
@@ -351,8 +353,8 @@ Atomic counters are a good way to handle concurrency:
|
|
351
353
|
@team.drafted_players.decrement
|
352
354
|
end
|
353
355
|
|
354
|
-
Atomic block - a cleaner way to do the above. Exceptions or
|
355
|
-
rewind counter back to previous state
|
356
|
+
Atomic block - a cleaner way to do the above. <b><em>Exceptions or returning nil
|
357
|
+
rewind counter back to previous state</em></b>:
|
356
358
|
|
357
359
|
@team.drafted_players.increment do |val|
|
358
360
|
raise Team::TeamFullError if val > @team.max_players
|
@@ -411,6 +413,6 @@ lock time.
|
|
411
413
|
|
412
414
|
== Author
|
413
415
|
|
414
|
-
Copyright (c) 2009-
|
416
|
+
Copyright (c) 2009-2012 {Nate Wiger}[http://nateware.com]. All Rights Reserved.
|
415
417
|
Released under the {Artistic License}[http://www.opensource.org/licenses/artistic-license-2.0.php].
|
416
418
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.3
|
data/lib/redis/hash_key.rb
CHANGED
@@ -70,7 +70,7 @@ class Redis
|
|
70
70
|
|
71
71
|
# Retrieve the entire hash. Redis: HGETALL
|
72
72
|
def all
|
73
|
-
h = redis.hgetall(key)
|
73
|
+
h = redis.hgetall(key) || {}
|
74
74
|
h.each { |k,v| h[k] = from_redis(v, options[:marshal_keys][k]) }
|
75
75
|
h
|
76
76
|
end
|
@@ -115,6 +115,15 @@ class Redis
|
|
115
115
|
arr + [kv[0], to_redis(kv[1], options[:marshal_keys][kv[0]])]
|
116
116
|
})
|
117
117
|
end
|
118
|
+
alias_method :update, :bulk_set
|
119
|
+
|
120
|
+
# Set keys in bulk if they do not exist. Takes a hash of field/values {'field1' => 'val1'}. Redis: HSETNX
|
121
|
+
def fill(pairs={})
|
122
|
+
raise ArgumentErorr, "Arugment to fill must be a hash of key/value pairs" unless pairs.is_a?(::Hash)
|
123
|
+
pairs.each do |field, value|
|
124
|
+
redis.hsetnx(key, field, to_redis(value, options[:marshal_keys][field]))
|
125
|
+
end
|
126
|
+
end
|
118
127
|
|
119
128
|
# Get keys in bulk, takes an array of fields as arguments. Redis: HMGET
|
120
129
|
def bulk_get(*fields)
|
data/lib/redis/list.rb
CHANGED
@@ -32,6 +32,18 @@ class Redis
|
|
32
32
|
from_redis redis.rpop(key)
|
33
33
|
end
|
34
34
|
|
35
|
+
# Atomically pops a value from one list, pushes to another and returns the
|
36
|
+
# value. Destination can be a String or a Redis::List
|
37
|
+
#
|
38
|
+
# list.rpoplpush(destination)
|
39
|
+
#
|
40
|
+
# Returns the popped/pushed value.
|
41
|
+
#
|
42
|
+
# Redis: RPOPLPUSH
|
43
|
+
def rpoplpush(destination)
|
44
|
+
from_redis redis.rpoplpush(key, destination.is_a?(Redis::List) ? destination.key : destination.to_s)
|
45
|
+
end
|
46
|
+
|
35
47
|
# Add a member to the start of the list. Redis: LPUSH
|
36
48
|
def unshift(value)
|
37
49
|
redis.lpush(key, to_redis(value))
|
data/lib/redis/set.rb
CHANGED
@@ -26,6 +26,11 @@ class Redis
|
|
26
26
|
redis.sadd(key, to_redis(value))
|
27
27
|
end
|
28
28
|
|
29
|
+
# Remove and return a random member. Redis:SPOP
|
30
|
+
def pop
|
31
|
+
from_redis redis.spop(key)
|
32
|
+
end
|
33
|
+
|
29
34
|
# Return all members in the set. Redis: SMEMBERS
|
30
35
|
def members
|
31
36
|
v = from_redis redis.smembers(key)
|
@@ -133,6 +138,19 @@ class Redis
|
|
133
138
|
redis.sdiffstore(name, key, *keys_from_objects(sets))
|
134
139
|
end
|
135
140
|
|
141
|
+
# Moves value from one set to another. Destination can be a String
|
142
|
+
# or Redis::Set.
|
143
|
+
#
|
144
|
+
# set.move(value, "name_of_key_in_redis")
|
145
|
+
# set.move(value, set2)
|
146
|
+
#
|
147
|
+
# Returns true if moved successfully.
|
148
|
+
#
|
149
|
+
# Redis: SMOVE
|
150
|
+
def move(value, destination)
|
151
|
+
redis.smove(key, destination.is_a?(Redis::Set) ? destination.key : destination.to_s, value)
|
152
|
+
end
|
153
|
+
|
136
154
|
# The number of members in the set. Aliased as size. Redis: SCARD
|
137
155
|
def length
|
138
156
|
redis.scard(key)
|
data/lib/redis/sorted_set.rb
CHANGED
@@ -58,11 +58,19 @@ class Redis
|
|
58
58
|
# When the given member does not exist in the sorted set, nil is returned.
|
59
59
|
# The returned rank (or index) of the member is 0-based for both commands
|
60
60
|
def rank(member)
|
61
|
-
redis.zrank(key, to_redis(member))
|
61
|
+
if n = redis.zrank(key, to_redis(member))
|
62
|
+
n.to_i
|
63
|
+
else
|
64
|
+
nil
|
65
|
+
end
|
62
66
|
end
|
63
67
|
|
64
68
|
def revrank(member)
|
65
|
-
redis.zrevrank(key, to_redis(member))
|
69
|
+
if n = redis.zrevrank(key, to_redis(member))
|
70
|
+
n.to_i
|
71
|
+
else
|
72
|
+
nil
|
73
|
+
end
|
66
74
|
end
|
67
75
|
|
68
76
|
# Return all members of the sorted set with their scores. Extremely CPU-intensive.
|
data/lib/redis/value.rb
CHANGED
@@ -13,11 +13,15 @@ class Redis
|
|
13
13
|
attr_reader :key, :options, :redis
|
14
14
|
def initialize(key, *args)
|
15
15
|
super(key, *args)
|
16
|
-
@redis.setnx(key, @options[:default]) if @options[:default]
|
16
|
+
@redis.setnx(key, to_redis(@options[:default])) if @options[:default]
|
17
17
|
end
|
18
18
|
|
19
19
|
def value=(val)
|
20
|
-
|
20
|
+
if val.nil?
|
21
|
+
delete
|
22
|
+
else
|
23
|
+
redis.set key, to_redis(val)
|
24
|
+
end
|
21
25
|
end
|
22
26
|
alias_method :set, :value=
|
23
27
|
|
@@ -26,8 +30,17 @@ class Redis
|
|
26
30
|
end
|
27
31
|
alias_method :get, :value
|
28
32
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
33
|
+
def inspect
|
34
|
+
"#<Redis::Value #{value.inspect}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(other); value == other end
|
38
|
+
def nil?; value.nil? end
|
39
|
+
def as_json(*args); value.as_json *args end
|
40
|
+
def to_json(*args); value.to_json *args end
|
41
|
+
|
42
|
+
def method_missing(*args)
|
43
|
+
self.value.send *args
|
44
|
+
end
|
32
45
|
end
|
33
46
|
end
|
data/redis-objects.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "redis-objects"
|
8
|
-
s.version = "0.5.
|
8
|
+
s.version = "0.5.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nate Wiger"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2012-06-13"
|
13
13
|
s.description = "Map Redis types directly to Ruby objects. Works with any class or ORM."
|
14
14
|
s.email = "nate@wiger.org"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -14,6 +14,11 @@ describe Redis::Value do
|
|
14
14
|
@value = Redis::Value.new('spec/value')
|
15
15
|
@value.delete
|
16
16
|
end
|
17
|
+
|
18
|
+
it "should marshal default value" do
|
19
|
+
@value = Redis::Value.new('spec/value', :default => {:json => 'data'}, :marshal => true)
|
20
|
+
@value.value.should == {:json => 'data'}
|
21
|
+
end
|
17
22
|
|
18
23
|
it "should handle simple values" do
|
19
24
|
@value.should == nil
|
@@ -58,6 +63,33 @@ describe Redis::Value do
|
|
58
63
|
@value.value.should == 'Peter Pan'
|
59
64
|
end
|
60
65
|
|
66
|
+
it "should provide a readable inspect" do
|
67
|
+
@value.value = 'monkey'
|
68
|
+
@value.inspect.should == '#<Redis::Value "monkey">'
|
69
|
+
@value.value = 1234
|
70
|
+
@value.inspect.should == '#<Redis::Value "1234">'
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should delegate unrecognized methods to the value' do
|
74
|
+
@value.value = 'monkey'
|
75
|
+
@value.to_sym.should == :monkey
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should properly pass equality operations on to the value' do
|
79
|
+
@value.value = 'monkey'
|
80
|
+
@value.should == 'monkey'
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should properly pass nil? on to the value' do
|
84
|
+
@value.delete
|
85
|
+
@value.nil?.should == true
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should equate setting the value to nil to deletion' do
|
89
|
+
@value.value = nil
|
90
|
+
@value.nil?.should == true
|
91
|
+
end
|
92
|
+
|
61
93
|
after do
|
62
94
|
@value.delete
|
63
95
|
end
|
@@ -187,6 +219,17 @@ describe Redis::List do
|
|
187
219
|
@list.get.should == ['a','c','f','j','a']
|
188
220
|
end
|
189
221
|
|
222
|
+
it "should handle rpoplpush" do
|
223
|
+
list2 = Redis::List.new("spec/list2")
|
224
|
+
list2.clear
|
225
|
+
|
226
|
+
@list << "a" << "b"
|
227
|
+
result = @list.rpoplpush(list2)
|
228
|
+
result.should == "b"
|
229
|
+
@list.should == ["a"]
|
230
|
+
list2.should == ["b"]
|
231
|
+
end
|
232
|
+
|
190
233
|
it "should handle lists of complex data types" do
|
191
234
|
@list.options[:marshal] = true
|
192
235
|
v1 = {:json => 'data'}
|
@@ -520,6 +563,15 @@ describe Redis::HashKey do
|
|
520
563
|
@hash.incr('counter')
|
521
564
|
@hash['counter'].to_i.should == 2
|
522
565
|
end
|
566
|
+
|
567
|
+
it "should respond to fill" do
|
568
|
+
@hash['foo'] = 'bar'
|
569
|
+
|
570
|
+
@hash.fill('abc' => '123', 'bang' => 'michael')
|
571
|
+
@hash['foo'].should == 'bar'
|
572
|
+
@hash['abc'].should == '123'
|
573
|
+
@hash['bang'].should == 'michael'
|
574
|
+
end
|
523
575
|
|
524
576
|
|
525
577
|
after do
|
@@ -559,7 +611,12 @@ describe Redis::Set do
|
|
559
611
|
@set.get.sort.should == ['a','b']
|
560
612
|
@set.length.should == 2
|
561
613
|
@set.size.should == 2
|
614
|
+
@set.delete('a')
|
615
|
+
@set.pop.should == 'b'
|
562
616
|
|
617
|
+
@set.add('a')
|
618
|
+
@set.add('b')
|
619
|
+
|
563
620
|
i = 0
|
564
621
|
@set.each do |st|
|
565
622
|
i += 1
|
@@ -742,6 +799,14 @@ describe Redis::SortedSet do
|
|
742
799
|
# @set.rank('b').should == 2
|
743
800
|
# @set.revrank('b').should == 3
|
744
801
|
|
802
|
+
# shouldn't report a rank for a key that doesn't exist
|
803
|
+
@set.rank('foo').should.not == @set.rank(@set.first)
|
804
|
+
@set.rank('foo').should == nil
|
805
|
+
|
806
|
+
# shouldn't report a rank for a key that doesn't exist
|
807
|
+
@set.revrank('foo').should.not == @set.revrank(@set.first)
|
808
|
+
@set.revrank('foo').should == nil
|
809
|
+
|
745
810
|
@set['f'] = 100
|
746
811
|
@set['g'] = 110
|
747
812
|
@set['h'] = 120
|
@@ -142,6 +142,26 @@ describe Redis::Objects do
|
|
142
142
|
@roster.contact_information.size.should == 2
|
143
143
|
end
|
144
144
|
|
145
|
+
it 'should be able to expire keys and then persist them' do
|
146
|
+
# on a hash_key
|
147
|
+
@roster.contact_information['Jenny_Phone'] = '8675309'
|
148
|
+
@roster.contact_information.expire 30
|
149
|
+
@roster.contact_information.ttl.should > -1
|
150
|
+
@roster.contact_information.ttl.should <= 30
|
151
|
+
@roster.contact_information.persist
|
152
|
+
@roster.contact_information.ttl.should == -1
|
153
|
+
@roster.contact_information['Jenny_Phone'].should == '8675309'
|
154
|
+
|
155
|
+
# on a value
|
156
|
+
@roster.my_rank = 42
|
157
|
+
@roster.my_rank.expire 30
|
158
|
+
@roster.my_rank.ttl.should > -1
|
159
|
+
@roster.my_rank.ttl.should <= 30
|
160
|
+
@roster.my_rank.persist
|
161
|
+
@roster.my_rank.ttl.should == -1
|
162
|
+
@roster.my_rank.to_i.should == 42
|
163
|
+
end
|
164
|
+
|
145
165
|
it "should be marshalling hash keys" do
|
146
166
|
@roster.contact_information['updated_at'] = Time.now
|
147
167
|
@roster.contact_information['updated_at'].class.should == Time
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,9 @@ require 'redis'
|
|
5
5
|
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
6
6
|
require 'bacon'
|
7
7
|
Bacon.summary_at_exit
|
8
|
+
if $0 =~ /\brspec$/
|
9
|
+
raise "\n===\nThese tests are in bacon, not rspec. Try: bacon #{ARGV * ' '}\n===\n"
|
10
|
+
end
|
8
11
|
|
9
12
|
UNIONSTORE_KEY = 'test:unionstore'
|
10
13
|
INTERSTORE_KEY = 'test:interstore'
|
metadata
CHANGED
@@ -1,59 +1,56 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-objects
|
3
|
-
version: !ruby/object:Gem::Version
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.3
|
4
5
|
prerelease:
|
5
|
-
version: 0.5.2
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Nate Wiger
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-06-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
16
15
|
name: bacon
|
17
|
-
|
18
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &2165783560 !ruby/object:Gem::Requirement
|
19
17
|
none: false
|
20
|
-
requirements:
|
21
|
-
- -
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version:
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
24
22
|
type: :development
|
25
|
-
version_requirements: *id001
|
26
|
-
- !ruby/object:Gem::Dependency
|
27
|
-
name: redis-namespace
|
28
23
|
prerelease: false
|
29
|
-
|
24
|
+
version_requirements: *2165783560
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: redis-namespace
|
27
|
+
requirement: &2165782960 !ruby/object:Gem::Requirement
|
30
28
|
none: false
|
31
|
-
requirements:
|
32
|
-
- -
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version:
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
35
33
|
type: :development
|
36
|
-
version_requirements: *id002
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: redis
|
39
34
|
prerelease: false
|
40
|
-
|
35
|
+
version_requirements: *2165782960
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: redis
|
38
|
+
requirement: &2165782480 !ruby/object:Gem::Requirement
|
41
39
|
none: false
|
42
|
-
requirements:
|
43
|
-
- -
|
44
|
-
- !ruby/object:Gem::Version
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
45
43
|
version: 2.1.1
|
46
44
|
type: :runtime
|
47
|
-
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2165782480
|
48
47
|
description: Map Redis types directly to Ruby objects. Works with any class or ORM.
|
49
48
|
email: nate@wiger.org
|
50
49
|
executables: []
|
51
|
-
|
52
50
|
extensions: []
|
53
|
-
|
54
|
-
extra_rdoc_files:
|
51
|
+
extra_rdoc_files:
|
55
52
|
- README.rdoc
|
56
|
-
files:
|
53
|
+
files:
|
57
54
|
- ATOMICITY.rdoc
|
58
55
|
- CHANGELOG.rdoc
|
59
56
|
- README.rdoc
|
@@ -85,25 +82,23 @@ files:
|
|
85
82
|
- spec/spec_helper.rb
|
86
83
|
homepage: http://github.com/nateware/redis-objects
|
87
84
|
licenses: []
|
88
|
-
|
89
85
|
post_install_message:
|
90
86
|
rdoc_options: []
|
91
|
-
|
92
|
-
require_paths:
|
87
|
+
require_paths:
|
93
88
|
- lib
|
94
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
90
|
none: false
|
96
|
-
requirements:
|
97
|
-
- -
|
98
|
-
- !ruby/object:Gem::Version
|
99
|
-
version:
|
100
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
96
|
none: false
|
102
|
-
requirements:
|
103
|
-
- -
|
104
|
-
- !ruby/object:Gem::Version
|
105
|
-
version:
|
106
|
-
requirements:
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements:
|
107
102
|
- redis, v2.1.1 or greater
|
108
103
|
rubyforge_project:
|
109
104
|
rubygems_version: 1.8.10
|
@@ -111,4 +106,3 @@ signing_key:
|
|
111
106
|
specification_version: 3
|
112
107
|
summary: Map Redis types directly to Ruby objects
|
113
108
|
test_files: []
|
114
|
-
|