redis-objects 0.5.2 → 0.5.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.
- 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
|
-
|